如何在 SwiftUI 中获取实际视图渲染的大小和位置?

Lor*_*lor 4 swift swiftui

问题是如何获取父视图中实际查看的渲染尺寸和位置?换句话说,如何Text("Foo")在下面的 SwiftUI 代码中获取实际大小?

GeometryReader 可用于获取父建议size和安全区域插入 via safeAreaInsets,这些信息在 内部定义GeometryProxy。从下面的截图中您可以看到,建议的尺寸为VStack宽度300300高度,实际尺寸VStack未知。

在此输入图像描述

struct FooView: View {
    var body: some View {
        GeometryReader { geometryProxy in
            VStack {
                Text("\(geometryProxy.size.height), \(geometryProxy.size.width)")
                Text("Foo")
            }
            .background(Color.green)
        }
        .frame(width: 300, height: 300)
        .background(Color.blue)
    }
}
Run Code Online (Sandbox Code Playgroud)

Lor*_*lor 7

实际渲染尺寸

.background解决方法是通过带有嵌套的修饰符来获取实际渲染的大小GeometryReader。然后,新几何代理内的尺寸信息可以存储在@State视图中定义的临时变量中。

struct FooSizePreferenceKey: PreferenceKey {
    static let defaultValue = CGSize.zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

struct FooView: View {
    @State private var fooSize: CGSize = .zero

    var body: some View {
        GeometryReader { geometryProxy in
            VStack {
                Text("\(self.fooSize.height), \(self.fooSize.width)")
                Text("Foo")
                    .background(
                        GeometryReader { fooProxy in
                            Color
                                .green
                                .preference(key: FooSizePreferenceKey.self,
                                            value: fooProxy.size)
                                .onPreferenceChange(FooSizePreferenceKey.self) { size in
                                    self.fooSize = size
                            }
                        }
                    )
            }
        }
        .frame(width: 300, height: 300)
        .background(Color.blue)
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

实际渲染位置

视图的实际渲染位置可以使用Anchor和 来计算anchorPreference。使用anchor和parent ,我们可以轻松获取目标视图的geometryProxy位置信息。.bound

struct FooAnchorData: Equatable {
    var anchor: Anchor<CGRect>? = nil
    static func == (lhs: FooAnchorData, rhs: FooAnchorData) -> Bool {
        return false
    }
}

struct FooAnchorPreferenceKey: PreferenceKey {
    static let defaultValue = FooAnchorData()
    static func reduce(value: inout FooAnchorData, nextValue: () -> FooAnchorData) {
        value.anchor = nextValue().anchor ?? value.anchor
    }
}

struct FooView: View {
    @State private var foo: CGPoint = .zero

    var body: some View {
        GeometryReader { geometryProxy in
            VStack {
                Text("\(self.foo.x), \(self.foo.y)")
                Text("Foo")
                    .background(
                        GeometryReader { fooProxy in
                            Color
                                .green
                                .anchorPreference(key: FooAnchorPreferenceKey.self,
                                                  value: .bounds,
                                                  transform: {FooAnchorData(anchor: $0) })
                                .onPreferenceChange(FooAnchorPreferenceKey.self) { data in
                                    self.foo = geometryProxy[data.anchor!].origin
                            }
                        }
                    )
            }
        }
        .frame(width: 300, height: 300)
        .background(Color.blue)
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述