比如我们要实现两个简单的页面:一个列表页展示学生列表,一个学生编辑页面。点击列表的每一条跳转到对应的编辑页面,编辑并保存后列表数据更新。
构建model
import SwiftUI
import Combine
struct Person : Identifiable {
let id : UUID
var name : String
var age : Int
}
final class PersonStore : ObservableObject {
@Published var persons : [Person] = [
.init(id: .init(), name: "张三", age: 1),
.init(id: .init(), name: "李四", age: 2),
.init(id: .init(), name: "王五", age: 3)
]
}
构建列表及编辑页View层(不具有跳转功能,纯展示)
struct PersonsView : View {
@ObservedObject var store: PersonStore
var body: some View {
NavigationView {
List(store.persons) { person in
VStack(alignment: .leading) {
Text(person.name)
.font(.headline)
Text("Age: \(person.age)")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}.navigationBarTitle(Text("Persons"))
}
}
接下来编写编辑页面:
struct EditingView: View {
@Environment(\.presentationMode) var presentation
@Binding var person: Person
var body: some View {
Form {
Section(header: Text("Personal information")) {
TextField("type something...", text: $person.name)
Stepper(value: $person.age) {
Text("Age: \(person.age)")
}
}
Section {
Button("Save") {
self.presentation.wrappedValue.dismiss()
}
}
}.navigationBarTitle(Text(person.name))
}
}
在编辑页面声明了person属性,并使用@Binding修饰。@Binding修饰符允许传递一个引用类型到值类型。这样在EditView的子View里我们可以使用$来传递person的引用而不是值的copy。
自定义数据结构IndexedCollection
我们知道列表List对应的数据需实现RandomAccessCollection,PersonStore中的数组已经满足要求。但是在跳转时需要获取当前选中的数据的索引,从而从store中获取并传递引用,因此我们需要自定义RandomAccessCollection类型,其中Element类型为原组(Index,Element)
struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection {
typealias Index = Base.Index
typealias Element = (index: Index, element: Base.Element)
let base: Base
var startIndex: Index { base.startIndex }
var endIndex: Index { base.endIndex }
func index(after i: Index) -> Index {
base.index(after: i)
}
func index(before i: Index) -> Index {
base.index(before: i)
}
func index(_ i: Index, offsetBy distance: Int) -> Index {
base.index(i, offsetBy: distance)
}
subscript(position: Index) -> Element {
(index: position, element: base[position])
}
}
extension RandomAccessCollection {
func indexed() -> IndexedCollection<Self> {
IndexedCollection(base: self)
}
}
重构列表View
重构列表View:增加跳转编辑页逻辑和数据传递逻辑。
1.使用NavigationLink实现跳转。
2.传递选中条目对应的Person引用,通过前一步的准备,现在很好实现了。
struct PersonsView : View {
@ObservedObject var store: PersonStore
var body: some View {
NavigationView {
List {
ForEach(store.persons.indexed(), id: \.1.id) { index, person in
NavigationLink(destination: EditingView(person: self.$store.persons[index])) {
VStack(alignment: .leading) {
Text(person.name)
.font(.headline)
Text("Age: \(person.age)")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle(Text("Persons"))
}
}
}