Вот как работает поток:
1. RecepesListView.swift
Код: Выделить всё
NavigationLink(recipe.mainInformation.name, destination: RecipeDetailView(recipe: binding(for: recipe)))
< /code>
2. RecemedetailView.swift
Код: Выделить всё
struct RecipeDetailView: View {
@Binding var recipe: Recipe
@State private var isPresented = false
var body: some View {
VStack {
// ... recipe details shown here
}
.navigationTitle(recipe.mainInformation.name)
.toolbar {
ToolbarItem {
Button("Edit") {
isPresented = true
}
}
}
.sheet(isPresented: $isPresented) {
NavigationStack {
ModifyRecipeView(recipe: $recipe)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Save") {
isPresented = false
}
}
}
.navigationTitle("Edit Recipe")
}
}
}
}
< /code>
3. ModifyRecipeView.swift
Код: Выделить всё
struct ModifyRecipeView: View {
@Binding var recipe: Recipe
@State private var selection: Selection = .main
enum Selection: String, CaseIterable {
case main = "Main"
case ingredients = "Ingredients"
case directions = "Directions"
}
var body: some View {
VStack {
Picker("Options", selection: $selection) {
ForEach(Selection.allCases, id: \.self) { selection in
Text(selection.rawValue)
}
}
.pickerStyle(SegmentedPickerStyle())
switch selection {
case .main:
ModifyMainInformationView(mainInformation: $recipe.mainInformation)
case .ingredients:
ModifyComponentsView(components: $recipe.ingredients)
case .directions:
ModifyComponentsView(components: $recipe.directions)
}
Spacer()
}
}
}
< /code>
4. ModifyComponentSview.swift
Код: Выделить всё
struct ModifyComponentsView: View
where DestinationView.Component == Component {
@Binding var components: [Component]
@State var newComponent: Component = Component()
var body: some View {
VStack {
let destination = DestinationView(component: $newComponent) { component in
components.append(component)
DispatchQueue.main.async {
newComponent = Component()
}
}
if components.isEmpty {
NavigationLink(destination: destination) {
Text("Add the first \(Component().instanceName)")
}
} else {
HStack {
Text(Component().pluralName().capitalized)
.font(.title).fontWeight(.bold)
.padding()
Spacer()
EditButton().padding()
}
List {
ForEach(components.indices, id: \.self) { i in
NavigationLink(destination: modifyComponent(i)) {
Text(components[i].description)
}
}
.onDelete { components.remove(atOffsets: $0) }
.onMove { indices, newOffset in
components.move(fromOffsets: indices, toOffset: newOffset)
}
}
Spacer()
NavigationLink(destination: destination) {
Text("Add New \(Component().pluralName())")
}
}
}
}
func modifyComponent(_ i: Int) -> some View {
DestinationView(component: $components[i]) { _ in return }
.navigationTitle("Edit \(components[i].instanceName)")
}
}
< /code>
5. Моя модель настройка (Recipe
Чтобы исключить проблемы с идентичностью модели или структурой, вот определение моих основных типов:
Код: Выделить всё
struct Recipe: Identifiable {
var id = UUID()
var mainInformation: MainInformation
var ingredients: [Ingredient]
var directions: [Direction]
var isFavorite: Bool = false
init(mainInformation: MainInformation, ingredients: [Ingredient], directions: [Direction]) {
self.mainInformation = mainInformation
self.ingredients = ingredients
self.directions = directions
}
init() {
self.init(
mainInformation: MainInformation(name: "", description: "", author: "", category: .breakfast),
ingredients: [],
directions: []
)
}
var isValid: Bool {
mainInformation.isValid && !ingredients.isEmpty && !directions.isEmpty
}
}
struct MainInformation {
var name: String
var description: String
var author: String
var category: Category
var isValid: Bool {
!name.isEmpty && !description.isEmpty && !author.isEmpty
}
}
struct Ingredient: RecipeComponent {
var name: String
var quantity: Double
var unit: Unit
var instanceName: String = "ingredient"
var description: String {
let formattedQuantity = String(format: "%g", quantity)
switch unit {
case .none:
let formattedName = quantity == 1 ? name : name + "s"
return "\(formattedQuantity) \(formattedName)"
default:
return quantity == 1 ? "1 \(unit.rawValue) \(name)" : "\(formattedQuantity) \(unit.plural) \(name)"
}
}
init(name: String, quantity: Double, unit: Unit) {
self.name = name
self.quantity = quantity
self.unit = unit
}
init() {
self.init(name: "", quantity: 0, unit: .none)
}
}
struct Direction: RecipeComponent {
var description: String
var isOptional: Bool
var instanceName: String = "direction"
init(description: String, isOptional: Bool) {
self.description = description
self.isOptional = isOptional
}
init() {
self.init(description: "", isOptional: false)
}
}
enum Category: String, CaseIterable {
case breakfast = "Breakfast"
case lunch = "Lunch"
case dinner = "Dinner"
case dessert = "Dessert"
case snack = "Snack"
}
enum Unit: String, CaseIterable {
case teaspoon = "Teaspoon"
case tablespoon = "Tablespoon"
case ounce = "Ounce"
case cup = "Cup"
case pound = "Pound"
case gram = "Gram"
case milliliter = "Milliliter"
case liter = "Liter"
case piece = "Piece"
case kilogram = "Kilogram"
case none = "No Unit"
var plural: String {
return self.rawValue + "s"
}
}
As soon as I delete or reorder an item in the ingredients or directions sections within the ModifyRecipeView sheet, the .sheet unexpectedly closes, and SwiftUI navigates Back к основному рецепту списка .
Есть нет явного .dismiss () вызовы в операциях удаления или перемещения.
Код: Выделить всё
Button("Save") {
createAction(ingredient)
dismiss()
}
Код: Выделить всё
RecipeDetailView
[*]
[*]
Подробнее здесь: https://stackoverflow.com/questions/796 ... bound-data