如何在SwiftUI中更新单个子视图?

如何在SwiftUI中更新单个子视图?,swiftui,Swiftui,我正在尝试在子视图和父视图之间建立SwiftUI连接。通过单击任何子视图,我只想重新绘制已点击的视图,而不是整个父视图 下面的当前实现不允许您在单击视图时重新绘制视图,因为它具有派生值 我尝试了不同的场景,将BindableObject协议添加到CustomColor,但没有成功 class CustomColor:可识别{ 设id=UUID() 颜色:颜色 初始化(颜色:颜色){ self.color=颜色 } func更改(颜色:颜色){ self.color=颜色 } } 类ColorSt

我正在尝试在子视图和父视图之间建立SwiftUI连接。通过单击任何子视图,我只想重新绘制已点击的视图,而不是整个父视图

下面的当前实现不允许您在单击视图时重新绘制视图,因为它具有派生值

我尝试了不同的场景,将
BindableObject
协议添加到
CustomColor
,但没有成功

class CustomColor:可识别{
设id=UUID()
颜色:颜色
初始化(颜色:颜色){
self.color=颜色
}
func更改(颜色:颜色){
self.color=颜色
}
}
类ColorStore:BindableObject{
变量颜色:[CustomColor]=[]{
迪塞特{
发送(自我)
}
}
var didChange=PassthroughSubject()
init(){
self.colors=Array.init(重复:CustomColor(颜色:。红色),计数:10)
}
}
结构ContentView:View{
@EnvironmentObject变量colorStore:colorStore
var body:一些观点{
导航视图{
名单{
ForEach(colorStore.colors){color in
颜色形状(颜色:颜色)
}
}.navigationBarTitle(文本(“颜色”))
}
}
}
结构颜色形状:视图{
颜色:自定义颜色
var body:一些观点{
按钮(操作:
{self.color.change(改为:。蓝色)}
,标签:{
ShapeView(形状:圆形(),样式:color.color)
})
}
}

目前不可能更新特定的子视图,我认为这是不可能的。
正如您在更改@State属性或Bindable对象时在会话中所说的,所有更改都会通过视图层次结构向下流动,SwiftUI框架会比较所有视图,并再次渲染更改的内容。

我想我已经找到了解决方案。 第一个问题是,我通过重复相同的元素而不是添加独立的元素来初始化颜色数组

更重要的是,
CustomColor
本身应该具有
BindableObject
一致性,而不是模型(我们不更改颜色数组,我们更改每种颜色)。 最后,我们不需要将对象包装在ForEach元素中(这样我们就失去了可重用性),而是将它们放在List元素中

使用此实现,将仅重新绘制已更改的视图,而不是整个集合

代码如下:

class CustomColor:BindableObject,可识别{
var didChange=PassthroughSubject()
设id=UUID()
颜色:颜色{
迪塞特{
self.didChange.send(self)
}
}
初始化(颜色:颜色){
self.color=颜色
}
功能更改(toColor颜色:color){
self.color=颜色
}
}
类色彩库{
变量颜色:[CustomColor]=[]
init(){
(0…10).forEach{in colors.append(CustomColor(color:.red))}
}
}
结构ContentView:View{
let colorStore:colorStore
var body:一些观点{
导航视图{
列表(colorStore.colors){color in
颜色形状(颜色:颜色)
}.navigationBarTitle(文本(“颜色”))
}
}
}
结构颜色形状:视图{
@ObjectBinding变量颜色:CustomColor
var body:一些观点{
按钮(操作:{self.color.change(toColor:.blue)},标签:{
ShapeView(形状:圆形(),样式:color.color)
})
}
}

我可以提供三种不同的版本。 它们都会切换单个按钮,并保持整个模型-
ColorStore
var同步。允许添加和删除颜色数组中的元素。还要注意的是,我们可以不使用
可识别的
一致性来列出数组元素

第1版。最接近问题的是:所有模型都是

class CustomColor: ObservableObject, Identifiable {

    var didChange = PassthroughSubject<CustomColor, Never>()

    let id = UUID()
    var color: Color {
        didSet {
            objectWillChange.send()
        }
    }

    init(color: Color) {
        self.color = color
    }

    func change(to color: Color) {
        self.color = color
    }

}

class ColorStore: ObservableObject {

    var didChange = PassthroughSubject<ColorStore, Never>()

    var colors: [CustomColor] = [] {
        didSet {
            objectWillChange.send()
        }
    }

    init() {
        (0...10).forEach { _ in colors.append(CustomColor(color: .red)) }
    }

}


struct ContentView: View {

    @ObservedObject var colorStore: ColorStore = ColorStore()

    var body: some View {
        NavigationView {
            List(colorStore.colors) { c in
                    ColorShape(color: c)
            }
// will work without `Identifiable`
//            List(colorStore.colors.indices, id: \.self) { c in
//                    ColorShape(color: self.colorStore.colors[c])
//            }
            .navigationBarTitle(Text("Colors"))
            .navigationBarItems(leading:
                Button(action: { self.colorStore.colors.append(CustomColor(color: .green)) }) {
                    Text("Add")
                }, trailing:
                Button(action: {
                    self.colorStore.colors.removeLast()
                    print(self.colorStore.colors)
                }, label: { Text("Remove") }))
        }
    }

}

struct ColorShape: View {

    @ObservedObject var color: CustomColor

    var body: some View {
        Button(action:
            { self.color.change(to: .blue)
                print(self.color)
        }
            , label: {
                Circle().fill(color.color)
        })
    }

}
版本3。主模型
ColorStore
及其子类型
CustomColor
被重写为结构。无需手动符合
可观察对象

struct CustomColor /* : Identifiable */ {

    // let id = UUID()
    var color: Color

    init(color: Color) {
        self.color = color
    }

    mutating func change(to color: Color) {
        self.color = color
    }

}

struct ColorStore {
    // If `CustomColor` is a `struct` i.e. value type, we can populate array with independent values, not with the same reference by using `repeating:` init.
    var colors: [CustomColor] = Array(repeating: CustomColor(color: .red), count: 10)

}


struct ContentView: View {

    @State var colorStore: ColorStore = ColorStore()

    var body: some View {
        NavigationView {
            List{
                ForEach(colorStore.colors.indices, id: \.self) { i in
                    return ColorShape(color: self.$colorStore.colors[i])
                }
            }
            .navigationBarTitle(Text("Colors"))
            .navigationBarItems(leading:
                Button(action: { self.colorStore.colors.append(CustomColor(color: .green)) }) {
                    Text("Add")
            }, trailing:
                // Removing causes index out of bound error (bug?)
                Button(action: {
                    self.colorStore.colors.removeLast()
                    print(self.colorStore.colors)}) {
                    Text("Remove") })
        }
    }
}

struct ColorShape: View {

    @Binding var color: CustomColor

    var body: some View {
        Button(action: {
            self.color.change(to: .blue)
            print(self.color)
        }) {
            Circle().fill(color.color)
        }
    }
}

我可以问你为什么不想重画所有的东西吗?为了表现?事实上,我想知道这有可能吗?我知道他们说你不必担心这样的事情,但还是要附上你的用户界面的图片,以便有一个清晰的界面understanding@jsbeginnerNodeJS,我把它附在问题上。那是不可能的。但是你可以为每一行做状态。我补充了答案。看来它是我想要的。请看一看。但是在这种情况下,您将向颜色数组添加新项目,ContentView将不会更新是的,它将不会更新。您必须将
BindableObject
一致性返回到ColorStore,并在NavigationBar中放置一个带有操作的按钮。根据我的要点。
struct CustomColor /* : Identifiable */ {

    // let id = UUID()
    var color: Color

    init(color: Color) {
        self.color = color
    }

    mutating func change(to color: Color) {
        self.color = color
    }

}

struct ColorStore {
    // If `CustomColor` is a `struct` i.e. value type, we can populate array with independent values, not with the same reference by using `repeating:` init.
    var colors: [CustomColor] = Array(repeating: CustomColor(color: .red), count: 10)

}


struct ContentView: View {

    @State var colorStore: ColorStore = ColorStore()

    var body: some View {
        NavigationView {
            List{
                ForEach(colorStore.colors.indices, id: \.self) { i in
                    return ColorShape(color: self.$colorStore.colors[i])
                }
            }
            .navigationBarTitle(Text("Colors"))
            .navigationBarItems(leading:
                Button(action: { self.colorStore.colors.append(CustomColor(color: .green)) }) {
                    Text("Add")
            }, trailing:
                // Removing causes index out of bound error (bug?)
                Button(action: {
                    self.colorStore.colors.removeLast()
                    print(self.colorStore.colors)}) {
                    Text("Remove") })
        }
    }
}

struct ColorShape: View {

    @Binding var color: CustomColor

    var body: some View {
        Button(action: {
            self.color.change(to: .blue)
            print(self.color)
        }) {
            Circle().fill(color.color)
        }
    }
}