UIView图层蒙版动画

UIView Layer Mask Animate

我正在尝试为 UIView 上的遮罩层设置动画。

基本上这段代码显示下图:

let bounds: CGRect = self.manualWBMaskView!.bounds
let maskLayer: CAShapeLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.fillColor = UIColor.blackColor().CGColor
let screenWith  : CGFloat = UIScreen.mainScreen().bounds.width
let roundedRectFrame : CGRect = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith/4), self.manualWBMaskView!.bounds.midY - (screenWith/4), screenWith/2, screenWith/2)
let path: UIBezierPath = UIBezierPath(roundedRect:roundedRectFrame, cornerRadius:10  )
path.appendPath(UIBezierPath(rect: bounds))
maskLayer.path = path.CGPath
maskLayer.fillRule = kCAFillRuleEvenOdd
self.manualWBMaskView!.layer.mask = maskLayer

我想让它动画到切口的那个位置,从全屏到上面的位置:

我尝试了 UIView 动画,但没有成功。既然我已经有一个 CAShapeLayer,我应该能够为它制作动画吗?

编辑**

这是我试过的动画代码,但没有用:

 //Before Animation Make the Mask fill the whole view:
    self.manualWBMaskView!.layer.mask = self.manualWBMaskView!.bounds

    var duration: NSTimeInterval = 0.8

    CATransaction.begin()
    CATransaction[kCATransactionAnimationDuration] = Int(duration)
    self.manualWBMaskView!.layer.mask = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith/4), self.manualWBMaskView!.bounds.midY - (screenWith/4), screenWith/2, screenWith/2)
    CATransaction.commit()

根据下面“originaluser2”的回复,我有这个:

func presentMaskScreenWithAnimation () {
                let bounds: CGRect = self.manualWBMaskView!.bounds
                let maskLayer: CAShapeLayer = CAShapeLayer()
                maskLayer.frame = bounds
                maskLayer.fillColor = UIColor.blackColor().CGColor
                let screenWith  : CGFloat = UIScreen.mainScreen().bounds.width
                let roundedRectFrame : CGRect = self.manualWBMaskView.bounds
                let path: UIBezierPath = UIBezierPath(roundedRect:roundedRectFrame, cornerRadius:0  )
                path.appendPath(UIBezierPath(rect: bounds))
                maskLayer.path = path.CGPath
                maskLayer.fillRule = kCAFillRuleEvenOdd
                self.manualWBMaskView!.layer.mask = maskLayer

       // define your new path to animate the mask layer to
        let screenWith2  : CGFloat = UIScreen.mainScreen().bounds.width
        let roundedRectFrame2 : CGRect = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith2/4), self.manualWBMaskView!.bounds.midY - (screenWith2/4), screenWith2/2, screenWith2/2)

        let path2 = UIBezierPath(roundedRect:roundedRectFrame2, cornerRadius:10  )

        // create new animation
        let anim = CABasicAnimation(keyPath: "path")    

        // from value is the current mask path
        anim.fromValue = maskLayer.path

        // to value is the new path
        anim.toValue = path2.CGPath

        // duration of your animation
        anim.duration = 5.0

        // custom timing function to make it look smooth
        anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

        // add animation
        maskLayer.addAnimation(anim, forKey: nil)

        // update the path property on the mask layer, using a CATransaction to prevent an implicit animation
        CATransaction.begin()
        CATransaction.setDisableActions(true)
           maskLayer.path = path2.CGPath
           maskLayer.fillRule = kCAFillRuleEvenOdd
        CATransaction.commit()

}

它做动画;但是,我现在有两个问题: 1. Shape在动画过程中全部关闭,但如果在动画结束时聚集在一起。 (以下截图)

  1. 蒙版反转了。它在形状内部是可见的,但在它周围是透明的。我需要相反的东西。形状内部是透明的。

您需要在遮罩层 path 属性 上使用 CABasicAnimation

这将允许您将蒙版从给定路径动画化到另一个给定路径。 Core Animation会自动为你插值中间步骤

像这样应该可以解决问题:

// define your new path to animate the mask layer to
let path = UIBezierPath(roundedRect: CGRectInset(view.bounds, 100, 100), cornerRadius: 20.0)
path.appendPath(UIBezierPath(rect: view.bounds))

// create new animation
let anim = CABasicAnimation(keyPath: "path")

// from value is the current mask path
anim.fromValue = maskLayer.path

// to value is the new path
anim.toValue = path.CGPath

// duration of your animation
anim.duration = 5.0

// custom timing function to make it look smooth
anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

// add animation
maskLayer.addAnimation(anim, forKey: nil)

// update the path property on the mask layer, using a CATransaction to prevent an implicit animation
CATransaction.begin()
CATransaction.setDisableActions(true)
maskLayer.path = path.CGPath
CATransaction.commit()

结果:

需要注意的一件事是 Core Animation 中似乎存在一个错误,它无法正确地从包含矩形的路径到包含带角半径的圆角矩形的路径进行动画处理。为此,我已向 Apple 提交了错误报告。

对此有一个简单的解决方法,即在原始路径上使用角半径值可忽略不计的圆角矩形(0.0001 对我来说可以)。有点 hacky – 但您在使用该应用程序时不会注意到其中的区别。


来自 Apple 的错误报告响应

不幸的是,Apple 认为这是“预期行为”。他们说了以下内容:

For the animation to render correctly, the animated paths should have the same number of elements/segments (ideally of the same type). Otherwise, there is no reasonable way to interpolate between the two.

For instance, if a first path has four elements and the second path has six elements, we can easily create the first four points by averaging the first four start and end points with a weight for current time, but it's impossible to come up with a general purpose way to calculate the intermediate positions for the fifth and sixth points. This is why the animation works just fine for corner radius 0.001 (the same number of elements), but fails for plain rectangular path (different number of elements).

我个人不同意,如果您指定一个圆角半径为 0 的圆角矩形,然后为圆角半径> 0 的圆角矩形设置动画 – 仍然会获得不正确的动画,即使两条路径 应该 具有“相同数量的元素”。我怀疑这不起作用的原因是内部实现检查半径为 0,并尝试通过使用具有 4 个元素的矩形路径来优化,这就是它不起作用的原因。

我会在有空时提交另一份错误报告来解释这一点。


其他几件事...

你用力打开你的 manualWBMaskView 很多。不。您需要学习如何 properly deal with optionals。每次你强行打开一些东西 - 你的应用程序可能会崩溃。

你也有这样几行:

let maskLayer: CAShapeLayer = CAShapeLayer()

这里的 : CAShapeLayer 不仅没有必要,而且有悖于良好的习惯。您应该始终让 Swift 尽可能地推断值的类型。