【问题标题】:How can I access the parent/Environment of a custom view?如何访问自定义视图的父级/环境?
【发布时间】:2021-12-13 10:40:58
【问题描述】:

我想访问我的自定义视图的父视图以了解我的视图父视图是 HStack 还是 VStack,就像 Divider() 可以做到的那样。目前我是硬编码值,但我的目标是我可以访问自定义视图中的父信息以选择正确的返回视图。

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")
        }
    }
}

【问题讨论】:

  • 看看我在stackoverflow.com/questions/70293599/… 的评论——这里是同样的情况。
  • @Asperi:谢谢,基本上你说的,有一些环境是苹果秘密使用的,我们无法访问它们,对吧?
  • 这是一种可能的变体,当然它们可以像我们在自己的实现中那样直接与子视图通信。但绝对是“父母”起源(对我们隐藏),反之亦然。

标签: swift swiftui


【解决方案1】:

我可以通过更换 Apple Stack 来解决问题,而无需中断 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 }

【讨论】:

    【解决方案2】:

    我很想说这不是隐藏的环境变量。当我转储所有环境变量时,我没有看到相关的变量(虽然有很多)。

    相反,我相信_VariadicView.Tree 是这样工作的。这包含一个根及其内容。我将以HStack 的工作方式为例。查看SwiftUI界面,可以看到如下sn-p代码:

    
    @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
    }
    

    注意BodyNever 类型(因此是原始视图类型)。 _tree 存储有关布局的信息,HStackLayout 类型显然表明这是一个HStack

    SwiftUI 将在内部使用_makeView(view:inputs:) 来创建视图,我假设它会对某些视图进行特殊处理。

    您需要制作HStack/VStack 的自定义版本并向下传递一个环境变量以了解您的子视图属于哪种类型。

    【讨论】:

    • 是的,在您回答之前,我刚刚回答了我的问题。谢谢。 ?
    猜你喜欢
    • 1970-01-01
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-30
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    相关资源
    最近更新 更多