SwiftUI, использующий объект среды в нескольких представлениях, вызывает проблемы с навигацией
Не знаю, злоупотребляю ли я идеей объекта среды, но испытываю проблему при использовании объекта среды, который публикует отложенное асинхронное значение. Одно представление переходит к следующему, но затем «корень» впоследствии обновляется и в результате вызывает «эхо», или даже если это решается, проблема навигации. Проблема становится еще более очевидной при использовании переходов между навигацией. Есть ли правильный способ использования, чтобы этого избежать? Или, может быть, какое-то другое решение?
Любое руководство будет оценено по достоинству.
Прилагаю сжатый образец, чтобы проиллюстрировать проблему.
Xcode 12.4 iOS 14.1
final class SetColor: ObservableObject {
@Published var asyncVal: Bool = false
func flipIt() {
DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute: {self.asyncVal.toggle()})
}
}
struct HomeView: View {
@StateObject var setCol: SetColor = SetColor()
@State private var navActive: Bool = false
var body: some View {
NavigationView {
ZStack {
Color(setCol.asyncVal ? .blue : .purple)
Button(action: {
setCol.flipIt()
navActive.toggle()
}, label: {
Text("Change and Move")
})
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: NavChild1().environmentObject(setCol),isActive: $navActive, label: { Text("GoTo 1 >") })
}
}
}
}
}
}
struct NavChild1: View {
@EnvironmentObject var setCol: SetColor
@State private var navActive: Bool = false
var body: some View {
ZStack {
Color(setCol.asyncVal ? .yellow : .orange)
Button(action: {
setCol.flipIt()
navActive.toggle()
}, label: {
Text("Change and Move")
})
.navigationTitle("Nav 1")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: NavChild2().environmentObject(setCol),isActive: $navActive, label: { Text("GoTo 2 >") })
}
}
}
}
}
struct NavChild2: View {
@EnvironmentObject var setCol: SetColor
@State private var navActive: Bool = false
var body: some View {
ZStack {
Color(setCol.asyncVal ? .yellow : .orange)
Button(action: {
setCol.flipIt()
navActive.toggle()
}, label: {
Text("Change and Move")
})
.navigationTitle("Nav 2")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: NavChild3().environmentObject(setCol),isActive: $navActive, label: { Text("GoTo 3 >") })
}
}
}
}
}
struct NavChild3: View {
@EnvironmentObject var setCol: SetColor
@State private var navActive: Bool = false
var body: some View {
ZStack {
Color(setCol.asyncVal ? .yellow : .orange)
Button(action: {
setCol.flipIt()
navActive.toggle()
}, label: {
Text("Change and Move")
})
.navigationTitle("Nav 3")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: NavChild3().environmentObject(setCol), isActive: .constant(false), label: { Text("Go Home") })
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
1 ответ
Вам не нужен крайний срок, установленный вами для действия GCD. Это вызывает действия навигации, даже если пользователь не нажимает на навигацию (я тестировал код в проекте). Это связано с тем, что вы накапливаете задания в очереди GCD, и когда они выполняются, вы находитесь в другом представлении (из-за задержки 0,5). Между прочим, они вызывают навигацию, так как флип наблюдается, и, следовательно, любой, кто слушает, выполнит навигацию.
В любом случае, вы хотите изменить команду отправки на это:
DispatchQueue.main.async { self.asyncVal.toggle() }
И навигация будет более плавной, без дополнительных команд навигации после этого.