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

取消表单更新时恢复初始值,如何优化SwiftUI现有实现?

优雅实现SwiftUI中Book对象的Cancel恢复功能

问题核心

当前代码需要逐个保存每个字段的初始值,字段数量增多时会变得繁琐。尝试直接把整个Book对象存为@State后赋值会报错,因为@Bindable修饰的book是SwiftData管理的不可变引用,无法直接替换整个实例。

解决方案

通过深拷贝初始Book对象,用@State保存这个副本,Cancel时将副本的属性批量赋值给原对象,无需逐个处理字段。

步骤1:给Book添加拷贝初始化方法

SwiftData的@Model类默认没有深拷贝功能,手动实现拷贝初始化器:

@Model
class Book {
    var title: String
    var author: String
    
    init(title: String, author: String) {
        self.title = title
        self.author = author
    }
    
    // 拷贝初始化器,复制所有字段
    init(from book: Book) {
        self.title = book.title
        self.author = book.author
    }
}

步骤2:修改BookDetail视图,统一保存初始对象

不再需要逐个存储字段,直接用@State保存整个初始对象的拷贝:

struct BookDetail: View {
    @Bindable var book: Book
    @State private var initialBook: Book? // 保存初始状态的拷贝
    
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        Form {
            TextField("Title", text: $book.title, axis: .vertical)
            TextField("Author", text: $book.author, axis: .vertical)
        }
        .toolbar {
            ToolbarItem(placement: .confirmationAction) {
                Button("Save") {
                    dismiss()
                }
            }
            ToolbarItem(placement: .cancellationAction) {
                Button("Cancel") {
                    restoreBook()
                    dismiss()
                }
            }
        }
        .onAppear {
            // 视图加载时创建初始拷贝
            if initialBook == nil {
                initialBook = Book(from: book)
            }
        }
    }
    
    func restoreBook() {
        guard let initialBook = initialBook else { return }
        // 批量赋值所有属性,后续新增字段只需修改拷贝初始化器
        book.title = initialBook.title
        book.author = initialBook.author
    }
}

进阶优化:用反射自动处理多字段

如果字段数量极多,不想手动写每个属性的赋值,可以用Swift的Mirror反射机制自动遍历赋值:

func restoreBook() {
    guard let initialBook = initialBook else { return }
    let initialMirror = Mirror(reflecting: initialBook)
    let bookMirror = Mirror(reflecting: book)
    
    // 遍历初始对象属性,自动赋值给原对象
    for (label, value) in initialMirror.children {
        guard let label = label,
              let bookChild = bookMirror.children.first(where: { $0.label == label }),
              let keyPath = bookChild.value as? AnyMutableProperty else { continue }
        keyPath.set(value)
    }
}

// 辅助协议,支持反射修改属性
protocol AnyMutableProperty {
    func set(_ value: Any)
}

extension WritableKeyPath: AnyMutableProperty where Root: NSObject {
    func set(_ value: Any) {
        guard let typedValue = value as? Value else { return }
        (root as NSObject)[keyPath: self] = typedValue
    }
}

注意:反射机制存在轻微性能开销,适合字段极多的场景,普通场景手动拷贝更简洁可靠。

关键说明

为什么不能直接替换整个book对象?因为@Bindable var book: Book中的book是SwiftData管理的对象引用,SwiftData需要追踪对象的属性变化而非实例替换,因此只能修改对象的属性值,不能直接替换整个实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:02:49