Swift 中的持续动画视图

Constantly animated view in Swift

我正在创建 AVPlayerController 的子类,它观察玩家并处理玩家的状态。如果 AVPlayerItemStatus.failed,我会在玩家的框架上添加自定义 UIView。目前,我的自定义视图代码的重要部分如下所示:

class NoiseView: UIView {

    ...

    var timer: Timer?
    func animate() {
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }

    func stopAnimating() {
        timer?.invalidate()
    }

    @objc func timerAction() {
        self.setNeedsDisplay()
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        let context = UIGraphicsGetCurrentContext()!
        let h = rect.height
        let w = rect.width

        var val: CGFloat!
        var color: UIColor!

        for i in 0...Int(w-1) {
            for j in 0...Int(h-1) {
                val = .random
                color = UIColor(red: val, green: val, blue: val, alpha: 1.0)

                context.setFillColor(color.cgColor)
                context.fill(CGRect(x: i, y: j, width: 1, height: 1))
            }
        }

        context.flush()
    } 
}

我正在从保存 NoiseView 对象的其他 ViewController 调用方法 animate()

问题是,它按预期工作(视图正在动画并产生白噪声)但应用程序开始运行缓慢。在不导致性能下降的情况下重绘视图的正确方法应该是什么?

顺便说一句,下降发生在 0.1s 间隔(10 FPS)。我想要完成的是拥有至少 30 FPS 的恒定白噪声,看起来像合法的电视噪声。

自定义 draw(rect:) 方法可能会导致性能下降。

我建议考虑在您的白噪声视图中添加 CAAnimationCAAnimationGroup

CABasicAnimation Documentation

CAAnimationGroup Whosebug Post

而不是使用计时器和调用 setNeedsDisplay,结果将继续调用 draw 方法,从而减慢应用程序。

使用 CAAnimation 并创建 CALayer 或 CAreplicator 层并为其设置动画。

如果你需要代码,你可以检查这个 link Color Picker 或者我可以 post 一段时间后的一些演示代码,它的 fifa 时间 :-p.

CAReplicatorlayer CALayer

您可以尝试多种策略来优化绘图代码,但对我来说最明显的一个是您应该在绘图代码之外预先计算您需要的 CGRects。

您运行的循环需要每个动画帧的w^h 次迭代来计算您需要的所有CGRect。这很多,而且完全没有必要,因为只要宽度和高度不变,CGRects 就会始终相同。相反,您应该这样做:

var cachedRects = [CGRect]()
override var frame: CGRect {
    didSet {
        calculateRects()
    }
}

func calculateRects() {
    cachedRects = []
    let w = frame.width
    let h = frame.height
    for i in 0...Int(w-1) {
        for j in 0...Int(h-1) {
            let rect = CGRect(x: i, y: j, width: 1, height: 1)
            cachedRects += [rect]
        }
    }
}

override func draw(_ rect: CGRect) {
    super.draw(rect)
    let context = UIGraphicsGetCurrentContext()!
    var val: CGFloat!
    var color: UIColor!
    for rect in cachedRects {
        val = .random
        color = UIColor(red: val, green: val, blue: val, alpha: 1.0)
        context.setFillColor(color.cgColor)
        context.fill(rect)
    }
    context.flush()
}

通过预缓存 rects,您只需执行 (w * h) 次迭代,这是一个巨大的改进。

如果这还不足以提高性能,您可以使用类似的预缓存策略进一步优化,例如不是随机化每个像素,而是在绘制代码之外预先计算随机颜色的图块,然后随机 assemble 他们在 drawRect()。如果随机发生器是什么性能问题,这将减少它必须要做的工作量。

关键策略是尽量减少您的 drawRect() 方法必须完成的工作量,因为它 运行 在每个动画帧上。

祝你好运!