如何在 UIView 中不断更新绘图

Posted

技术标签:

【中文标题】如何在 UIView 中不断更新绘图【英文标题】:How to continuously update drawing in UIView 【发布时间】:2017-04-26 20:34:05 【问题描述】:

我不断地从源中读取数据(CGFloat 数组),并且我想从该源上的数据点在 UIView 上绘制一条线。

我了解我需要使用 UIBezierPath 来创建线路。最初我可以这样做,但我不知道如何继续更新 UIView 上的绘图 [就像一系列 path.move(..)]。

有人知道如何实现吗?

TIA

【问题讨论】:

一些想法。首先,由于您希望更新 UI,因此您需要主线程来执行此操作。接下来,你能解释一下“不断读取数据”是什么意思吗?该 CGFloat 数组上的一些代码会有所帮助。最后,你可能最终会捏造一些东西——比如让“看起来”像是在不断更新。最快的方法是 (1) 读取数组中的 所有 数据,同时 (2) 向用户提供一些反馈,然后 (3) 构建 Bezier 路径,然后 (4) 动画绘图它。 @dfd 我将从麦克风馈送中获取浮点数,并且我想实时绘制/绘制这些点。 【参考方案1】:

子类 UIView 并覆盖draw rect 方法:

import UIKit

class MyLineView: UIView 
    // Edit:add the data to draw
    public var lineData : Array<Float>?

    override func draw(_ rect: CGRect) 
        // Read the values and draw the lines
        // using your bezier functions

        // Edit: just to test - you can see the new data
        if let data = lineData 
            print("Redrawing \(data.count) elements")
        
    

在您的视图控制器中创建Timer 类型的属性,并以您所需的帧速率在适当的点启动它:

import UIKit

class ViewController: UIViewController 

    var animationTimer : Timer?
    var myLineView : MyLineView?
    **// Edit: add an array of data**
    var myData : Array<Float>?

    override func viewDidLoad() 
        super.viewDidLoad()

        self.myLineView = MyLineView(frame: self.view.bounds)
        self.view.addSubview(myLineView!)
        // Edit: init data
        self.myData = Array()
    

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

        // Update the view at roughly 10Hz
        animationTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block:  (timer) in
            //Edit: example update of the data before redrawing
            self.myData?.append(Float(1))
            self.myLineView!.lineData = self.myData!

            self.myLineView!.setNeedsDisplay()
        )
    

    override func viewDidDisappear(_ animated: Bool) 
        super.viewDidDisappear(animated)

        // Stop updating
        animationTimer!.invalidate()
        animationTimer = nil
    

这将以大约 10Hz 的速率在您的子类上调用 drawRect。

【讨论】:

我看到你调用了 setNeedDisplay(),但是我将如何在自定义 UIView 中操作我的贝塞尔路径? 我已更新我的答案以显示将数据传输到视图。使用您已经使用的代码来绘制初始线,只需将其扩展为每帧重新绘制整个数据集。 标记为答案,因为 self.myLineView!.lineData = self.myData! 帮助了我。我的数据拉取在另一个线程上,所以我不得不将self.myLineView!.setNeedsDisplay() 放入DispatchQueue.main.async 。谢谢! 在较新版本的 Swift 中,这些覆盖是什么?

以上是关于如何在 UIView 中不断更新绘图的主要内容,如果未能解决你的问题,请参考以下文章

如何使用弹出视图控制器更新 UIView 上的属性?

如何在执行自定义绘图的类方法中访问类的外观代理?

从 UIImage 清除 UIView 绘图

使用drawRect延迟加载UIView:绘图

大型自定义 UIView - CABackingStoreUpdate 性能

Swift drawRect 函数不会更新绘图