SwiftUI中使用@Environment(\.dismiss)引发App冻结问题求助
问题分析与解决方案:导航后点击按钮导致CPU满载、App冻结
问题现象
App编译运行正常,但从ServiceListView导航到EmployeeDetailView后,点击任意按钮都会触发CPU占用率拉满,App直接冻结。移除ServiceListView中的@Environment(\.dismiss) private var dismiss声明后,App恢复正常。尝试过相关方案但无效,苹果论坛存在同类@Environment(.dismiss)无限循环Bug记录。
核心原因
这是SwiftUI环境变量@Environment(\.dismiss)的已知循环引用Bug:当父视图(ServiceListView)持有dismiss环境变量,同时通过NavigationLink导航到的子视图(EmployeeDetailView)也持有dismiss变量时,会触发视图树的无限重渲染循环,最终导致CPU耗尽、App冻结。
解决方案
- 移除未使用的
dismiss声明:如果ServiceListView本身不需要调用dismiss()方法,直接删除该环境变量声明即可(这也是你测试后有效的方案)。 - 延迟获取
dismiss变量:若父视图确实需要使用dismiss,可通过闭包方式延迟获取,避免直接声明引发循环:// 替代直接声明@Environment(\.dismiss) private func getDismiss() -> DismissAction { @Environment(\.dismiss) var dismiss return dismiss } - 升级导航组件:切换到iOS 16+的
NavigationStack,其导航生命周期管理更稳定,可规避这类环境变量循环问题。
相关代码片段
ServiceListView 代码
struct ServicesListView: AppTNowView { var services: [Service] = [] var isAvailableNowView = false @State private var moveToEmployeeDetail = false @State private var isCallingFirstTime = true @StateObject var viewModel = ServicesListViewModel() // 移除此处的@Environment(\.dismiss)即可解决问题 // @Environment(\.dismiss) private var dismiss var body: some View { ZStack { loadView LoaderView(isLoading: $viewModel.showLoader) }.alert(isPresented: $viewModel.showError, content: { Alert(title: Text(viewModel.errorMessage)) }).onAppear{ if isCallingFirstTime { viewModel.selectedServiceID = services.first?.id ?? 0 viewModel.selectedService = services.first fetchEmployee() isCallingFirstTime.toggle() } }.onChange(of: viewModel.selectedServiceID, perform: { newValue in debugPrint("Appt: selected index \(newValue)") viewModel.selectedServiceID = newValue viewModel.selectedService = services.first(where: {$0.id == newValue}) fetchEmployee() }) } }
ServiceListView 中的导航代码
ScrollView { if viewModel.employeeList.isEmpty { EmptyListView(imageName: ImageName.Home.noEmployee.rawValue, descriptionText: "暂无该服务对应的员工") .padding(.top, 50) } else { VStack { ForEach(viewModel.employeeList) { employee in NavigationLink { EmployeeDetailView(employee: employee) .hideNavigationBar } label: { EmployeeInfoCardView(employee: employee) } } } } }
EmployeeDetailView 代码
struct EmployeeDetailView: AppTNowView { @State private var isCalanderPresented = false @StateObject var viewModel = EmployeeDetailViewModle() @FocusState private var isMessageFocused: Bool @Environment(\.dismiss) private var dismiss var employee: Employee var body: some View { ZStack { loadView LoaderView(isLoading: $viewModel.showLoader) } .alert(isPresented: $viewModel.showError, content: { Alert(title: Text(viewModel.errorMessage)) }) .sheet(isPresented: $isCalanderPresented) { CalanderView(date: $viewModel.selecteDate, isCalanderPresented: $isCalanderPresented) .presentationDetents([.medium, .large]) .onDisappear { } } } }
内容的提问来源于stack exchange,提问作者Qazi Ammar
相关产品推荐
相关产品推荐

