@BindableObject 对 didChange.send() 的异步调用不会使其视图无效(并且永远不会更新)

Posted

技术标签:

【中文标题】@BindableObject 对 didChange.send() 的异步调用不会使其视图无效(并且永远不会更新)【英文标题】:@BindableObject async call to didChange.send() does not invalidate its view (and never updates) 【发布时间】:2019-07-10 11:23:14 【问题描述】:

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

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

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

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

在某些其他情况下,将 @ObjectBinding 替换为 @EnvironmentObject 可以解决此问题。但在这种情况下,它只会有一点改善。

我在回答另一个问题时遇到了这个问题: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()
        
    

【问题讨论】:

【参考方案1】:

您需要将 didChange 替换为 let objectWillChange = ObservableObjectPublisher(),因为 ObservableObject 已经隐式实现了 PassthroughtSubject。

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

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

我发现 this link 解释了如何触发视图失效。

【讨论】:

非常感谢您的回答。我知道 ObservableObject 的变化。这是一个古老的问题(7 月 10 日)。我应该更新它或完全删除它。我已经忘记了。感谢您引起我的注意!干杯。

以上是关于@BindableObject 对 didChange.send() 的异步调用不会使其视图无效(并且永远不会更新)的主要内容,如果未能解决你的问题,请参考以下文章

不符合协议 BindableObject - Xcode 11 Beta 4

WPF - 为啥在 XAML 中设置 Binding BindableObject.Text 时 SetProperty() 不触发 CanExecute?

解析 NSBlockOperation 的 executionBlocks

读 MAUI 源代码 理解可绑定对象和可绑定属性的存储机制

如何使用 ObservableObject 更新 UIViewRepresentable

SwiftUI 可观察对象返回陈旧数据