2

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


4

2 回答 2

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()
    }
}
于 2020-09-13T10:23:41.060 回答
2

错误是因为address.$location_name- 您希望成为Published<String>发布者 - 不存在 generic 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 {}
于 2020-09-13T15:42:26.767 回答