我使用 Xcode 11 beta 6 ,尝试使用@Published
为具有属性的类型声明协议(但该问题可以推广为 any 我猜是PropertyWrapper)。
final class DefaultWelcomeViewModel: WelcomeViewModel & ObservableObject {
@Published var hasAgreedToTermsAndConditions = false
}
我要声明的内容:
protocol WelcomeViewModel {
@Published var hasAgreedToTermsAndConditions: Bool { get }
}
这会导致编译错误:Property 'hasAgreedToTermsAndConditions' declared inside a protocol cannot have a wrapper
所以我尝试将其更改为:
protocol WelcomeViewModel {
var hasAgreedToTermsAndConditions: Published<Bool> { get }
}
并尝试
DefaultWelcomeViewModel does not conform to protocol
不会编译,嗯,所以我不能使用Published<Bool>
,让我们尝试一下!
struct WelcomeScreen<ViewModel> where ViewModel: WelcomeViewModel & ObservableObject {
@EnvironmentObject private var viewModel: ViewModel
var body: some View {
// Compilation error: `Cannot convert value of type 'Published<Bool>' to expected argument type 'Binding<Bool>'`
Toggle(isOn: viewModel.hasAgreedToTermsAndConditions) {
Text("I agree to the terms and conditions")
}
}
}
// MARK: - ViewModel
protocol WelcomeViewModel {
var hasAgreedToTermsAndConditions: Published<Bool> { get }
}
final class DefaultWelcomeViewModel: WelcomeViewModel & ObservableObject {
var hasAgreedToTermsAndConditions = Published<Bool>(initialValue: false)
}
这会导致Toggle
:Cannot convert value of type 'Published<Bool>' to expected argument type 'Binding<Bool>'
上的编译错误。
答案 0 :(得分:6)
我认为您要提出的明确问题与您要解决的问题不同,但我会尽力为您提供帮助。
首先,您已经意识到不能在协议内声明属性包装器。这是因为属性包装器声明在编译时被合成为三个单独的属性,而这不适用于抽象类型。
因此,要回答您的问题,您不能在协议内部明确声明属性包装器,但是可以为属性包装器的每个综合属性创建单独的属性要求,例如:
protocol WelcomeViewModel {
var hasAgreed: Bool { get }
var hasAgreedPublished: Published<Bool> { get }
var hasAgreedPublisher: Published<Bool>.Publisher { get }
}
final class DefaultWelcomeViewModel: ObservableObject, WelcomeViewModel {
@Published var hasAgreed: Bool = false
var hasAgreedPublished: Published<Bool> { _hasAgreed }
var hasAgreedPublisher: Published<Bool>.Publisher { $hasAgreed }
}
如您所见,属性包装器已经在具体类型上合成了两个属性(_hasAgreed
和$hasAgreed
),我们可以简单地从协议所需的计算属性中返回它们。 / p>
现在,我相信Toggle
完全存在另一个问题,编译器很乐意提醒我们:
无法将“已发布”类型的值转换为预期参数类型“绑定”
此错误也很简单。 Toggle
期望有Binding<Bool>
,但我们正在尝试提供一种不同类型的Published<Bool>
。幸运的是,我们选择使用@EnvironmentObject
,这使我们能够在viewModel
上使用“投影值”来获得视图模型属性的Binding
。使用合格属性包装器上的$
前缀可以访问这些值。实际上,上面我们已经使用hasAgreedPublisher
属性来完成此操作。
因此,我们将Toggle
更新为使用Binding
:
struct WelcomeView: View {
@EnvironmentObject var viewModel: DefaultWelcomeViewModel
var body: some View {
Toggle(isOn: $viewModel.hasAgreed) {
Text("I agree to the terms and conditions")
}
}
}
通过在viewModel
前面加上$
,我们可以访问一个在视图模型上支持“动态成员查找”的对象,以便获得视图成员的Binding
模型。