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

SwiftUI问题:使用@Published触发.sheet时保存修改值致崩溃

问题分析与解决方案

崩溃原因

  1. 值类型更新异常:Transport是值类型,直接修改editedTransport?.name不会触发@Published的状态更新,导致视图与ViewModel状态不一致,关闭sheet时引发绑定冲突。
  2. Sheet生命周期冲突:在sheet的onDismiss回调中设置editedTransport = nil,与保存操作中的数据修改存在竞态,导致视图销毁时访问无效的绑定实例。

修复后的代码

struct Transport: Identifiable {
    var id = UUID().uuidString
    var name: String
}

class TransportViewModel: ObservableObject {
    @Published var transports = [Transport(name: "Airplane"), Transport(name: "Car"), Transport(name: "Ship")]
    @Published var editedTransport: Transport?
    
    // 提供编辑name的绑定,确保值类型修改时触发状态更新
    var editedName: Binding<String>? {
        guard let transport = editedTransport else { return nil }
        return Binding(
            get: { transport.name },
            set: { newName in
                editedTransport = Transport(id: transport.id, name: newName)
            }
        )
    }
    
    func saveTransport() {
        guard let unwrappedTransport = editedTransport else { return }
        if let index = transports.firstIndex(where: { $0.id == unwrappedTransport.id }) {
            transports[index] = unwrappedTransport
        }
        editedTransport = nil
    }
    
    func cancelEditing() {
        editedTransport = nil
    }
}

struct TransportListView: View {
    @StateObject var transportViewModel = TransportViewModel()
    
    var body: some View {
        List {
            ForEach(transportViewModel.transports) { transport in
                Text(transport.name)
                    .onTapGesture {
                        transportViewModel.editedTransport = transport
                    }
            }
        }
        .sheet(item: $transportViewModel.editedTransport) {
            // 移除此处的nil赋值,交由按钮逻辑处理
        } content: { _ in
            TransportEditSheet()
        }
        .environmentObject(transportViewModel)
    }
}

struct TransportEditSheet: View {
    @EnvironmentObject var transportViewModel: TransportViewModel
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        List {
            // 使用ViewModel提供的安全绑定
            if let editedName = transportViewModel.editedName {
                TextField("输入名称", text: editedName)
            }
            
            Button("取消") {
                transportViewModel.cancelEditing()
                dismiss()
            }
            .foregroundColor(.red)
            
            Button("保存") {
                transportViewModel.saveTransport()
                dismiss()
            }
            .foregroundColor(.green)
        }
    }
}

关键修改点

  • 在ViewModel中新增editedName绑定属性,修改名称时重新创建Transport实例,确保@Published能正确触发状态更新。
  • 将editedTransport的重置逻辑移到cancelEditing和saveTransport方法中,避免sheet的onDismiss与按钮操作的状态冲突。
  • 移除TransportEditSheet中不安全的Binding.map用法,直接使用ViewModel提供的绑定,保证视图状态稳定。

内容的提问来源于stack exchange,提问作者Nizami

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:40:29