ObservedObject не работает с пунктом назначения NavigationLink, если есть обновления для родительского объекта

У меня есть два экрана, мастер и деталь, деталь имеет ObservedObject, у которого есть его состояние. Я также хочу скрыть панель навигации на мастере и показать ее на деталях. Для этого у меня есть скрытый статус панели навигации как свойство @State в главном представлении и я отправляю его обратно в подробное представление как переменная привязки.

Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я обновляю эту переменную на подробном экране, ObservedObject перестает работать.

Вот пример кода, который воспроизводит проблему:

struct ContentView: View {
    @State var navigationBarHidden = true

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView(navigationBarHidden: $navigationBarHidden)) {
                    Text("Go Forward")
                }
            }
            .navigationBarTitle("", displayMode: .inline)
            .navigationBarHidden(navigationBarHidden)
            .onAppear { self.navigationBarHidden = true }
        }
    }
}

class DetailViewModel: ObservableObject {
    @Published var text = "Didn't work"
}

struct DetailView: View {
    @Binding var navigationBarHidden: Bool
    @ObservedObject var viewModel = DetailViewModel()

    var body: some View {
        VStack {
            Text(viewModel.text)
        }.onAppear {
            self.navigationBarHidden = false
            self.viewModel.text = "Worked"
        }
    }
}

Если я оставлю все как есть, текст не изменится на «Работает». Если я удалю строку self.navigationBarHidden = false, ObservedObject будет работать правильно, а текст обновится.

Как добиться ожидаемого поведения, обновить панель навигации, сохранив работающий наблюдаемый объект?


person Daniel Amarante    schedule 06.03.2020    source источник


Ответы (1)


Причина в том, что

NavigationLink(destination: DetailView(navigationBarHidden: $navigationBarHidden)) {
    Text("Go Forward")
}

создать новый DetailView и так далее новый DetailViewModel при активации

пытаться

import SwiftUI

struct ContentView: View {
    @State var navigationBarHidden = true
    @ObservedObject var viewModel = DetailViewModel()
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView(navigationBarHidden: $navigationBarHidden).environmentObject(viewModel)) {
                    Text("Go Forward")
                }
            }
            .navigationBarTitle("", displayMode: .inline)
            .navigationBarHidden(navigationBarHidden)
            .onAppear { self.navigationBarHidden = true }
        }
    }
}

class DetailViewModel: ObservableObject {
    @Published var text = "Didn't work"
}

struct DetailView: View {
    @Binding var navigationBarHidden: Bool
    @EnvironmentObject var viewModel: DetailViewModel

    var body: some View {
        VStack {
            Text(viewModel.text)
        }.onAppear {
            self.navigationBarHidden = false
            self.viewModel.text = "Worked"
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Теперь вы делитесь моделью с DetailView, и она работает, как ожидалось (написано)

Если я удалю строку self.navigationBarHidden = false, ObservedObject будет работать правильно, а текст обновится.

Если вы удалите эту строку, DetailView не будет воссоздан (в представлении ничего не изменилось). Состояние не является частью состояния просмотра, это ссылочный тип, поэтому SwiftUI не видит никаких изменений до тех пор, пока не изменятся некоторые значения, которые ими обернуты.

person user3441734    schedule 06.03.2020
comment
Вы добавили environmentObject в NavigationLink. Есть ли видеоролик WWDC или какая-либо документация, в которой говорится, что вам нужно это сделать? Я не спорю, это ответ, но я застрял в этой проблеме, и решение найти было нелегко. - person Des; 05.11.2020
comment
@Des см. Документацию для public func environmentObject ‹B› (_ bindable: B) - ›some View, где B: ObservableObject - person user3441734; 06.11.2020
comment
@Des, конечно, есть и другие способы добиться того же - person user3441734; 06.11.2020