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),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),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()
    }
}

解决方法

您不需要在 GCD 行动中设置的最后期限。即使用户没有按下导航,它也会导致导航操作(我已经在项目中测试了代码)。 这是因为您在 GCD 队列中累积作业,并且当它们执行时,您处于另一个视图中(由于 0.5 停顿)。顺便说一句,它们会导致导航,因为翻转是观察到的,因此无论谁听,都会执行导航。

无论如何,您要做的是将调度命令更改为:

DispatchQueue.main.async { self.asyncVal.toggle() }

而且导航会更流畅,之后无需执行额外的导航命令。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...