Bagaimana cara mendapatkan ukuran dan posisi yang diberikan tampilan aktual di SwiftUI?

Aug 15 2020

Pertanyaannya adalah bagaimana cara mendapatkan ukuran dan posisi yang ditampilkan sebenarnya dalam tampilan induk? Dengan kata lain, bagaimana cara mendapatkan Text("Foo")ukuran sebenarnya pada kode SwiftUI di bawah ini?

GeometryReaderdapat digunakan untuk mendapatkan orang tua yang diusulkan sizedan inset area aman melalui safeAreaInsetsdan informasi ini didefinisikan di dalam GeometryProxy. Anda dapat melihat dari gambar di bawah ini, ukuran yang diusulkan untuk VStackadalah 300lebar dan 300tinggi dan ukuran sebenarnya VStacktidak diketahui.

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

Jawaban

2 Metropolis Aug 15 2020 at 12:17

Ukuran Rendered Sebenarnya

Solusinya adalah mendapatkan ukuran aktual yang dirender melalui .backgroundpengubah dengan nested GeometryReader. Informasi ukuran di dalam proxy geometri baru kemudian dapat disimpan dalam @Statevariabel sementara yang ditentukan di Tampilan.

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

Posisi Rendered Aktual

Posisi aktual yang diberikan untuk tampilan tersebut dapat dihitung menggunakan Anchordan anchorPreference. Dengan menggunakan jangkar dan induk geometryProxy, kita bisa dengan mudah mendapatkan .boundinformasi posisi tampilan target.

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