【iOS】SwiftUI 中ForEach的使用记录

69 阅读1分钟

需求:有一个列表表单,编辑的时候上级页面带进来默认值,在编辑也可以修改。在SwiftUI中如何用ForEach优雅的实现。

直接看代码

import SwiftUI

class MyViewModel: ObservableObject {
    @Published var stockItemList: [itemModel]! = [itemModel(name: "张三"), itemModel(name: "李四"), itemModel(name: "王麻子")]
}

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

// 写法1
struct ListView: View {
    @StateObject var vm: MyViewModel
    var body: some View {
        ForEach($vm.stockItemList, id: \.id) { $item in
            ItemView(vm: $item)
        }
    }
}

// 写法2
// struct ListView: View {
//    @StateObject var vm: MyViewModel
//
//    var body: some View {
//        ForEach(vm.stockItemList.indices, id: \.self) { index in
//            ItemView(vm: $vm.stockItemList[index])
//        }
//    }
// }


struct ItemView: View {

    @Binding var vm: itemModel

    var body: some View {

        TextField("请输入", text: $vm.name)
    }
}


#Preview {
    let vm = MyViewModel()
    return ListView(vm: vm)
}

这两种写法其实都可以完成我们的需求。MyViewModel是大的数据模型,里面可以包含其他的业务字段,绑定字段,还有提交函数,或者数据转换等等。

总结:

  • 写法1
ForEach($vm.stockItemList, id: \.id) { 
    $item in ItemView(vm: $item)
}

使用了$vm.stockItemList,即BindingstockItemList数组的投影。这里,ForEach通过$符号直接绑定了数组中的每个itemModel实例,允许ItemView中的TextField直接修改每个itemModel实例的name属性。使用id: .id确保了即使数组中的元素顺序改变,视图也能正确地识别并重用。

  • 写法2
ForEach(vm.stockItemList.indices, id: \.self) { index in 
    ItemView(vm: $vm.stockItemList[index])
}

这种写法通过遍历stockItemList数组的索引来访问每个元素。它使用vm.stockItemList[index]来获取每个元素,并通过$符号创建到该元素的Binding。这种方法同样允许ItemView中的TextField修改每个itemModel实例的name属性。使用id: .self意味着每个索引本身作为唯一标识符,这在大多数情况下是有效的,但如果数组元素顺序发生变化,可能会导致不必要的视图重建。

综上所诉,尽量使用第一种写法