保存和关闭视图后,SwiftUI 自动导航到详细信息视图

时间:2021-04-11 13:09:21

标签: ios swift swiftui

所以我正在 SwiftUI 中编写一个待办事项列表应用程序以掌握它的窍门,但我遇到了一个问题。

在我的第一个视图(项目列表)中,我有一个带有“添加”按钮的工具栏,该按钮使用 NavigationLink 导航到详细信息视图。在详细视图中,我还有一个用作保存按钮的工具栏按钮,它会关闭此视图并将项目添加到两个视图使用的视图模型中保存的项目列表中。

问题是,如果我在点击保存按钮时保存项目,它将首先导航回第一个视图,然后再次自动导航到第二个视图。如果我改为使用内置的后退按钮,则不会发生此问题,但显然我想保存该项目,并且仅在按下保存时才保存。这也只有在我在关闭视图之前将项目添加到视图模型中的项目列表时才会发生,如果我在按下完成时仅关闭视图而不保存项目,则不会发生此错误。

这不是使用 SwiftUI 保存和关闭视图的标准方法,还是有某种其他更好的模式?无论如何,我需要解决这个问题。

第一次观看:

struct TodoListView: View {
    @EnvironmentObject var viewModel: TodoListViewModel

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.listOfTodos) { todoItem in
                    ItemCellView(todoItem: todoItem)
                }
            }
            .navigationTitle("Things to do")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink(
                        destination: AddEditTodoView(todoItem: TodoListInfo.TodoItem())
                    ) {
                        Text("Add item") // The navigation bug happens when using this button
                    }
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct ItemCellView: View {
    var todoItem: TodoListInfo.TodoItem

    var body: some View {
        HStack {
            NavigationLink(destination: AddEditTodoView(todoItem: todoItem)) {
                Text(todoItem.title) // The navigation bug doesn't happen when editing an existing item
            }
        }
        .padding()
    }
}

第二种观点:

struct AddEditTodoView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    @EnvironmentObject var viewModel: TodoListViewModel
    @State var todoItem: TodoListInfo.TodoItem

    var body: some View {
        Form {
            Section(header: Text("Title")) {
                TextField("Title", text: $todoItem.title)
            }
        }
        .navigationTitle(Text("Edit task"))
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Done") {
                    viewModel.upsert(item: todoItem) // No bug if I comment out this line
                    presentationMode.wrappedValue.dismiss()
                }
                .disabled(todoItem.title == "")
            }
        }
    }
}

查看模型:

class TodoListViewModel: ObservableObject {
    @Published private var todoListInfo: TodoListInfo
    private var autoSaveCancellable: AnyCancellable? // even without the autoSaveCancellable part, the bug happens

    init(testData: Bool = false) {
        todoListInfo = TodoListInfo(testData: testData)
        autoSaveCancellable = $todoListInfo.sink {
            TodoListInfo.persistTodoList($0)
        }
    }

    var listOfTodos: [TodoListInfo.TodoItem] {
        todoListInfo.todos
    }

    func upsert(item: TodoListInfo.TodoItem) {
        if let itemIndex = todoListInfo.todos.firstIndex(where: { $0.id == item.id }) {
            todoListInfo.todos[itemIndex] = item
        } else {
            todoListInfo.todos.append(item) // This gets called when adding an item
        }
    }
}

1 个答案:

答案 0 :(得分:0)

对此的解决方案是使导航基于绑定状态。

NavigationLink(
    destination: ExchangeItemSelectedView(exchange: observer),
    tag: exchange.id,
    selection: $exchangeSelection
) {
  Text("Tap Me")
}

然后,而不是使用 @State 来存储 exchangeSelection 使用 @SceneStorage 这将允许您从应用程序内的任何位置访问绑定,在创建新项目的代码中,它应该调度async 将选择值更新为新的项目 ID。