添加以UIImageView为中心的CALayer子层 [英] Add a CALayer sublayer centered over UIImageView

查看:46
本文介绍了添加以UIImageView为中心的CALayer子层的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  • Xcode 7.2
  • Swift 2

我正在尝试用所谓的"BlurFilterMask"覆盖图像.这是我用Swift代码动态添加的CALayer,这是BlurFilterMask:

  BlurFilterMask类:CALayer {私人让GRADIENT_WIDTH:CGFloat = 50.0var origin:CGPoint = CGPointZero {didSet {setNeedsDisplay()}}var直径:CGFloat = 50.0 {didSet {setNeedsDisplay()}}覆盖init(){super.init()}需要初始化吗?(编码器aDecoder:NSCoder){fatalError("init(coder :)尚未实现")}覆盖func drawInContext(ctx:CGContext){CGContextSaveGState(ctx)让clearRegionRadius:CGFloat = self.diameter * 0.5让blurRegionRadius:CGFloat = clearRegionRadius + GRADIENT_WIDTH让baseColorSpace = CGColorSpaceCreateDeviceRGB();let colors:[CGFloat] = [0.0,0.0,0.0,0.0,//清除区域0.0,0.0,0.0,0.6]//模糊区域颜色let colourLocations:[CGFloat] = [0.0,0.0]让渐变= CGGradientCreateWithColorComponents(baseColorSpace,颜色,colourLocations,2)CGContextDrawRadialGradient(ctx,梯度,self.origin,clearRegionRadius,self.origin,blurRegionRadius,.DrawsAfterEndLocation);}} 

在我的 UIViewController.viewDidLoad()中,我调用addMaskOverlay(),这是实现:

  func addMaskOverlay(){让blurFilterMask = BlurFilterMask()blurFilterMask.diameter = 80blurFilterMask.frame = heroImageView.boundsblurFilterMask.origin = heroImageView.centerblurFilterMask.shouldRasterize = trueheroImageView.layer.addSublayer(blurFilterMask)blurFilterMask.setNeedsDisplay()} 

例如,无论我使用什么模拟器,无论是iPhone 5,iPhone 6还是iPad 2,我的中心,宽度和高度都始终相同:

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

但是我的BlurFilterMask的中心总是有所不同,具体取决于设备模拟器,并且它永远不会从UIImageView的中心开始,例如iPhone 5:

这是iPhone 6:

我想我可能在CALayer BlurFilterMask上需要垂直居中和水平居中约束,所以我尝试添加约束:

  heroImageView.layer.addSublayer(blurFilterMask)让xConstraint = NSLayoutConstraint(项目:blurFilterMask,属性:.CenterX,relatedBy:.等于,toItem:heroImageView,属性:.CenterX,乘数:1,常数:0)让yConstraint = NSLayoutConstraint(项目:blurFilterMask,属性:.CenterY,relatedBy:.等于,toItem:heroImageView,属性:.CenterY,乘数:1,常数:0)heroImageView.addConstraint(xConstraint)heroImageView.addConstraint(yConstraint) 

但是我无法将约束添加到我不认为的CALayer中,尝试时出现错误:

由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'* + [NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:约束项必须均为实例UIView或NSLayoutGuide.'**

似乎问题可能是约束,但是如果我不能将约束添加到CALayer ...

该代码无济于事...

不确定从何处去,任何建议将不胜感激.

解决方案

问题在于,调用 viewDidLoad 时,布局尚未完成,因此您的测量是从的初始状态开始进行的视图控制器,最有可能是从情节提要的大小来决定的.

一旦布局完成并且 heroImageView 的最终大小已知,您就需要调用 blurFilterMask .

您可以通过覆盖 viewDidLayoutSubviews 来实现此目的,即...

被调用以通知视图控制器其视图刚刚布置了其子视图.

阅读

  • Xcode 7.2
  • Swift 2

I am trying to overlay an Image with what I am calling a "BlurFilterMask". It is a CALayer that I dynamically add with Swift Code, here is the 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 what Simulator I run in, be it iPhone 5 or iPhone 6 or iPad 2, for examples, 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 UIImageView, example in iPhone 5:

And here is iPhone 6:

I thought maybe I need vertically centering and horizontally centering constraints on my CALayer BlurFilterMask so I tried adding 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 cannot add Constraints to a CALayer I do not think, I get an error when I try:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]: Constraint items must each be an instance of UIView, or NSLayoutGuide.'**

It seems like the issue might be constraints but if I cannot add constraints to a CALayer...

That code didn't help...

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

解决方案

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

You need to call blurFilterMask once layout has completed and the final size of heroImageView is known.

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

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

Read the docs as there are some conditions but in your case this does work leaving you with this..

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)*/

Was meant to be commented out as it didn't make much sense leaving...

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

    }

 }

这篇关于添加以UIImageView为中心的CALayer子层的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆