首页 > 解决方案 > 为什么这个绑定在 macOS 的子视图中不起作用?

问题描述

我有几层 ObservableObjects 和 Published 属性。当我直接在视图中使用它们时,它们似乎按预期工作。但是,当我尝试将列表移动到它自己的类型时,父视图中的绑定似乎不起作用。

例如,为什么启用 ModelList,当您选择行时,Button 不会在启用和禁用之间切换。但是,如果您将其注释掉并启用 List.init 行,那么在选择和取消选择行时,按钮会正确启用和禁用。

https://youtu.be/7Kvh2w8z__4

这有效

View
 List(selection: viewModel.dataStore.selection)

这不

View
 ModelList(dataStore: viewModel.dataStore) 
  List(selection: dataStore.selection)

完整代码示例

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        VStack {
            // Using the the dataStore, the button bind works
            //            List.init(viewModel.dataStore.models, id: \.id, selection: $viewModel.dataStore.selection) {
            //                Text("Name: \($0.name)")
            //            }

            // Using the dataStore in the subview, the button binding doesn't work
            ModelList(dataStore: viewModel.dataStore)
            
            Button(action: {
                print("Delete")
            }, label: {
                Image(systemName: "minus")
            })
            .disabled($viewModel.dataStore.selection.wrappedValue.count == 0)
            Text("Selection \($viewModel.dataStore.selection.wrappedValue.count)")
        }
    }
}
struct ModelList: View {
    @ObservedObject public var dataStore: DataStore
    
    var body: some View {
        List.init(dataStore.models, id: \.id, selection: $dataStore.selection) {
            Text("Name: \($0.name)")
        }
    }
}

class ViewModel: ObservableObject {
    @Published var dataStore: DataStore = DataStore()
}
class DataStore: ObservableObject {
    @Published public var selection = Set<Int>()
    @Published public var models = [Model(id: 1, name: "First")]
}

struct Model: Identifiable {
    let id: Int
    let name: String
}

@main
struct LayersApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: ViewModel())
        }
    }
}

标签: macosswiftui

解决方案


子视图应该接受一个绑定,而不是另一个 ObservedObject。

@ObservedObject public var dataStore: DataStore应该@Binding public var dataStore: DataStore

现在在使用子视图时,传入绑定ModelList(dataStore: $viewModel.dataStore)

完整的工作示例:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        VStack {
            ModelList(dataStore: $viewModel.dataStore)
            
            Button(action: {
                print("Delete \(viewModel.dataStore.selection)")
            }, label: {
                Image(systemName: "minus")
            })
            .disabled($viewModel.dataStore.selection.wrappedValue.count == 0)
            Text("Selection \($viewModel.dataStore.selection.wrappedValue.count)")
        }
    }
}
struct ModelList: View {
    @Binding public var dataStore: DataStore

    var body: some View {
        List.init(dataStore.models,
                  id: \.id,
                  selection: $dataStore.selection) {
            Text("Name: \($0.name)")
        }
    }
}

class ViewModel: ObservableObject {
    @Published var dataStore: DataStore = DataStore()
    
    init() {
        print("ViewModel")
    }
}
class DataStore: ObservableObject {
    
    @Published public var selection = Set<Int>()
    @Published public var models = [Model(id: 1, name: "First")]
    
    init() {
        print("DataStore")
    }
}

struct Model: Identifiable, Equatable {
    let id: Int
    let name: String
}

@main
struct LayersApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: ViewModel())
        }
    }
}

推荐阅读