Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Navigation popping back when modifying list binding property in a pushed view

When I update a binding property from an array in a pushed view 2+ layers down, the navigation pops back instantly after a change to the property.

Xcode 13.3 beta, iOS 15.

I created a simple demo and code is below.

Shopping Lists List Edit List section Edit
ShoppingListsView ShoppingListEditView ShoppingListEditsectionView

Updating the list title (one view deep) is fine, navigation stack stays same, and changes are published if I return. But when adjusting a section title (two deep) the navigation pops back as soon as I make a single change to the property.

I have a feeling I'm missing basic fundamentals here, and I have a feeling it must be related to the lists id? but I'm struggling to figure it out or work around it.

GIF

Example

Code:

Models:

struct ShoppingList {
    let id: String = UUID().uuidString
    var title: String
    var sections: [ShoppingListSection]
}

struct ShoppingListSection {
    let id: String = UUID().uuidString
    var title: String
}

View Model:

final class ShoppingListsViewModel: ObservableObject {
    @Published var shoppingLists: [ShoppingList] = [
        .init(
            title: "Shopping List 01",
            sections: [
                .init(title: "Fresh food")
            ]
        )
    ]
}

Content View:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ShoppingListsView()
        }
    }
}

ShoppingListsView

struct ShoppingListsView: View {
    @StateObject private var viewModel = ShoppingListsViewModel()

    var body: some View {
        List($viewModel.shoppingLists, id: \.id) { $shoppingList in
            NavigationLink(destination: ShoppingListEditView(shoppingList: $shoppingList)) {
                Text(shoppingList.title)
            }
        }
        .navigationBarTitle("Shopping Lists")
    }
}

ShoppingListEditView

struct ShoppingListEditView: View {
    @Binding var shoppingList: ShoppingList

    var body: some View {
        Form {
            Section(header: Text("Title")) {
                TextField("Title", text: $shoppingList.title)
            }
            Section(header: Text("Sections")) {
                List($shoppingList.sections, id: \.id) { $section in
                    NavigationLink(destination: ShoppingListSectionEditView(section: $section)) {
                        Text(section.title)
                    }
                }
            }
        }
        .navigationBarTitle("Edit list")
    }
}

ShoppingListSectionEditView

struct ShoppingListSectionEditView: View {
    @Binding var section: ShoppingListSection

    var body: some View {
        Form {
            Section(header: Text("Title")) {
                TextField("title", text: $section.title)
            }
        }
        .navigationBarTitle("Edit section")
    }
}

like image 559
lacking-cypher Avatar asked Mar 02 '23 11:03

lacking-cypher


2 Answers

try this, works for me:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ShoppingListsView()
        }.navigationViewStyle(.stack)  // <--- here
    }
}
like image 190
workingdog support Ukraine Avatar answered May 16 '23 10:05

workingdog support Ukraine


Try to make you object confirm to Identifiable and return value which unique and stable, for your case is ShoppingList.

Detail view seems will pop when object id changed.

like image 34
Nobel Avatar answered May 16 '23 09:05

Nobel