【问题标题】:How to be notified of change to ObservedObject which conforms to a protocol如何通知符合协议的 ObservedObject 的更改
【发布时间】:2020-09-13 10:01:05
【问题描述】:

我正在构建一个 AutoCompletion 视图,并希望传递一个包含要自动完成的字段的对象。 目前我有两种不同的类型,我需要在一个属性上自动完成,这两个属性的名称相同。我已经创建了一个协议,并正在使用它来构建一个通用视图来接受它。

我遇到的问题是 onReceive 破坏了编译。不幸的是,除了“无法推断复杂的闭包返回类型...”之外,我无法收到错误消息,但如果我注释掉 onReceive,错误就会清除。

如果我将结构中的 LocationNameAutoComplete 替换为 Address,那么它可以编译并运行良好 - 但这意味着我不能将它与其他类型的 FactorySite 一起使用。

如果我能看到有关 onReceive 的实际错误消息,那将是一个开始...

有没有更好的方法来做到这一点?

谢谢

struct LocationNameTextField<T>: View where T: LocationNameAutoComplete {

    @ObservedObject var address: T

    var body: some View {
        VStack {
            TextField("", text: $address.location_name)
                .onReceive(self.address.$location_name) { attr in
                    print("OK")
                }
        }
    }
}



protocol LocationNameAutoComplete: ObservableObject {
    var location_name: String {get set}
}

struct Address: LocationNameAutoComplete {
    @Published var location_name: String
}

struct FactorySite: LocationNameAutoComplete {
    @Published var location_name: String
}


【问题讨论】:

    标签: swiftui protocols observedobject


    【解决方案1】:

    您在协议中的location_name 属性不是发布者,因此您不能在通用视图中引用它,您拥有的唯一发布者是objectWillChange

    这是可编译的代码,包括一些其他修复,(Xcode 11.7)

    struct LocationNameTextField<T>: View where T: LocationNameAutoComplete {
    
        @ObservedObject var address: T
    
        var body: some View {
            VStack {
                TextField("", text: $address.location_name)
                    .onReceive(self.address.objectWillChange) { _ in
                        print("OK")
                    }
            }
        }
    }
    
    protocol LocationNameAutoComplete: ObservableObject {
        var location_name: String {get set}
    }
    
    class Address: LocationNameAutoComplete {
        @Published var location_name: String = ""
    }
    
    class FactorySite: LocationNameAutoComplete {
        @Published var location_name: String = ""
    }
    

    更新:如果您需要某些属性的显式通用发布者(在本例中为 location_name),这是可能的方法

    struct LocationNameTextField<T>: View where T: LocationNameAutoComplete {
    
        @ObservedObject var address: T
    
        var body: some View {
            VStack {
                TextField("", text: $address.location_name)
                    .onReceive(self.address.location_name_publisher) { attr in
                        print("OK")
                    }
            }
        }
    }
    
    protocol LocationNameAutoComplete: ObservableObject {
        var location_name: String {get set}
        var location_name_publisher: AnyPublisher<String, Never> { get }
    }
    
    class Address: LocationNameAutoComplete {
        @Published var location_name: String = ""
    
        var location_name_publisher: AnyPublisher<String, Never> {
            $location_name.eraseToAnyPublisher()
        }
    }
    
    class FactorySite: LocationNameAutoComplete {
        @Published var location_name: String = ""
    
        var location_name_publisher: AnyPublisher<String, Never> {
            $location_name.eraseToAnyPublisher()
        }
    }
    

    【讨论】:

    • 有趣。第一个解决方案给了我用户输入的单词,但 address.location_name 中缺少最后一个字母。第二种解决方案在每次按下一个键时输出两次 OK。它给了我另一种思考的方法。谢谢。
    【解决方案2】:

    错误是因为address.$location_name - 您希望它是 Published&lt;String&gt; 发布者 - 对于通用 T 不存在,因为协议 LocationNameAutoComplete 不需要它,而您不能使用属性包装器@Published 自动综合此需求。

    一种方法是手动定义发布者属性并在每个符合类型中实现它(如 Asperi 所示)。

    另一种方法是创建一个基类而不是实现它的协议:

    class LocationNameAutoComplete: ObservableObject {
        @Published var location_name: String
    
        init(location: String) { self.location = location }
    }
    

    然后,几乎所有其他内容都保持不变:

    struct LocationNameTextField<T>: View where T: LocationNameAutoComplete {
        @ObservedObject var address: T
    
        var body: some View {
            VStack {
                TextField("", text: $address.location_name)
                    .onReceive(self.address.$location_name) { attr in
                        print("OK")
                    }
            }
        }
    } 
    
    class Address: LocationNameAutoComplete {}
    
    class FactorySite: LocationNameAutoComplete {}
    

    【讨论】:

    • 我喜欢使用子类来实现职责分离。目前没有要求在一个表单中包含多个自动完成字段,但如果我猜有多个字段,它可能会变得混乱。
    猜你喜欢
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多