好的,我花了一些时间弄清楚这些内容,但一旦我搞对了,一切都很清晰明了。
目前不可能在协议中使用PropertyWrappers。但您可以在视图中使用泛型,并期望您的ViewModel符合您的协议。这非常适合测试或者如果您需要为预览设置轻量级的东西。
我有一些示例样例,以便您可以正确配置您的代码
协议:
protocol UploadStoreProtocol:ObservableObject {
var uploads:[UploadModel] {get set}
}
视图模型:
您需要确保您的视图模型是ObservableObject
,并在可以更改的变量上添加@Published
。
class SamplePreviewStore:UploadStoreProtocol {
@Published var uploads:[UploadModel] = []
init() {
uploads.append( UploadModel(id: "1", fileName: "Image 1", progress: 0, started: true, errorMessage: nil))
uploads.append( UploadModel(id: "2", fileName: "Image 2", progress: 47, started: true, errorMessage: nil))
uploads.append( UploadModel(id: "3", fileName: "Image 3", progress: 0, started: false, errorMessage: nil))
}
}
class UploadStorage:UploadStoreProtocol {
@Published var uploads:[UploadModel] = []
init() {
uploads.append( UploadModel(id: "1", fileName: "Image 1", progress: 0, started: false, errorMessage: nil))
uploads.append( UploadModel(id: "2", fileName: "Image 2", progress: 0, started: false, errorMessage: nil))
uploads.append( UploadModel(id: "3", fileName: "Image 3", progress: 0, started: false, errorMessage: nil))
uploads.append( UploadModel(id: "4", fileName: "Image 4", progress: 0, started: false, errorMessage: nil))
uploads.append( UploadModel(id: "5", fileName: "Image 5", progress: 0, started: false, errorMessage: nil))
}
func addItem(){
uploads.append( UploadModel(id: "\(Int.random(in: 100 ... 100000))", fileName: "Image XX", progress: 0, started: false, errorMessage: nil))
}
func removeItemAt(index:Int){
uploads.remove(at: index)
}
}
对于 UI 视图,您可以使用泛型:
struct UploadView<ViewModel>: View where ViewModel:UploadStoreProtocol {
@ObservedObject var store:ViewModel
var body: some View {
List(store.uploads.indices){ item in
ImageRow(item: $store.uploads[item])
}.padding()
}
}
struct ImageRow: View {
@Binding var item:UploadModel
var body: some View {
HStack{
Image(item.id ?? "")
.resizable()
.frame(width: 50.0, height: 50.0)
VStack (alignment: .leading, spacing: nil, content: {
Text(item.fileName ?? "-")
Text(item.errorMessage ?? "")
.font(.caption)
.foregroundColor(.red)
})
Spacer()
VStack {
if (item.started){
Text("\(item.progress)").foregroundColor(.purple)
}
UploadButton(is_started: $item.started)
}
}
}
}
现在您的视图已准备好获取ViewModel,您可以在外部设置您的存储库:
@main
struct SampleApp: App {
@StateObject var uploadStore = UploadStorage()
var body: some Scene {
WindowGroup {
UploadView(store: uploadStore)
}
}
}
同时,您可以预览以下内容:
struct ContentView_Previews: PreviewProvider {
@StateObject static var uploadStore = SamplePreviewStore()
static var previews: some View {
UploadView(store: uploadStore)
}
}
@Published var some
会生成私有存储属性_some
,而协议是禁止使用的。 - Asperi@Published
,而是通过objectWillChange
发布器手动发布更改。这样做的好处是我们可以摆脱默认的协议实现。更多信息请参见:https://www.hackingwithswift.com/books/ios-swiftui/manually-publishing-observableobject-changes - Bartosz Olszanowski@EnvironmentObject
,只是需要手动指定泛型的具体类。例如:NavigationLink(destination: ItemView<SomeItemModel>()) { ... }
- Jeremy