自定义数据类型UI绑定

dasonxie / 2023-09-05 / 原文

场景:

在收货地址列表页面A中,点击一个地址进详情页面B,然后修改地址保存关闭页面B,收货地址A需要同步UI更新修改的信息。

机制:

1、在SwiftUI中,使用@Binding只能绑定基本数据类型,不能处理自定义数据类型。
2、@StateObject和@ObservedObject的监听,在目前的测试中体现的是向下传递。即页面A创建的模型数组,传递到页面B用@ObservedObject监听,然后在页面A延时修改了数据某个模型中的属性,这时页面A的UI不会更新,页面B的UI会更新。

点击查看问题代码
import SwiftUI

class FruitModelData: ObservableObject {
    @Published var fruits: [FruitModel] = [
        FruitModel(name: "Apple", count: 5),
        FruitModel(name: "Banana", count: 3)
    ]
}

struct ContentView1: View {
    @StateObject var fruitModelData: FruitModelData
    
    var body: some View {
        NavigationView {
            List {
                ForEach(fruitModelData.fruits) { fruit in
                    NavigationLink(destination: FruitDetail(fruit: fruit)) {
                        HStack {
                            Text(fruit.name)
                            Text("\(fruit.count)")
                        }
                    }
                }
            }
        }
    }
}

// FruitDetail.swift
struct FruitDetail: View {
    @ObservedObject var fruit: FruitModel
    
    var body: some View {
        VStack {
            Text("Name: \(fruit.name)")
            Text("Count: \(fruit.count)")
            
            Button("Increment Count") {
                fruit.count += 1
            }
        }
    }
}

struct DSTest_Previews: PreviewProvider {
    static var previews: some View {
        ContentView1(fruitModelData: FruitModelData())
    }
}
3、也可能学艺不精,还没学到这种场景适合的技术。

解决方案:

@Binding

通过上面的两个现象可以得出,要在页面B修改属性的时候,同时向上反馈更新页面A的UI,那就只能用到@Binding,所以做法是把模型拆分成基本数据类型

点击查看@Binding代码
import SwiftUI

class FruitModelData: ObservableObject {
    @Published var fruits: [FruitModel] = [
        FruitModel(name: "Apple", count: 5),
        FruitModel(name: "Banana", count: 3)
    ]
}

struct ContentView1: View {
    @StateObject var fruitModelData: FruitModelData
    
    var body: some View {
        NavigationView {
            List {
                ForEach(fruitModelData.fruits.indices, id: \.self) { i in
                    let model = fruitModelData.fruits[i]
                    NavigationLink(destination: FruitDetail(fruit: model, count: $fruitModelData.fruits[i].count)) {
                        HStack {
                            Text(model.name)
                            Text("\(model.count)")
                        }
                    }
                }
            }
        }
    }
}

// FruitDetail.swift
struct FruitDetail: View {
    @ObservedObject var fruit: FruitModel
    @Binding var count: Int
    
    var body: some View {
        VStack {
            Text("Name: \(fruit.name)")
            Text("Count: \(fruit.count)")
            
            Button("Increment Count") {
                count += 1
            }
        }
    }
}

struct DSTest_Previews: PreviewProvider {
    static var previews: some View {
        ContentView1(fruitModelData: FruitModelData())
    }
}

combine

既然我们修改单个模型中的属性无法触发更新,那我们就直接通知更新整个数组,这样使用到的UI都会刷新

点击查看objectWillChange代码

import SwiftUI

class FruitModelData: ObservableObject {
    @Published var fruits: [FruitModel] = [
        FruitModel(name: "Apple", count: 5),
        FruitModel(name: "Banana", count: 3)
    ]
}

struct ContentView1: View {
    @StateObject var fruitModelData: FruitModelData
    
    var body: some View {
        NavigationView {
            List {
                ForEach(fruitModelData.fruits) { fruit in
                    NavigationLink(destination: FruitDetail(fruit: fruit, fruitModelData: fruitModelData)) {
                        HStack {
                            Text(fruit.name)
                            Text("\(fruit.count)")
                        }
                    }
                }
            }
        }
    }
}

// FruitDetail.swift
struct FruitDetail: View {
    @ObservedObject var fruit: FruitModel
    var fruitModelData: FruitModelData //这里把整个数组的对象传递过来
    
    var body: some View {
        VStack {
            Text("Name: \(fruit.name)")
            Text("Count: \(fruit.count)")
            
            Button("Increment Count") {
                fruit.count += 1
                //既然修改其中一个模型触发不了,那我就通知你整个模型,我已经更新了数据,但是需要集成ObservableObject
                fruitModelData.objectWillChange.send()
            }
        }
    }
}

struct DSTest_Previews: PreviewProvider {
    static var previews: some View {
        ContentView1(fruitModelData: FruitModelData())
    }
}