SwiftUI:异步任务(async/await)完成后跳转新视图的实现方法
解决方案
因为你的视图已经嵌入父级NavigationView,可以用两种方式实现异步获取数据后的跳转:
方法1:利用selection绑定的隐藏NavigationLink(兼容iOS 15及以下)
你已经定义了selection变量,只需要在FollowersView的视图层级中添加一个隐藏的NavigationLink,绑定这个selection并指定跳转目标视图即可。
修改后的代码如下:
import SwiftUI struct FollowersView: View { @State var followers: [Follower] @State var user: User? @State private var selection: String? = nil @StateObject private var viewModel = GitHubUsersViewModel() let columns = [ GridItem(.flexible(minimum: 150, maximum: 175)), GridItem(.flexible(minimum: 150, maximum: 175)), ] var body: some View { ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(followers, id: \.self) { follower in FollowerCardView(follower: follower) .onTapGesture { Task { do { self.user = try await self.viewModel.manager.getUser(for: follower.login) self.selection = NavigationTags.userFound } catch { // 可添加错误处理逻辑,比如提示用户获取失败 print("获取用户信息失败:\(error)") } } } } } .padding(.horizontal) } .frame(maxHeight: .infinity) // 添加隐藏的NavigationLink .background( NavigationLink( destination: UserDetailView(user: user!), // 替换为你的目标视图 tag: NavigationTags.userFound, selection: $selection ) { EmptyView() } .hidden() ) } } // 假设你的NavigationTags定义如下 enum NavigationTags: String { case userFound = "userFound" } // 示例目标视图 struct UserDetailView: View { let user: User var body: some View { Text(user.login) // 根据User模型展示对应内容 .navigationTitle(user.login) } }
注意事项:
- 必须在
Task的do-catch块中处理错误,避免user!强制解包导致崩溃 NavigationLink放在.background里并用.hidden()隐藏,不会影响原有布局
方法2:使用navigationDestination(iOS 16+推荐)
iOS 16及以后,Apple推出了更简洁的navigationDestination修饰符,无需隐藏的NavigationLink,直接绑定跳转条件和目标视图即可。
修改后的代码:
import SwiftUI struct FollowersView: View { @State var followers: [Follower] @State var user: User? @StateObject private var viewModel = GitHubUsersViewModel() let columns = [ GridItem(.flexible(minimum: 150, maximum: 175)), GridItem(.flexible(minimum: 150, maximum: 175)), ] var body: some View { ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(followers, id: \.self) { follower in FollowerCardView(follower: follower) .onTapGesture { Task { do { self.user = try await self.viewModel.manager.getUser(for: follower.login) // 无需设置selection,user非nil时自动触发跳转 } catch { print("获取用户信息失败:\(error)") } } } } } .padding(.horizontal) } .frame(maxHeight: .infinity) // 添加navigationDestination .navigationDestination(item: $user) { user in UserDetailView(user: user) } } } // 示例目标视图 struct UserDetailView: View { let user: User var body: some View { Text(user.login) .navigationTitle(user.login) } } // 注意:User模型需要遵循Identifiable协议,或在navigationDestination中指定id路径 extension User: Identifiable { var id: String { login } // 假设login是用户唯一标识 }
注意事项:
- 目标对象
user需要遵循Identifiable协议,或者在navigationDestination中通过id: \.login指定唯一标识路径 - 当
user从nil变为非nil时自动触发跳转;若需重置user,可在目标视图的onDisappear回调中处理
内容的提问来源于stack exchange,提问作者JFortYork
相关产品推荐
相关产品推荐

