如何在UIBezierPath中使两行以不同的速度进行动画处理

时间:2015-01-09 00:07:28

标签: xcode swift autolayout caanimation cashapelayer

说出以下情况: 我画了一个四边形的形状,这是UIView的面具。我将形状图层表示为maskLayermaskLayer以不对称的方式裁剪UIView的底部。

然后我想在动画中完全展示我的UIView。动画应该在maskLayer的左侧下降到UIView的底部,然后.2秒后我的maskLayer的右侧也下降到UIView的底部,从而完全揭示了UIView的实体。

我的方法是首先按下左边的行,然后按下面的代码右下角:

  //this quadrilateral will put down left corner to the bottom of screen
  var path2 = UIBezierPath()
  path2.moveToPoint(CGPointZero)
  path2.addLineToPoint(CGPoint(x: 0, y: frame.height))
  path2.addLineToPoint(CGPoint(x: frame.width, y: frame.height / goldRatio / goldRatio))
  path2.addLineToPoint(CGPoint(x: frame.width, y: 0))
  path2.closePath()

  //this rectangle path will put down both corner to the bottom of screen
  //thus fix the view to its original shape
  var path3 = UIBezierPath()
  path3.moveToPoint(CGPointZero)
  path3.addLineToPoint(CGPoint(x: 0, y: frame.height))
  path3.addLineToPoint(CGPoint(x: frame.width, y: frame.height))
  path3.addLineToPoint(CGPoint(x: frame.width, y: 0))
  path3.closePath()

我花了2个小时试图弄明白无济于事。请你给我一些关于如何实现这一目标的说明。

初始状态如下: a

结束状态如下: b

我真的很感谢你的帮助!

2 个答案:

答案 0 :(得分:0)

最容易做到这一点的方法......作弊。

不要试图为它真正不需要它的形状的路径设置动画。

你应该做的就是这样。

创建最终的状态视图。屏幕底部的矩形,上面有UI等等......

此最终状态视图不会移动。它永远都在这里。

现在创建另一个视图并将其作为最终状态视图下方的子视图插入。在此,您可以添加一个角形切角的形状图层。

现在你需要做的就是向下动画这个角度视图的位置,直到它完全低于最终的状态视图。

如果颜色相同,则会产生动画形状路径的效果。

要获得不同的速度,您可以将矩形形状图层旋转到45度。然后在视图向下滑动时将其设置为0度?

事实上,您可以使用旋转和移动的单个形状图层来完成此操作。

答案 1 :(得分:0)

要执行此类动画,您通常会使用CADisplayLink(有点像计时器,但链接到显示的更新而不是某个任意间隔)来重复更改path关联具有所需的形状。我认为如果您使用CAShapeLayer并且只更改此形状的path属性,这是最简单的。

为了使这项工作,您需要一个函数来表示给定时间点的路径(或者更容易,一个路径在动画持续时间内某个percentageComplete的瞬间)。由于你有一个规则的形状(通常具有相同的点数),你可以简单地在startPointsendPoints的某个数组之间进行插值。

因此,创建形状图层,捕获开始时间,启动显示链接,然后对于显示链接的每个“勾号”,计算总animationDuration的百分比,并更新形状层的路径相应:

class ViewController: UIViewController {

    let animationDuration = 2.0
    var displayLink: CADisplayLink!
    var startTime: CFAbsoluteTime!
    var shapeLayer: CAShapeLayer!

    let goldRatio: CGFloat = 1.6180339887

    var startPoints:[CGPoint]!
    var endPoints:[CGPoint]!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.startAnimation()
    }

    func startAnimation() {
        startPoints = [
            CGPoint(x: 0, y: view.bounds.size.height),
            CGPoint(x: 0, y: view.bounds.size.height - view.bounds.size.height / goldRatio),
            CGPoint(x: view.bounds.size.width, y: 0),
            CGPoint(x: view.bounds.size.width, y: view.bounds.size.height)
        ]

        endPoints = [
            CGPoint(x: 0, y: view.bounds.size.height),
            CGPoint(x: 0, y: view.bounds.size.height * 0.75),
            CGPoint(x: view.bounds.size.width, y: view.bounds.size.height * 0.75),
            CGPoint(x: view.bounds.size.width, y: view.bounds.size.height)
        ]

        assert(startPoints.count == endPoints.count, "Point counts don't match")

        createShape()
        startDisplayLink()
    }

    func startDisplayLink() {
        displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
        startTime = CFAbsoluteTimeGetCurrent()
        displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
    }

    func stopDisplayLink() {
        displayLink.invalidate()
        displayLink = nil
    }

    func handleDisplayLink(displayLink: CADisplayLink) {
        var percent = CGFloat((CFAbsoluteTimeGetCurrent() - startTime) / animationDuration)

        if percent >= 1.0 {
            percent = 1.0
            stopDisplayLink()
        }

        updatePathBasedUponPercentComplete(percent)
    }

    func createShape() {
        shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = UIColor.blueColor().CGColor
        shapeLayer.strokeColor = UIColor.clearColor().CGColor
        updatePathBasedUponPercentComplete(0.0)

        view.layer.addSublayer(shapeLayer)
    }

    func updatePathBasedUponPercentComplete(percentComplete: CGFloat) {
        shapeLayer.path = pathBasedUponPercentComplete(percentComplete, frame: view.frame).CGPath
    }

    func pathBasedUponPercentComplete(percentComplete: CGFloat, frame: CGRect) -> UIBezierPath {
        var path = UIBezierPath()

        for i in 0 ..< startPoints.count {
            let point = CGPoint(
                x: startPoints[i].x + (endPoints[i].x - startPoints[i].x) * percentComplete,
                y: startPoints[i].y + (endPoints[i].y - startPoints[i].y) * percentComplete
            )

            if i == 0 {
                path.moveToPoint(point)
            } else {
                path.addLineToPoint(point)
            }
        }
        path.closePath()

        return path
    }

}