SwiftUI中使用.navigationDestination(isPresented:)导航卡顿问题求助
问题情况
使用.navigationDestination(isPresented:)跳转时,只要在目标视图NewComposeView的sendMessage闭包里调用viewModel相关方法(哪怕只是简单打印),导航就会出现卡顿。但注释掉闭包内的viewModel.sendMessage后,跳转恢复正常。
相关代码如下:
主视图代码
@State private var navigateToChatView = false var body: some View { MyView() .navigationTitle("Users") .searchable(text: $viewModel.searchText) .navigationDestination(isPresented: $navigateToChatView) { if let selectedUser = selectedUsers.first, let name = selectedUser.name { NewComposeView(sendMessage: { message in viewModel.sendMessage(message: message) // 注释此行导航正常 }) .navigationTitle(name) } } }
NewComposeView代码
import SwiftUI struct NewComposeView: View { @State private var message: String = "" var sendMessage: (String) -> Void var body: some View { HStack { ZStack { RoundedRectangle(cornerRadius: 18) .stroke() HStack { TextField("Write a message", text: $message) Spacer() Button { sendMessage(message) message = "" } label: { Image(systemName: "paperplane.fill") .resizable() .frame(width: 20, height: 20) .font(.title) } } .padding(EdgeInsets(top: 3, leading: 20, bottom: 3, trailing: 20)) } .frame(height: 40) } .foregroundColor(Color(.white)) } }
问题根源
本质是闭包捕获了主视图中的viewModel,导致SwiftUI在导航跳转时需要处理主视图与目标视图间额外的状态绑定关系——哪怕只是在闭包里执行打印操作,只要捕获了viewModel,这种绑定关联就会存在,拖慢导航的渲染速度。
解决办法
办法1:改用 .navigationDestination(item:) 触发导航
将选中的用户作为可选对象,通过item触发导航,避免闭包直接捕获viewModel,逻辑更清晰的同时也能解决卡顿:
@State private var selectedUser: User? // 替换为你的实际用户类型 var body: some View { MyView() .navigationTitle("Users") .searchable(text: $viewModel.searchText) .navigationDestination(item: $selectedUser) { user in NewComposeView(sendMessage: { message in viewModel.sendMessage(message: message) }) .navigationTitle(user.name ?? "") } }
办法2:提前封装sendMessage逻辑,减少闭包捕获
在主视图中将发送消息的逻辑单独封装为方法,再将方法传递给NewComposeView,避免在导航闭包内直接捕获viewModel:
// 主视图内新增方法 private func sendMessageToUser(_ message: String) { viewModel.sendMessage(message: message) } var body: some View { MyView() .navigationTitle("Users") .searchable(text: $viewModel.searchText) .navigationDestination(isPresented: $navigateToChatView) { if let selectedUser = selectedUsers.first, let name = selectedUser.name { NewComposeView(sendMessage: sendMessageToUser) .navigationTitle(name) } } }
办法3:给ViewModel的方法添加@MainActor
虽然排除了异步调用的影响,但如果sendMessage内存在隐性的非主线操作,也可能导致UI卡顿。给该方法添加@MainActor确保其在主线执行:
// 在你的ViewModel中修改 @MainActor func sendMessage(message: String) { // 原有业务逻辑 }
验证方式
修改后测试导航跳转,还可以在主视图和NewComposeView的body中添加print(self),观察是否存在不必要的重复渲染,确认问题是否解决。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

