简体   繁体   中英

SwiftUI NavigationView pops by itself

it's very strange issue as I cannot reproduce in isolated code, but I hope that someone may think about the reason. I have a view, let's say ContentView that has its ContentViewModel that is ObservedObject, and then there's another View ContentView2. And we have NavigationView in ContentView that wraps navigation link to ContentView2. And it's a bit weird, but when we do some changes that affect ContentViewModel, then NavigationView pops ContentView2 so that we end up in initial ContentView, but we didn't do anything like dismissing ContentView2 or tapping back button. I have a similar code to the one used in my project, but please note that in this code everything works fine:

func qrealm() -> Realm {
    return try! Realm(configuration: .init( inMemoryIdentifier: "yw"))
}

class SomeRObj: Object {
    @objc dynamic var name: String = ""
    
    convenience init(name: String) {
        self.init()
        self.name = name
    }
    
    static var instance: SomeRObj {
        return qrealm().objects(SomeRObj.self).first!
    }
}
struct SomeRObjWrapped: Hashable {
    var obj: SomeRObj
    var prop: Int
}

class ContentViewModel: ObservableObject {
    @Published var someRObj: [SomeRObjWrapped] = []
    var any: Any?
    init() {
        let token = qrealm().objects(SomeRObj.self).observe { changes in
            switch changes {
            case let .initial(data), let .update(data, deletions: _, insertions: _, modifications: _):
                let someObjs = data.map { SomeRObjWrapped(obj: $0, prop: Int.random(in: 1..<50)) }
                self.someRObj = Array(someObjs)
            default: break
            }
        }
        self.any = token
    }
}

struct ContentView: View {
    @ObservedObject var model: ContentViewModel = ContentViewModel()
    
    var body: some View {
        NavigationView {
        VStack {
            ForEach(model.someRObj, id: \.self) { obj in
                Heh(obj: obj.obj, pr: obj.prop)
            }
            NavigationLink(destination: ContentView2()) {
                Text("Link")
            }
        }
        }
    }
}

struct Heh: View {
    var obj: SomeRObj
    var pr: Int
    var body: some View {
        Text("\(obj.name) \(pr)")
    }
}

struct ContentView2: View {
    var body: some View {
        Button(action: { try! qrealm().write {
            let elem = qrealm().objects(SomeRObj.self).randomElement()
            elem?.name = "jotaro kujo"
            }
            
        }, label: { Text("Toggle") })
    }
}

You can replace \.self with \.id :

ForEach(model.someRObj, id: \.id) { obj in
    Heh(obj: obj.obj, pr: obj.prop)
}

Then every object will be identified by id and the ForEach loop will only refresh when the id is changed.

Thanks to pawello2222, I found the real reason behind it. I had a NavigationLink inside my List, so that each time there was a change NavigationLink is redrawn and it's state refreshed. I hope that it will be helpfull to someone, and the solution as pawello2222 wrote before is to identify view by id parameter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM