如何通知符合协议的ObservedObject更改

时间:2020-09-13 10:01:05

标签: swiftui protocols observedobject

我正在构建一个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
}


2 个答案:

答案 0 :(得分:2)

您在协议中的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()
    }
}

答案 1 :(得分:2)

错误是因为通用address.$location_name不存在Published<String>(您希望是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 {}