【发布时间】:2021-05-13 08:32:50
【问题描述】:
我有一个登录屏幕,我使用与状态变量绑定的隐藏NavigationLink 以编程方式推送到下一个屏幕。推送有效,但它似乎推送两次并弹出一次,正如您在此屏幕录制中看到的那样:
这是我的视图层次结构:
App
NavigationView
LaunchView
LoginView
HomeView
App:
var body: some Scene {
WindowGroup {
NavigationView {
LaunchView()
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.environmentObject(cache)
}
}
LaunchView:
struct LaunchView: View {
@EnvironmentObject var cache: API.Cache
@State private var shouldPush = API.shared.accessToken == nil
func getUser() {
[API call to get user, if already logged in](completion: { user in
if let user = user {
// in our example, this is NOT called
// thus `cache.user.hasData` remains `false`
cache.user = user
}
shouldPush = true
}
}
private var destinationView: AnyView {
cache.user.hasData
? AnyView(HomeView())
: AnyView(LoginView())
}
var body: some View {
if API.shared.accessToken != nil {
getUser()
}
return VStack {
ActivityIndicator(style: .medium)
NavigationLink(destination: destinationView, isActive: self.$shouldPush) {
EmptyView()
}.hidden()
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
这是我的LoginView 的清理版:
struct LoginView: View {
@EnvironmentObject var cache: API.Cache
@State private var shouldPushToHome = false
func login() {
[API call to get user](completion: { user in
self.cache.user = user
self.shouldPushToHome = true
})
}
var body: some View {
VStack {
ScrollView(showsIndicators: false) {
// labels
// textfields
// ...
PrimaryButton(title: "Anmelden", action: login)
NavigationLink(destination: HomeView(), isActive: self.$shouldPushToHome) {
EmptyView()
}.hidden()
}
// label
// signup button
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
LoginView 本身是 NavigationView 的子代。
HomeView 真的很简单:
struct HomeView: View {
@EnvironmentObject var cache: API.Cache
var body: some View {
let user = cache.user
return Text("Hello, \(user.contactFirstname ?? "") \(user.contactLastname ?? "")!")
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
这里出了什么问题?
更新:
当我将App 中的LaunchView() 直接替换为LoginView() 时,我意识到问题确实不会发生。不知道这有什么关系...
更新 2:
正如 Tushar 在下面指出的那样,将 destination: destinationView 替换为 destination: LoginView() 可以解决问题 - 但显然缺少所需的功能。
所以我玩弄了它,现在明白发生了什么:
-
LaunchView被渲染 -
LaunchView发现还没有用户数据,所以推送到LoginView - 在用户交互时,
LoginView推送到HomeView - 此时,
LaunchView中的NavigationLink再次被调用(我知道为什么,但是断点显示了这一点),因为那里是 现在用户数据,它呈现HomeView而不是LoginView。
这就是为什么我们只看到一个推送动画,而 LoginView 变成了 HomeView 没有任何推送动画,b/c 它被替换,本质上。
所以现在的目标是阻止LaunchView 的NavigationLink 重新渲染其目标视图。
【问题讨论】:
-
您可以尝试在您的 LaunchView 中将目的地硬编码到 LoginView() 以查看会发生什么?
-
我猜问题在于 API 调用,以及它们如何更新您的模型状态,以及正在观察您的模型以执行刷新的视图。你需要更仔细地检查。如果我以最简单的方式测试您的代码,而不依赖于“缓存类”和“访问令牌”值,并且只使用单个 @State 属性来切换状态,那么一切正常。
-
据我了解您的“缓存”是@EnvironmentObject 属性,因此一旦该模型的任何属性值发生更改,所有标有 EnvironmentObject 的视图都将重新呈现主体。
-
哦对了,所以每次
cache.user更改时都会调用getUser(),对吧?然后结果触发通过NavigationLink重复推送,从而导致问题。 -
是的,你没看错。
标签: swift swiftui navigation