SwiftUI NavigationLink появляется автоматически, что неожиданно

У меня возникли проблемы с NavigationLink на iPad с разделенным экраном (альбомная ориентация). Вот пример:

Вот код для воспроизведения проблемы:

import SwiftUI

final class MyEnvironmentObject: ObservableObject {
    @Published var isOn: Bool = false
}

struct ContentView: View {
    @EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        NavigationView {
            NavigationLink("Go to FirstDestinationView", destination: FirstDestinationView(isOn: $object.isOn))
        }
    }
}

struct FirstDestinationView: View {
    @Binding var isOn: Bool

    var body: some View {
        NavigationLink("Go to SecondDestinationView", destination: SecondDestinationView(isOn: $isOn))
    }
}

struct SecondDestinationView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle(isOn: $isOn) {
            Text("Toggle")
        }
    }
}

// Somewhere in SceneDelegate
ContentView().environmentObject(MyEnvironmentObject())

Кто-нибудь знает, как это исправить? Простое решение - отключить режим разделения экрана, но для меня это невозможно.

4 ответа

Хорошо, вот результаты моего расследования (протестировано с Xcode 11.2), а ниже - код, который работает.

В iPad NavigationView попал в стиль Master/Details, поэтому ContentViewесли начальная ссылка активна и привязки процесса обновляются из environmentObject, поэтому обновите, что приведет к активации ссылки просмотра сведений через ту же привязку, что приведет к повреждению стека навигации. (Примечание: это отсутствует в iPhone из-за стиля стека, который отключает корневое представление).

Так что, вероятно, это обходной путь, но он работает - идея состоит не в том, чтобы передавать привязку из представления в представление, а в использовании environmentObject непосредственно в окончательном представлении. Возможно, так будет не всегда, но в любом случае в таких сценариях необходимо избегать обновления корневого представления, поэтому оно не должно иметь такой же привязки в теле. Что-то подобное.

final class MyEnvironmentObject: ObservableObject {
    @Published var selection: Int? = nil
    @Published var isOn: Bool = false
}

struct ContentView: View {
    @EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        NavigationView {
            List {
                NavigationLink("Go to FirstDestinationView", destination: FirstDestinationView())
            }
        }
    }
}

struct FirstDestinationView: View {

    var body: some View {
        List {
            NavigationLink("Go to SecondDestinationView", destination: SecondDestinationView())
        }
    }
}

struct SecondDestinationView: View {
@EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        VStack {
            Toggle(isOn: $object.isOn) {
                Text("Toggle")
            }
        }
    }
}

Когда что-то внутри EnvironmentObject изменения, он снова отобразит весь вид, включая NavigationLink. Это основная причина автоматического возврата.

Мои исследования по этому поводу:

  • ОК на iOS 15 (похоже, Apple исправила)
  • Все еще не работает на iOS 14
  • Причина, по которой «Эта ошибка исчезла, когда я отказался от @EnvironmentObject и вместо этого выбрал @ObservedObject». @Jon Vogel упомянул, что ObservedObject - это локальное состояние, на которое не будут влиять другие представления, в то время как EnvironmentObject является глобальным состоянием и может изменяться из любых других удаленных представлений.

Вам нужно использовать isDetailLink(_:) , чтобы исправить это, например

      struct FirstDestinationView: View {
    @Binding var isOn: Bool

    var body: some View {
        NavigationLink("Go to SecondDestinationView", destination: SecondDestinationView(isOn: $isOn))
        .isDetailLink(false)
    }
}

Эта ошибка исчезла, когда я уронил @EnvironmentObject и пошел с @ObservedObject вместо.

Другие вопросы по тегам