@BindableObject 异步调用 didChange.send() 不会使其视图无效(并且永远不会更新) [英] @BindableObject async call to didChange.send() does not invalidate its view (and never updates)

查看:23
本文介绍了@BindableObject 异步调用 didChange.send() 不会使其视图无效(并且永远不会更新)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在@ObjectBinding 中发现了我认为的错误.对于@BindableObject 异步调用其 didChange.send() 的情况,有时依赖于该更改的视图不会失效,因此它们的主体不会再次被请求,它们的外观也不会更新.

I have found what I think is a bug in @ObjectBinding. For cases when a @BindableObject get its didChange.send() called asynchronously, sometimes views that depend on that change, do not get invalidated, so their bodies are never requested again, and their look is not updated.

List 视图中的行应该每秒更新一次.起初它工作正常.但是,当使用+"按钮添加新行时,事情开始走向横向.

The rows in the List view should update every second. At first it works fine. However, when new rows are added with the '+' button, things begin to go sideways.

我使行 tappable,所以当它们被点击时,@State 变量会改变以改变文本的颜色.由于正确检测到此绑定,因此视图无效并且其外观会发生变化.这表明计时器实际上正在运行,即使视图直到点击它才显示它.

I made the rows tappable, so than when they are tapped, a @State variable changes to alter the color of text. Because this binding is detected properly, the view is invalidated and its look changes. This shows that the timer was actually running, even though the view did not show it until it was tapped.

请注意,以下代码的唯一目的是演示这种奇怪的行为.我不是在寻找解决方法.有了这个问题,我的目的是确认这是否是一个错误,如果不是,我做错了什么.

Note that the only purpose of the code below is to demonstrate this odd behaviour. I'm not looking for a workaround. With this question I am aiming to confirm if this is a bug, and if not, what I am doing wrong.

在其他一些情况下,用@EnvironmentObject 替换@ObjectBinding 可以修复它.但在这种情况下,它只会改善一点.

In some other cases, replacing @ObjectBinding by @EnvironmentObject fixes it. In this case though, it only improves a little.

我在回答另一个问题时遇到了这个问题:下载图像时 SwiftUI 和 Combine 无法顺利运行

I came across this issue when answering this other question: SwiftUI and Combine not working smoothly when downloading image

更新:顺便说一句,如果@ObjectBinding 被@State 取代,事情会很顺利.

import SwiftUI
import Combine

struct Row: Equatable {
    let id: Int
    let name: String
}

struct ContentView : View {
    @State private var rows: [Row] = [Row(id: 0, name: "Row #0"), Row(id: 1, name: "Row #1")]

    var body: some View {
        NavigationView {
            List(rows.identified(by: \.id)) { row in
                RowView(row: row, rowData: RowData())
            }
            .navigationBarItems(trailing:
                Button(action: {
                    self.addRow()
                }, label: {
                    Text("+").font(.system(size: 36.0))
                }))
        }
    }

    func addRow() {
        rows.append(Row(id: rows.count, name: "Row #\(rows.count)"))
    }

}

struct RowView: View {
    let row: Row
    @ObjectBinding var rowData: RowData
    @State private var showBlue = false

    var body: some View {
        Text("\(row.name) - Seconds \(rowData.value)").foregroundColor(showBlue ? Color.blue : Color.primary)
            .tapAction {
                self.showBlue.toggle()
        }
    }
}

class RowData: BindableObject {
    var didChange = PassthroughSubject<Void, Never>()

    var value: Int = 1 {
        didSet { didChange.send() }
    }

    init() {
        update()
    }

    func update() {
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
            self.value += 1
            self.update()
        }
    }
}

推荐答案

你需要用 let objectWillChange = ObservableObjectPublisher() 替换你的 didChange,因为 ObservableObject 已经隐式地实现了 PassthroughtSubject.

You need to replace your didChange with let objectWillChange = ObservableObjectPublisher() as ObservableObject implicitly already implements a PassthroughtSubject.

此外,Apple 文档现在建议使用 @Published 而不是 didSet:@Published var value: Int = 1

Also, Apple docs recommends now to use @Published instead of the didSet: @Published var value: Int = 1

在您的更新函数中,将 self.update() 替换为 self.objectWillChange.send()

In your update function, replace self.update() with self.objectWillChange.send()

我发现了这个链接 解释如何触发视图失效.

I found this link explaining how to trigger a view invalidation.

这篇关于@BindableObject 异步调用 didChange.send() 不会使其视图无效(并且永远不会更新)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆