【问题标题】:UIView Layer Mask AnimateUIView 图层蒙版动画
【发布时间】:2016-04-06 18:32:03
【问题描述】:

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

  1. 面罩倒置。它在形状内部可见,但在其周围是透明的。我需要相反的。里面的形状是透明的。

【问题讨论】:

  • 你的动画代码在哪里?
  • 我很抱歉。用我试过的代码更新了 OP。
  • 你为什么一直强制解开manualWBMaskView?你只是要求崩溃。请了解如何properly deal with optionals
  • 我承认,我不是这方面的专家。但是,Xcode 一直强迫我输入 '!'

标签: swift uiview mask layer cashapelayer


【解决方案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 认为这是“预期行为”。他们说:

为了正确渲染动画,动画路径应该具有相同数量的元素/段(最好是相同类型)。否则,就没有合理的方式在两者之间进行插值。

例如,如果第一条路径有四个元素,而第二条路径有六个元素,我们可以通过对当前时间的前四个起点和终点进行加权平均来轻松创建前四个点,但这是不可能的用一种通用的方法来计算第五和第六点的中间位置。这就是为什么动画在圆角半径 0.001(相同数量的元素)下效果很好,但在普通矩形路径(不同数量的元素)下效果不佳。

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

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


其他几件事...

你强制解开你的manualWBMaskView 相当多。不。你需要学习如何properly deal with optionals。每次你强制解开某些东西 - 你的应用程序可能会崩溃。

你也有几行这样的:

let maskLayer: CAShapeLayer = CAShapeLayer()

这里的: CAShapeLayer 不仅没有必要——而且违背了良好的做法。你应该总是让 Swift 尽可能地推断出一个值的类型。

【讨论】:

  • 这似乎行得通。看来我快到了。该动画的几个小问题。我更新了原帖。
  • @gizmodo 我已经编辑了我的答案。关键点首先是您需要将视图边界的路径附加到您要制作动画的路径上,以获得反转效果。其次,您需要更改原始路径以使用 0.0001 的角半径 - 因为 Core Animation 中似乎存在一个错误,它无法正确地从矩形动画到圆角矩形。
  • 完美运行!我只需要添加以下内容以使其停止消失: anim.fillMode = kCAFillModeForwards 和 anim.removedOnCompletion = false
  • 另外,我已经向 Apple 提交了一份关于 rect to rounded rect 动画错误的错误报告。
  • @gizmodo 如果您有兴趣,我收到了 Apple 关于从 rect 到圆角 rect 错误的动画的回复。我已经用它更新了我的帖子。
猜你喜欢
  • 2018-01-03
  • 1970-01-01
  • 2012-09-15
  • 2013-08-25
  • 2014-11-09
  • 1970-01-01
  • 1970-01-01
  • 2018-05-19
  • 1970-01-01
相关资源
最近更新 更多