You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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冻结。

解决方案

  1. 移除未使用的dismiss声明:如果ServiceListView本身不需要调用dismiss()方法,直接删除该环境变量声明即可(这也是你测试后有效的方案)。
  2. 延迟获取dismiss变量:若父视图确实需要使用dismiss,可通过闭包方式延迟获取,避免直接声明引发循环:
    // 替代直接声明@Environment(\.dismiss)
    private func getDismiss() -> DismissAction {
        @Environment(\.dismiss) var dismiss
        return dismiss
    }
    
  3. 升级导航组件:切换到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 06:24:58