与更改数组值同步更新 UIView

Posted

技术标签:

【中文标题】与更改数组值同步更新 UIView【英文标题】:updating UIView synchronously with changing array values 【发布时间】:2017-04-04 10:37:28 【问题描述】:

我想根据从数组中获取的数据在UIView 中创建多个水平条。 我想要这样的东西:

但是当我试图使其通用时,它会显示如下内容:

这是我的draw 函数:

override func draw(_ rect: CGRect) 
    guard let currentGraphicsContext = UIGraphicsGetCurrentContext() else  return 

    var sumOfAllSegments : CGFloat = 0
    dataToRepresent.forEach (element) in
        sumOfAllSegments += CGFloat(element["value"] as! CGFloat)
    
    var lastSegmentRect = CGRect()
    var progressRect = rect
    dataToRepresent.forEach  (element) in
        let currentSegmentValue = element["value"] as! CGFloat
        let color = element ["color"] as! CGColor
        let percentage = (currentSegmentValue/sumOfAllSegments) * frame.width
        progressRect.size.width = percentage
        progressRect.origin.x = lastSegmentRect.origin.x + lastSegmentRect.size.width
        if(progressRect.origin.x > 0) 
            progressRect.origin.x += 2.0
        
        lastSegmentRect = progressRect
        makingBars(rect: progressRect , color: color , context: currentGraphicsContext)
    

makingBars函数:

    func makingBars(rect : CGRect , color : CGColor , context : CGContext) 
    context.addRect(rect)
    context.setFillColor(color)
    DispatchQueue.global(qos: .background).async 
        DispatchQueue.main.async 
            context.fill(rect)
        
    

编辑 1:

这是崩溃的截图:

编辑 2:

【问题讨论】:

不能使用 DispatchQueue 来绘制进程。 为什么不尝试设置条的高度?您正在设置宽度并且它正在工作,但您没有控制任何关于高度的东西。您可以根据顶部栏的 y 位置和高度以及底部栏的 y 位置来计算它。 progressRect.size.height = (topBar.origin.y + topbar.size.height) - botttomBar.origin.y 【参考方案1】:

正如 Aitor Pagán 在他的评论中指出的那样,您没有正确设置矩形的高度。

除此之外,视图的draw(_:) 方法必须同步操作。您不能使用 GCD。摆脱 DispatchQueue.global()/DispatchQueue.main.async() 代码。它会阻止您的绘图方法正常运行。 (系统在调用我们的 draw 方法之前设置了绘图上下文。绘图上下文在 draw 方法之外不再有效,并且您使用 GCD 的方式,您的填充调用不会被调用,直到 draw 方法之后返回。)

【讨论】:

我正在使用:progressRect.size.height = frame.height * 2 为我的矩形设置高度,如果我不使用 GCD,我的应用程序崩溃,在 context.fill(rect) 行给出 EXC_BAD_ACCESS 您发布的代码根本没有设置progressRect.size.height。如果您更新了代码,请编辑您的问题以显示新代码。您发布的 GCD 代码是绝对错误的,并且永远无法正常工作。我不确定你为什么会崩溃,但这是错误的解决方案。发布您的更新代码。【参考方案2】:

自动布局可以更轻松地实现此类类型要求。您会发现此线程很有用。 Creating a 3x3 grid with auto layout constraints 但在这种情况下,您必须以编程方式进行约束。手动基于框架的布局会产生问题,因为我们有不同的屏幕尺寸和旋转。

【讨论】:

【参考方案3】:

这个gist 是一个绘制水平块的自定义 UIView,它是一个绘制电池电量表的视图。它只是通过添加子视图来实现。

我怀疑您的代码只是错误地计算了一些帧 - 要对此进行测试,您可以更改每个条的颜色和/或仅绘制少量条以检查重叠的帧。

【讨论】:

我认为你的代码很糟糕。您不是故意使用 drawRect 方法。 我不认为我理解你。来自文档“使用 Core Graphics 和 UIKit 等技术来绘制其视图内容的子类应覆盖此方法并在那里实现其绘制代码。”。这就是这个子类所做的......无论如何我的代码可能仍然[非常]糟糕 此方法中需要使用CoreGraphics函数来实现视图的位图表示。但是您更改视图层次结构这是错误的。你可以这样做,但这是不好的方式。 不,请参阅文档 - 在 drawRect 中允许使用 UIKit。这是链接 - developer.apple.com/reference/uikit/uiview/1622529-drawrect 你可以使用 UIKit 像 [image drawInRect:], [string drawInRect:] 等等The default implementation of this method does nothing. Subclasses that use technologies such as Core Graphics and UIKit to draw their view’s content should override this method and implement their drawing code there. You do not need to override this method if your view sets its content in other ways. For example, you do not need to override this method if your view just displays a background color or if your view sets its content directly using the underlying layer object.【参考方案4】:

尝试通过这样的想法更改您的代码:

override func draw(_ rect: CGRect) 
    guard let currentGraphicsContext = UIGraphicsGetCurrentContext() else  return 

    var sumOfAllSegments : CGFloat = 0
    dataToRepresent.forEach (element) in
        sumOfAllSegments += CGFloat(element["value"] as! CGFloat)
    
    var position: CGFloat = 0
    var progressRect = rect
    dataToRepresent.forEach  (element) in
        let currentSegmentValue = element["value"] as! CGFloat
        let color = element ["color"] as! CGColor
        let percentage = (currentSegmentValue/sumOfAllSegments) * frame.width
        progressRect.size.width = percentage
        progressRect.origin.x = position
        if(progressRect.origin.x > 0) 
            progressRect.origin.x += 2.0
        

        makingBars(rect: progressRect , color: color , context: currentGraphicsContext)
        position += progressRect.width + 2.0
    

func makingBars(rect : CGRect , color : CGColor , context : CGContext) 
    context.addRect(rect)
    context.setFillColor(color)
    content.drawPath(using:.fill)

【讨论】:

你自己调用draw(rect:)吗?能否提供崩溃时的调用堆栈? 不,我没有自己调用 draw(rect:),并检查 EDIT 2 的调用堆栈

以上是关于与更改数组值同步更新 UIView的主要内容,如果未能解决你的问题,请参考以下文章

Vue使用v-for显示列表时,数组里的item数据更新,视图中列表不同步更新的解决方法

更新/更改数组值(快速)

Angular:绑定并与数组保持同步

更改 UIView 的框架并更新cornerRadius

更新 UIView 会产生子视图对齐/Swift 的散列

更改滑块时如何更新子类 UIView 以进行绘图?