Привязка внутри детали NavigationSplitView (TextField, TextEditor)

Я использую двухколоночныйNavigationSplitView. Попытка выяснить, как обновить модель данных через.onSubmitмодификатор и использовать представление безBinding.constant.

В рамкахdetailраздел, у меня естьTextFieldиTextEditor.

  1. Как избежатьBinding.contant()? Я имею в виду, мне нужна мутация.
  2. Это правильный способ обновленияvalueсобственность в модели?

Мне нужен один выбор в списке.

Вот мой пример кода (70 строк):

      struct Model: Identifiable, Hashable {
    var id = UUID()
    var title: String = "Brand new"
    var value: String = ""
    
    func updateValue() async -> Model {
        return Model(id: id, title: title, value: "The boar  is running through the field happily")
    }
}

final class DataModel: ObservableObject {
    @Published
    var models: [Model] = [
        .init(title: "First", value: "fur"),
        .init(title: "Second", value: "meow"),
        .init(title: "Another", value: "Make SwiftUI, not war")
    ]
    
    @MainActor
    func updateModel(for model: Model.ID) async -> Void {
        var findModel = models.first { $0.id == model }
        findModel = await findModel?.updateValue()
    }
}

struct ModelView: View {
    @StateObject
    private var dataModel = DataModel()
    
    @State
    private var listSelection: Model.ID?
    
    private var selectedModel: Model? {
        guard let selection = listSelection else { return nil }
        return dataModel.models.first { $0.id == selection }
    }
    
    var body: some View {
        NavigationSplitView {
            List(dataModel.models, selection: $listSelection) { model in
                NavigationLink(model.title, value: model.id)
            }
        } detail: {
            if let selectedModel {
                VStack {
                    TextField("Title", text: .constant(selectedModel.title))
                        .padding()
                        .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))
                        .submitLabel(.go)
                        .onSubmit {
                            Task {
                                // Update Model.value by hit `Go`
                                await dataModel.updateModel(for: selectedModel.id)
                            }
                        }
                    TextEditor(text: .constant(selectedModel.value))
                }
                .padding()
                .navigationTitle(selectedModel.title)
            }
        }
    }
}

struct ModelView_Previews: PreviewProvider {
    static var previews: some View {
        ModelView()
            .colorScheme(.light)
    }
}

1 ответ

Через пару дней я понял, что я могу сделать. Никто не ответил на вопрос, поэтому я решил проблему таким образом.

Окончательное решение ниже:

      struct Model: Identifiable, Hashable {
    var id = UUID()
    var title: String = "Brand new"
    var value: String = ""
    
    func updateValue() async -> Model {
        return Model(id: id, title: title, value: "The boar  is running through the field happily")
    }
}

final class DataModel: ObservableObject {
    @Published
    var models: [Model] = [
        .init(title: "First", value: "fur"),
        .init(title: "Second", value: "meow"),
        .init(title: "Another", value: "Make SwiftUI, not war")
    ]
    
    @MainActor
    func updateModel(for model: Binding<Model>) async -> Void {
        model.wrappedValue = await model.wrappedValue.updateValue()
    }
    
    func bindingToModel(_ model: Model.ID) -> Binding<Model> {
        Binding<Model> {
            guard let index = self.models.firstIndex(where: { $0.id == model }) else {
                return Model()
            }
            return self.models[index]
        } set: { newModel in
            guard let index = self.models.firstIndex(where: { $0.id == model }) else { return }
            self.models[index] = newModel
        }
    }
}

struct ModelView: View {
    @StateObject
    private var dataModel = DataModel()
    
    @State
    private var listSelection: Model.ID?
    
    var body: some View {
        NavigationSplitView {
            List(dataModel.models, selection: $listSelection) { model in
                NavigationLink(model.title, value: model.id)
            }
        } detail: {
            if let listSelection, let bindModel = dataModel.bindingToModel(listSelection)  {
                VStack {
                    TextField("Title", text: bindModel.title)
                        .padding()
                        .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))
                        .submitLabel(.go)
                        .onSubmit {
                            Task {
                                // Update Model.value by hit `Go`
                                await dataModel.updateModel(for: bindModel)
                            }
                        }
                    TextEditor(text: bindModel.value)
                }
                .padding()
                .navigationTitle(bindModel.title)
            }
        }
    }
}
Другие вопросы по тегам