I have the following SwiftUI
View
"PaletteBar":
struct PaletteBar: View {
final class State: ObservableObject {
@Published var selectedColor: Color? = .black
@Published var isEraserSelected = false
}
private enum Constants {
static let colors: [Color] = [.black, .white, .blue, .green, .yellow, .red]
static let eraserColor: Color = .black.opacity(0.7)
}
// MARK: - Properties
@ObservedObject var state = State()
// MARK: - Body
var body: some View {
HStack {
PaletteItem(color: Constants.eraserColor, isSelected: Binding(
get: { state.isEraserSelected },
set: {
state.isEraserSelected = $0
state.selectedColor = $0 ? nil : state.selectedColor
}
))
.overlay { Image(systemName: "pencil.slash") }
.foregroundColor(.white)
.frame(maxWidth: .infinity)
ForEach(Constants.colors, id: \.self) { color in
PaletteItem(color: color, isSelected: Binding(
get: { state.selectedColor == color },
set: {
state.selectedColor = $0 ? color : nil
state.isEraserSelected = $0 ? false : state.isEraserSelected
}
))
.frame(maxWidth: .infinity)
}
}
}
}
This stores the state in an @ObservedObject
. I would like to pass this state up the hierarchy e.g PaletteBar
is a sub view of AnnotateImageToolbar
:
struct AnnotateImageToolbar: View {
final class State: ObservableObject {
@Published var selectedColor: Color? = .black
@Published var isEraserSelected = false
}
private enum Constants {
static let spacing: CGFloat = 12.0
static let backgroundColor: Color = .black.opacity(0.7)
static let colorsInsets = EdgeInsets(top: 12.0, leading: 16.0, bottom: 0.0, trailing: 16.0)
static let sliderInsets = EdgeInsets(top: 0.0, leading: 16.0, bottom: 12.0, trailing: 16.0)
static let radius: CGFloat = 8.0
}
// MARK: - State
@StateObject var state = State()
var body: some View {
VStack(spacing: Constants.spacing) {
PaletteBar()
.padding(Constants.colorsInsets)
ZStack {
SizeBar()
.foregroundColor(.gray)
.frame(maxHeight: 12.0)
BrushSizeSlider()
}
.padding(Constants.sliderInsets)
}
.background(Constants.backgroundColor)
.clipShape(RoundedCorners(radius: Constants.radius, corners: [.topLeft, .topRight]))
}
}
Here I create an identical state object @StateObject var state = State()
. Is there a way to bind the values from the state in PaletteBar
to the state in AnnotateImageToolbar
? Or Should I be creating a single state object and injecting it into each view?
CodePudding user response:
Inject it as environment object, like
@StateObject var state = State()
var body: some View {
VStack(spacing: Constants.spacing) {
// ... other code
}
.background(...)
.clipShape(...)
.environmentObject(state) // << here !!
and use it inside children:
struct PaletteBar: View {
// MARK: - Properties
@EnvironmentObject var state: State // << injected by parent !!
// ...
}