I want to access the parent of my custom view to know whether my view parent is a HStack or VStack, like Divider() could do it. Currently I am hard coding value, but my goal is that I could be get access the parent information inside my custom view to select the right return view.
struct ContentView: View {
var body: some View {
HStack { Divider() }
VStack { Divider() }
HStack { CustomView(parentIsHStack: true) }
VStack { CustomView(parentIsHStack: false) }
}
}
struct CustomView: View {
let parentIsHStack: Bool
var body: some View {
if parentIsHStack {
Text("Parent is HStack")
}
else {
Text("Parent is VStack")
}
}
}
CodePudding user response:
I could solve the issue with replacing Apple Stack's, without broking apple api.
struct ContentView: View {
var body: some View {
HStack { Divider() }
VStack { Divider() }
HStack { CustomView() }
VStack { CustomView() }
ZStack { CustomView() }
}
}
struct CustomView: View {
@Environment(\.stack) var stack
var body: some View {
Text("Parent is " stack.rawValue)
}
}
struct HStack<Content>: View where Content: View {
let alignment: VerticalAlignment
let spacing: CGFloat?
let content: () -> Content
init(alignment: VerticalAlignment = VerticalAlignment.center, spacing: CGFloat? = nil, @ViewBuilder content: @escaping () -> Content) {
self.alignment = alignment
self.spacing = spacing
self.content = content
}
var body: some View {
return SwiftUI.HStack(alignment: alignment, spacing: spacing, content: { content() })
.environment(\.stack, Stack.hStack)
}
}
struct VStack<Content>: View where Content: View {
let alignment: HorizontalAlignment
let spacing: CGFloat?
let content: () -> Content
init(alignment: HorizontalAlignment = HorizontalAlignment.center, spacing: CGFloat? = nil, @ViewBuilder content: @escaping () -> Content) {
self.alignment = alignment
self.spacing = spacing
self.content = content
}
var body: some View {
return SwiftUI.VStack(alignment: alignment, spacing: spacing, content: { content() })
.environment(\.stack, Stack.vStack)
}
}
struct ZStack<Content>: View where Content: View {
let alignment: Alignment
let content: () -> Content
init(alignment: Alignment = Alignment.center, @ViewBuilder content: @escaping () -> Content) {
self.alignment = alignment
self.content = content
}
var body: some View {
return SwiftUI.ZStack(alignment: alignment, content: { content() })
.environment(\.stack, Stack.zStack)
}
}
private struct StackKey: EnvironmentKey { static let defaultValue: Stack = Stack.unknown }
extension EnvironmentValues {
var stack: Stack {
get { return self[StackKey.self] }
set(newValue) { self[StackKey.self] = newValue }
}
}
enum Stack: String { case vStack, hStack, zStack, unknown }
CodePudding user response:
I would be tempted to say this is not a hidden environment variable. I don't see a relevant one when I dump all the environment variables (there are a lot though).
Instead, I believe it's how _VariadicView.Tree
works. This contains a root and its content. I'll take how HStack
works for example. Inspecting the SwiftUI interface, you can see the following snippet of code:
@frozen public struct HStack<Content> : SwiftUI.View where Content : SwiftUI.View {
@usableFromInline
internal var _tree: SwiftUI._VariadicView.Tree<SwiftUI._HStackLayout, Content>
@inlinable public init(alignment: SwiftUI.VerticalAlignment = .center, spacing: CoreGraphics.CGFloat? = nil, @SwiftUI.ViewBuilder content: () -> Content) {
_tree = .init(
root: _HStackLayout(alignment: alignment, spacing: spacing), content: content())
}
public static func _makeView(view: SwiftUI._GraphValue<SwiftUI.HStack<Content>>, inputs: SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs
public typealias Body = Swift.Never
}
Notice that the Body
is of type Never
(therefore a primitive view type). The _tree
stores information about the layout, and the type HStackLayout
obviously shows this is a HStack
.
SwiftUI will be using _makeView(view:inputs:)
internally to create the view, which I'm assuming gives special treatment to certain views.
You'll need to make custom versions of HStack
/VStack
and pass an environment variable down to know which kind your subview is in.