Thursday, March 10, 2016

Consistenlty Centering CALayer Sublayer over a UIImageView

Leave a Comment
  • Xcode 7.2
  • Swift 2

My issue is that I cannot consistently center my CALayer.

I have a related SO question here. The answer to this question was forward progress and brings me to the issue I am having now. Note in the answer to my related question, @WarrenBurton seems to only test portrait mode.

I think this issue, which really is calculating/positioning a sublayer dead center over a UIImageView may relate to the actual size of the Image selected for the UIImageView but I could be wrong about this.

enter image description here

Here is my print() output for portrait mode rotation:

willTransitionToTraitCollection()! blurFilerMask != nil resetMaskOverlay()! (160.0, 128.0) 328.0 200.0 BlurFilterMask -> drawInContext()! 

But the CALayer is centered in Landscape mode:

enter image description here

Here is my print() output for landscape mode rotation:

willTransitionToTraitCollection()! blurFilerMask != nil resetMaskOverlay()! (284.0, 108.0) 568.0 200.0 BlurFilterMask -> drawInContext()! 

My UIImage Size Inspector Screenshot, Constraints most important part to consider perhaps:

enter image description here

My UIImage mode is Scale To Fill

Here is all the code in my TestViewController:

import UIKit  class TestViewController: UIViewController {      @IBOutlet weak var heroImageView: UIImageView!       var blurFilterMask:BlurFilterMask! = nil      override func viewDidLoad() {         super.viewDidLoad()          // Do any additional setup after loading the view.     }      override func viewDidLayoutSubviews() {         super.viewWillLayoutSubviews()         resetMaskOverlay()     }       override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {         super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)         print("willTransitionToTraitCollection()!")          if blurFilterMask != nil {             print("blurFilerMask != nil")             blurFilterMask.removeFromSuperlayer()             blurFilterMask = nil         }      }      func resetMaskOverlay(){         print("resetMaskOverlay()!")          print(heroImageView.center)         print(CGRectGetWidth(heroImageView.bounds))         print(CGRectGetHeight(heroImageView.bounds))          if blurFilterMask == nil {             blurFilterMask = BlurFilterMask()         }          blurFilterMask.diameter = 80         blurFilterMask.frame = heroImageView.bounds         blurFilterMask.origin = heroImageView.center         blurFilterMask.shouldRasterize = true         heroImageView.layer.addSublayer(blurFilterMask)         blurFilterMask.setNeedsDisplay()      }  } 

Here is my CALayer which I call BlurFilterMask:

import UIKit  class BlurFilterMask : CALayer {      private let GRADIENT_WIDTH : CGFloat = 50.0      var origin : CGPoint = CGPointZero {         didSet {             //setNeedsDisplay()         }     }     var diameter : CGFloat = 50.0 {         didSet {             //setNeedsDisplay()         }     }      override init() {         super.init()     }      required init?(coder aDecoder: NSCoder) {         fatalError("init(coder:) has not been implemented")     }      override func drawInContext(ctx: CGContext) {         print("BlurFilterMask -> drawInContext()!")         CGContextSaveGState(ctx)          let clearRegionRadius : CGFloat  = self.diameter * 0.5         let blurRegionRadius : CGFloat  = clearRegionRadius + GRADIENT_WIDTH          let baseColorSpace = CGColorSpaceCreateDeviceRGB();         let colours : [CGFloat] = [0.0, 0.0, 0.0, 0.0,     // Clear region             0.0, 0.0, 0.0, 0.6] // blur region color         let colourLocations : [CGFloat] = [0.0, 0.0]         let gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2)           CGContextDrawRadialGradient(ctx, gradient, self.origin, clearRegionRadius, self.origin, blurRegionRadius, .DrawsAfterEndLocation);       }  } 

Now further testing of this issue, I can demonstrate the exact opposite of my first screenshots (the first scenario was: Portrait mode is NOT centered and Landscape mode is correctly centered).

In this next example. I have changed the UIImageView constraints so that fill the UIImageView fills the SuperView and you can see below my CALayer is correctly centered in Portrait mode but NOT Landscape mode, the exact opposite of my previous screenshot demo:

UIImageView Constraints: enter image description here

Portrait mode with UIImageView "fill" superview constraints:

enter image description here

Landscape mode with UIImageView "fill" superview constraints:

enter image description here

1 Answers

Answers 1

Problem with your code is setting centre of your BlurFilterMask. Centre should centre of BlurFilterMask right? Use this updated 'resetMaskOverlay' function.

    func resetMaskOverlay(){     print("resetMaskOverlay()!")      print(heroImageView.center)     print(CGRectGetWidth(heroImageView.bounds))     print(CGRectGetHeight(heroImageView.bounds))      if blurFilterMask == nil {         blurFilterMask = BlurFilterMask()     }      blurFilterMask.diameter = 80     blurFilterMask.frame = heroImageView.bounds     blurFilterMask.origin = CGPointMake(CGRectGetMidX(heroImageView.bounds), CGRectGetMidY(heroImageView.bounds))     blurFilterMask.shouldRasterize = true     heroImageView.layer.addSublayer(blurFilterMask)     blurFilterMask.setNeedsDisplay()  } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment