Add CALayer sublevel centered over UIImageView

  • Xcode 7.2
  • Swift 2

I am trying to overlay an image on what I call "BlurFilterMask". This is a CALayer that I dynamically add using Swift Code, here is BlurFilterMask:

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) { 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); } } 

In my UIViewController.viewDidLoad() I call addMaskOverlay (), and here is the implementation:

  func addMaskOverlay(){ let blurFilterMask = BlurFilterMask() blurFilterMask.diameter = 80 blurFilterMask.frame = heroImageView.bounds blurFilterMask.origin = heroImageView.center blurFilterMask.shouldRasterize = true heroImageView.layer.addSublayer(blurFilterMask) blurFilterMask.setNeedsDisplay() } 

No matter which simulator I run, be it iPhone 5 or iPhone 6 or iPad 2, for example, I always get the same center, width and height:

  • print (heroImageView.center) = (300.0, 67.5)
  • print (CGRectGetWidth (heroImageView.bounds)) = 600.0
  • print (CGRectGetHeight (heroImageView.bounds)) // 135.0

But the center of my BlurFilterMask is always somewhere different, depending on the device simulator, and it never starts in the center of the UIImageView, for example, in iPhone 5:

enter image description here

And here is the iPhone 6:

enter image description here

I thought maybe I needed vertically centering and horizontally centering constraints for my CALayer BlurFilterMask, so I tried to add the constraints:

 heroImageView.layer.addSublayer(blurFilterMask) let xConstraint = NSLayoutConstraint(item: blurFilterMask, attribute: .CenterX, relatedBy: .Equal, toItem: heroImageView, attribute: .CenterX, multiplier: 1, constant: 0) let yConstraint = NSLayoutConstraint(item: blurFilterMask, attribute: .CenterY, relatedBy: .Equal, toItem: heroImageView, attribute: .CenterY, multiplier: 1, constant: 0) heroImageView.addConstraint(xConstraint) heroImageView.addConstraint(yConstraint) 

But I can not add Constraints to CALayer. I do not think I get an error message when I try:

Application termination due to an undetected 'NSInvalidArgumentException' exception, reason: '* + [NSLayoutConstraint constraintWithItem: attribute: relatedBy: toItem: attribute: multiplier: constant:]: Constraint elements must be an instance of UIView or NSLayoutGuide. '**

It seems that the problem may be a limitation, but if I can not add restrictions for CALayer ...

This code did not help ...

Not sure where to go from here, any suggestions would be greatly appreciated.

+6
source share
1 answer

The problem is that when calling viewDidLoad layout has not yet been completed, so your measurements are taken from the initial state of the view controller, most likely from the size of the storyboard, if that is what you are using.

You need to call blurFilterMask after the layout is complete and get the final heroImageView size.

You can do this by overriding viewDidLayoutSubviews , which ...

Called to notify the view controller that its view has just laid out its routines.

Read the documents as there are some conditions, but in your case it does the job, leaving you with it.

 class ViewController: UIViewController { @IBOutlet var heroImageView: UIImageView! var blurFilterMask:BlurFilterMask! = nil func resetMaskOverlay(){ if blurFilterMask == nil { blurFilterMask = BlurFilterMask() blurFilterMask.diameter = 120 heroImageView.layer.addSublayer(blurFilterMask) } blurFilterMask.frame = heroImageView.bounds blurFilterMask.origin = heroImageView.center } override func viewDidLayoutSubviews() { resetMaskOverlay() } } 

I assumed this line in blurFilterMask

  CGContextTranslateCTM(ctx, 0.0, yDiff)*/ 

It was assumed that he would be commented, as this does not make much sense ...

 class BlurFilterMask : CALayer { let GRADIENT_WIDTH : CGFloat = 50.0 var origin : CGPoint = CGPointZero { didSet { setNeedsDisplay() } } var diameter : CGFloat = 50.0 { didSet { setNeedsDisplay() } } override func drawInContext(ctx: CGContext) { 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); } } 

test1-5s test1-6s +

+3
source

All Articles