Swift 3

时间:2017-05-16 16:32:27

标签: swift swift3 uiviewanimation uibezierpath curve

我想绘制一些贝塞尔线,我想用波浪效果为它们设置动画,

实施例 enter image description here

您对我如何做到这一点有什么想法吗? Bezier系列是最好的方法吗? 我发现只有2个lib,但它们对我需要的东西并不是很有用,我尝试修改一个lib的代码,遗憾的是没有成功https://github.com/yourtion/YXWaveView

我发现这个lib,https://antiguab.github.io/bafluidview/可以完成这项工作,但它是用obj-c编写的,也许你在swift中知道这样的东西

1 个答案:

答案 0 :(得分:22)

您可以使用display link(一种针对屏幕刷新率优化的特殊计时器)来更改正在呈现的path。显示链接的处理程序应计算已经过的时间量并相应地修改要呈现的路径。您可以使用CAShapeLayer来呈现路径,也可以使用自定义UIView子类。形状层可能更容易:

class ViewController: UIViewController {

    private var displayLink: CADisplayLink?
    private var startTime: CFAbsoluteTime?

    /// The `CAShapeLayer` that will contain the animated path

    private let shapeLayer: CAShapeLayer = {
        let _layer = CAShapeLayer()
        _layer.strokeColor = UIColor.white.cgColor
        _layer.fillColor = UIColor.clear.cgColor
        _layer.lineWidth = 3
        return _layer
    }()

    // start the display link when the view appears

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        view.layer.addSublayer(shapeLayer)

        self.startDisplayLink()
    }

    // Stop it when it disappears. Make sure to do this because the
    // display link maintains strong reference to its `target` and
    // we don't want strong reference cycle.

    override func viewDidDisappear(_ animated: Bool) {
        stopDisplayLink()
    }

    /// Start the display link

    private func startDisplayLink() {
        startTime = CFAbsoluteTimeGetCurrent()
        displayLink?.invalidate()
        displayLink = CADisplayLink(target: self, selector:#selector(handleDisplayLink(_:)))
        displayLink?.add(to: RunLoop.current, forMode: .commonModes)
    }

    /// Stop the display link

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

    /// Handle the display link timer.
    ///
    /// - Parameter displayLink: The display link.

    func handleDisplayLink(_ displayLink: CADisplayLink) {
        let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
        shapeLayer.path = wave(at: elapsed).cgPath
    }

    /// Create the wave at a given elapsed time.
    ///
    /// You should customize this as you see fit.
    ///
    /// - Parameter elapsed: How many seconds have elapsed.
    /// - Returns: The `UIBezierPath` for a particular point of time.

    private func wave(at elapsed: Double) -> UIBezierPath {
        let centerY = view.bounds.height / 2
        let amplitude = CGFloat(50) - fabs(fmod(CGFloat(elapsed), 3) - 1.5) * 40

        func f(_ x: Int) -> CGFloat {
            return sin(((CGFloat(x) / view.bounds.width) + CGFloat(elapsed)) * 4 * .pi) * amplitude + centerY
        }

        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: f(0)))
        for x in stride(from: 0, to: Int(view.bounds.width + 9), by: 10) {
            path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
        }

        return path
    }
}

唯一棘手的部分是编写wave函数,该函数在特定时间内产生UIBezierPath,并在时间过去时反复调用它时产生所需的效果。在这一个中,我正在渲染一条正弦曲线,其中幅度和偏移量根据生成路径时所经过的时间而变化,但是你可以在你的演绎中做任何你想做的事情。希望这说明了基本的想法。

以上代码产生:

enter image description here