UIView图层蒙版动画

时间:2016-04-06 18:32:03

标签: swift uiview mask layer cashapelayer

我正在尝试在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

enter image description here

我想让它从全屏到以上位置动画到切口的位置:

enter image description here

我试过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.动画期间形状全部关闭,但如果在动画结束时聚集在一起。 (以下截图)

  1. 面具倒置。它在形状内部可见,但在其周围透明。我需要相反的。形状内部是透明的。
  2. enter image description here

1 个答案:

答案 0 :(得分:19)

您希望在掩码图层的CABasicAnimation属性中使用path

这将允许您将遮罩从给定路径动画到另一个给定路径。核心动画将自动为您插入中间步骤。

这样的事情可以解决问题:

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

<强>结果:

enter image description here

有一点需要注意的是,核心动画中似乎存在一个错误,它无法从包含矩形的路径正确动画到包含带圆角半径的圆角矩形的路径。我已经向Apple提交了一份错误报告。

有一个简单的解决方法,就是在原始路径上使用圆角矩形,角半径可忽略不计(0.0001对我来说没问题)。有点hacky - 但是在使用应用程序时你无法注意到它的区别。

Apple的错误报告回复

不幸的是,Apple认为这是&#34;预期的行为&#34;。他们说了以下几点:

  

要使动画正确渲染,动画路径应具有相同数量的元素/片段(理想情况下为相同类型)。否则,没有合理的方法在两者之间进行插值。

     

例如,如果第一条路径有四个元素而第二条路径有六个元素,我们可以通过平均前四个起点和终点以及当前时间的权重来轻松创建前四个点,但它是“不可能想出一个通用的方法来计算第五和第六点的中间位置。这就是为什么动画在角半径0.001(相同数量的元素)下工作得很好,但对于普通矩形路径(不同数量的元素)则失败。

我个人不同意,如果指定圆角半径为0的圆角,然后动画为圆角矩形,角半径> 0 - 仍然获得了不正确的动画,即使两个路径应该具有相同数量的元素&#34;。我怀疑这不起作用的原因是内部实现检查半径为0,并尝试使用带有4个元素的矩形路径进行优化,这就是为什么它不起作用。

当我抽出时间时,我会提交另一份错误报告来解释这一点。

其他一些事情......

你强行解开你的manualWBMaskView。唐&#39;吨。您需要了解如何properly deal with optionals。每次你强行解开东西 - 你的应用程序都可能崩溃。

您还有以下几行:

let maskLayer: CAShapeLayer = CAShapeLayer()

这里的: CAShapeLayer不仅是不必要的 - 而且违背了良好的做法。你应该总是让Swift推断它可以的值的类型。