在 SwiftUI 中处理派生状态

Ste*_*ili 3 swift swiftui

假设我正在创建一个“日期编辑器”视图。目标是: - 采用默认的种子日期。- 它允许用户更改输入。- 如果用户随后选择,他们可以按“保存”,在这种情况下,视图的所有者可以决定对数据执行某些操作。

这是实现它的一种方法:

struct AlarmEditor : View {
    var seedDate : Date
    var handleSave : (Date) -> Void

    @State var editingDate : Date?

    var body : some View {
        let dateBinding : Binding<Date> = Binding(
            get: {
                return self.editingDate ?? seedDate
            },
            set: { date in
                self.editingDate = date
            }
        )

        return VStack {
            DatePicker(
                selection: dateBinding,
                displayedComponents: .hourAndMinute,
                label: { Text("Date") }
            )
            Spacer()
            Button(action: {
                self.handleSave(dateBinding.wrappedValue)
            }) {
                Text("Save").font(.headline).bold()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题

如果所有者改变 的值怎么办seedDate

在这种情况下,我想做的是将 的值重置editingDate为新的 seedDate。

这样做的惯用方法是什么?

Asp*_*eri 5

我更愿意通过此类编辑器显式使用 ViewModel 来完成此操作,并且它需要对代码进行最少的修改。这是可能的方法(使用 Xcode 11.2.1 进行测试和使用):

测试家长

struct TestAlarmEditor: View {
    private var editorModel = AlarmEditorViewModel()
    var body: some View {
        VStack {
            AlarmEditor(viewModel: self.editorModel, handleSave: {_ in }, editingDate: nil)
            Button("Reset") {
                self.editorModel.seedDate = Date(timeIntervalSinceNow: 60 * 60)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑器的简单视图模型

class AlarmEditorViewModel: ObservableObject {
    @Published var seedDate = Date() // << can be any or set via init
}
Run Code Online (Sandbox Code Playgroud)

更新了编辑器

struct AlarmEditor : View {
    @ObservedObject var viewModel : AlarmEditorViewModel

    var handleSave : (Date) -> Void

    @State var editingDate : Date?

    var body : some View {
        let dateBinding : Binding<Date> = Binding(
            get: {
                return self.editingDate ?? self.viewModel.seedDate
            },
            set: { date in
                self.editingDate = date
            }
        )

        return VStack {
            DatePicker(
                selection: dateBinding,
                displayedComponents: .hourAndMinute,
                label: { Text("Date") }
            )
            .onReceive(self.viewModel.$seedDate, perform: { 
                self.editingDate = $0 })                    // << reset here
            Spacer()
            Button(action: {
                self.handleSave(dateBinding.wrappedValue)
            }) {
                Text("Save").font(.headline).bold()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)