【问题标题】:Constantly animated view in SwiftSwift 中的持续动画视图
【发布时间】:2018-07-03 17:08:54
【问题描述】:

我正在创建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()
    } 
}

我正在从其他 ViewController 调用方法 animate(),以保留 NoiseView 对象。

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

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

【问题讨论】:

  • 你试图通过用高度和宽度为 1px 的随机条纹填充整个屏幕来实现什么动画?顺便说一句,减轻 CPU 的负担并利用使用 GPU 的核心图形 API 将解决减少帧丢失的问题。但是我不能在不知道你想要实现的动画的情况下发布代码
  • 我正在尝试实现白噪声。它应该使用一组新的灰点重新渲染每一帧。

标签: ios swift animation draw


【解决方案1】:

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

您正在运行的循环需要 每一帧动画 进行 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()
}

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

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

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

祝你好运!

【讨论】:

  • 这仍然会最大化 CPU,需要在 CALayer 中使用动画才能在 GPU 上工作。
  • 你能指出他们在 Apple 的文档(或相关文档)中的哪个地方这么说吗,因为 UIViews 是由 CALayers 支持的,并且是否使用 CPU 或 GPU 来绘制并不取决于你是否直接操作CALayer。但我可能错了。
【解决方案2】:

而不是使用计时器并调用 setNeedsDisplay,结果将继续调用 draw 方法,从而降低应用程序的速度。

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

如果您需要代码,可以查看此链接Color Picker 或者我可以在一段时间后发布一些演示代码,它的国际足联时间:-p。

CAReplicatorlayerCALayer

【讨论】:

    【解决方案3】:

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

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

    CABasicAnimation Documentation

    CAAnimationGroup StackOverflow Post

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-21
      • 1970-01-01
      • 2017-09-18
      • 2021-12-29
      • 2018-06-15
      • 1970-01-01
      • 2017-01-17
      • 2016-08-25
      相关资源
      最近更新 更多