I am trying to establish a SwiftUI connection between the child view and the parent view. By clicking on any child view, I want to redraw only the view that has been tapped, not the entire parent view.
The current implementation below does not allow you to redraw the view when you click on it, as it has a derived value.
I tried different scenarios by adding the BindableObject
protocol to CustomColor
, but without success.
class CustomColor: Identifiable {
let id = UUID()
var color: Color
init(color: Color) {
self.color = color
}
func change(to color: Color) {
self.color = color
}
}
class ColorStore: BindableObject {
var colors: [CustomColor] = [] {
didSet {
didChange.send(self)
}
}
var didChange = PassthroughSubject<ColorStore, Never>()
init() {
self.colors = Array.init(repeating: CustomColor(color: .red), count: 10)
}
}
struct ContentView: View {
@EnvironmentObject var colorStore: ColorStore
var body: some View {
NavigationView {
List {
ForEach(colorStore.colors) { color in
ColorShape(color: color)
}
}.navigationBarTitle(Text("Colors"))
}
}
}
struct ColorShape: View {
var color: CustomColor
var body: some View {
Button(action:
{ self.color.change(to: .blue) }
, label: {
ShapeView(shape: Circle(), style: color.color)
})
}
}
I think I've found a solution. The first problem was that I initialized array of colors by repeating the same element instead of adding independent ones.
What is more CustomColor
itself should have BindableObject
conformance, not the model (we don't change the array of colors, we change each color).
Lastly, we don't need to wrap objects in ForEach element (we loose reusability that way), and instead we put them in List element.
With this implementation, only the view that has been changed will be redrawn, not the entire collection.
Here is the code:
class CustomColor: BindableObject, Identifiable {
var didChange = PassthroughSubject<CustomColor, Never>()
let id = UUID()
var color: Color {
didSet {
self.didChange.send(self)
}
}
init(color: Color) {
self.color = color
}
func change(toColor color: Color) {
self.color = color
}
}
class ColorStore {
var colors: [CustomColor] = []
init() {
(0...10).forEach { _ in colors.append(CustomColor(color: .red)) }
}
}
struct ContentView: View {
let colorStore: ColorStore
var body: some View {
NavigationView {
List(colorStore.colors) { color in
ColorShape(color: color)
}.navigationBarTitle(Text("Colors"))
}
}
}
struct ColorShape: View {
@ObjectBinding var color: CustomColor
var body: some View {
Button(action: { self.color.change(toColor: .blue) }, label: {
ShapeView(shape: Circle(), style: color.color)
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With