Created
December 21, 2022 12:36
-
-
Save michaelevensen/30b451acc08aad4306fbc7ab5b03139b to your computer and use it in GitHub Desktop.
Example of how you can create a Master Detail data editing and creation flow in SwiftUI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BindingTest.swift | |
// MutatingState | |
// | |
// Created by Michael Nino Evensen on 21/12/2022. | |
// | |
import SwiftUI | |
struct Object: Identifiable { | |
var id = UUID() | |
var value: Int | |
var string: String? | |
var date: Date? | |
init(value: Int = 1, string: String? = nil, date: Date? = nil) { | |
self.value = value | |
self.string = string | |
self.date = date | |
} | |
struct MutableData { | |
var value: Int = 1 | |
var string: String = "" | |
var date: Date = .now | |
} | |
var mutableData: MutableData { | |
return MutableData(value: value, string: string ?? "", date: date ?? .now) | |
} | |
mutating func update(with data: MutableData) -> Self { | |
self.value = data.value | |
self.string = data.string | |
self.date = data.date | |
return self | |
} | |
} | |
struct BindingTest: View { | |
@State private var showNewItemSheet: Bool = false | |
@State private var items = [Object]() | |
// New | |
@State var new = Object() | |
var body: some View { | |
NavigationStack { | |
List($items) { item in | |
NavigationLink { | |
BindingTestDetailView(initialValue: item) | |
} label: { | |
HStack { | |
VStack(alignment: .leading) { | |
if let title = item.wrappedValue.string { | |
Text(title) | |
} | |
if let date = item.wrappedValue.date { | |
Text(date.formatted(.dateTime.day().month())) | |
.foregroundColor(.secondary) | |
.font(.caption2) | |
} | |
} | |
Spacer() | |
Text(String(describing: item.wrappedValue.value.formatted(.number))) | |
.foregroundColor(.secondary) | |
} | |
} | |
} | |
} | |
.safeAreaInset(edge: .bottom) { | |
Button("+ Add new") { | |
showNewItemSheet = true | |
} | |
.buttonStyle(.borderedProminent) | |
} | |
.sheet(isPresented: $showNewItemSheet) { | |
BindingTestDetailView(initialValue: $new, didSave: { newItem in | |
self.items.append(newItem) | |
// Reset | |
new = Object() | |
}) | |
} | |
} | |
} | |
struct BindingTestDetailView: View { | |
@Environment(\.dismiss) private var dismiss | |
@Binding var obj: Object | |
@State private var didSpecifyDate: Bool = false | |
// Mutable local state | |
@State private var mutable = Object.MutableData() | |
let didSave: ((Object) -> Void)? | |
init( | |
initialValue: Binding<Object>, | |
didSave: ((Object) -> Void)? = { _ in } | |
) { | |
self._obj = initialValue | |
self.didSave = didSave | |
self._mutable = State(initialValue: initialValue.wrappedValue.mutableData) | |
if let _ = initialValue.wrappedValue.date { | |
self._didSpecifyDate = State(initialValue: true) | |
} | |
} | |
var body: some View { | |
VStack { | |
Text(String(describing: self.mutable)) | |
TextField("String", text: $mutable.string) | |
Toggle(isOn: $didSpecifyDate) { | |
Label("Has Date", systemImage: "calendar") | |
} | |
if didSpecifyDate { | |
DatePicker("Date", selection: $mutable.date, displayedComponents: .date) | |
} | |
Stepper(mutable.value.formatted(.number), value: $mutable.value, in: 0...10) | |
Button("Save") { save() } | |
.buttonStyle(.borderedProminent) | |
} | |
.padding() | |
} | |
private func save() { | |
var updated = self.obj.update(with: mutable) | |
// Remove date if unspecified | |
if didSpecifyDate == false { | |
self.obj.date = nil | |
updated.date = nil | |
} | |
didSave?(updated) | |
dismiss() | |
} | |
} | |
struct BindingTest_Previews: PreviewProvider { | |
static var previews: some View { | |
BindingTest() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment