I’ve created a view that acts as a container view, that has multiple properties whose values I wish to pass down to its sibling views.
The below example container view only stores padding values and isn’t a practical use-case, but the idea is transferrable to one that is (that I’ll explain at the end).
public struct Container<Content: View>: View {
let content: Content
// MARK: Padding
@State var bottom: CGFloat = .zero
@State var leading: CGFloat = .zero
@State var trailing: CGFloat = .zero
@State var top: CGFloat = .zero
// MARK: -
public init(padding: Padding...,
@ViewBuilder content: () -> Content) {
for pad in padding {
switch pad {
case .bottom(let padding): bottom = padding
case .leading(let padding): leading = padding
case .trailing(let padding): trailing = padding
case .top(let padding): top = padding
case .horizontal(let padding): leading = padding
trailing = padding
case .vertical(let padding): bottom = padding
top = padding
}
}
self.content = content()
}
// MARK: -
public var body: some View {
content
.padding(.bottom, bottom)
.padding(.leading, leading)
.padding(.top, top)
.padding(.trailing, trailing)
}
}
extension Container {
public enum Padding {
case vertical(_:CGFloat)
case horizontal(_:CGFloat)
case leading(_:CGFloat)
case trailing(_:CGFloat)
case top(_:CGFloat)
case bottom(_:CGFloat)
}
}
In practise, this looks like:
Container(padding:.vertical(50)) {
/// children have no idea about Container’s set property values
}
And it works fine. But what I’d like to achieve is:
Container(padding:.vertical(50)) { padding in
/// children have access to padding.bottom,top,leading,trailing
}
My reference in approach is the GeometryReader that provides access to a GeometryProxy for its sibling views to depend on.
As mentioned at the top, although I’m unlikely to find benefit in passing down the padding values, the immediate use-case I’m focused on is the creation of an Expandable container view that stores properties such as expanded: Bool
and methods like toggleExpanded()
attached to its tap gesture handler that changes the state of expanded
.
UPDATE:
pawello2222’s suggestion is technically satisfactory for the example code I shared. However because of the static nature of the values, it turns out it’s not immediately transferrable to my actual use case as it involves a value that can be changed based on user interaction. Lesson learnt! Here is the Expandable view:
public struct Expandable<Content: View>: View {
let content: Content
@State public var expanded: Bool = false
public init(@ViewBuilder content: ((Binding<Bool>)) -> Content) {
self.content = content(($expanded))
}
// MARK: -
public var body: some View {
content
.onTapGesture(perform: toggle)
}
// MARK: - Methods
func toggle() {
expanded.toggle()
}
}
In other words, I need the children views to have access to a @Binding expanded
property, so it’s not only able to read the current expanded
value but also has the possibility of toggling its state back up to its parent.
The problem with the above code is it tells me:
Variable 'self.content' used before being initialized