Tinggi SwiftUI ScrollView VStack GeometryReader diabaikan

Oct 10 2020

Saya ingin menggunakan ScrollView di luar VStack, sehingga konten saya dapat digulir jika VStack meluas melebihi ukuran layar. Sekarang saya ingin menggunakan GeometryReaderdalam VStack dan itu menyebabkan masalah, yang hanya dapat saya selesaikan dengan mengatur bingkai GeometryReader, yang tidak terlalu membantu saya karena saya menggunakan pembaca untuk menentukan ukuran tampilan.

Berikut adalah kode tanpa ScrollView dan berfungsi dengan baik:

struct MyExampleView: View {
  var body: some View {
    VStack {
        Text("Top Label")
            .background(Color.red)
        
        GeometryReader { reader in
            Text("Custom Sized Label")
                .frame(width: reader.size.width, height: reader.size.width * 0.5)
                .background(Color.green)
        }
        
        Text("Bottom Label")
            .background(Color.blue)
    }
    .background(Color.yellow)
  }
}

Ini menghasilkan gambar berikut:

Label berukuran khusus harus berukuran lebar penuh, tetapi lebar setengah untuk tinggi. Sekarang jika saya membungkus kode yang sama dalam ScrollView, ini terjadi:

Tidak hanya semuanya menjadi lebih kecil, tetapi ketinggian Label Berukuran Khusus entah bagaimana diabaikan. Jika saya menyetel tinggi GeometryReader, saya dapat menyesuaikan perilaku itu, tetapi saya ingin GeometryReader tumbuh sebesar isinya. Bagaimana saya bisa mencapai ini?

Terima kasih

Jawaban

1 Asperi Oct 10 2020 at 20:02

Harus dipahami bahwa GeometryReaderitu bukan alat ajaib, itu hanya membaca ruang yang tersedia dalam konteks induk saat ini, tetapi ... ScrollViewtidak memiliki ruang sendiri yang tersedia, itu nol, karena itu menentukan ruang yang dibutuhkan dari konten internal ... jadi gunakan GeometryReaderdi sini Anda telah mendapat siklus - anak meminta orang tua untuk ukuran, tetapi orang tua mengharapkan ukuran dari anak ... Penyaji SwiftUI entah bagaimana menyelesaikan ini (menemukan ukuran minimal yang diketahui), hanya untuk tidak crash.

Berikut adalah solusi yang mungkin untuk skenario Anda - instrumen yang sesuai di sini adalah preferensi tampilan. Disiapkan & diuji dengan Xcode 12 / iOS 14.

struct DemoLayout_Previews: PreviewProvider {
    static var previews: some View {
        Group {
          MyExampleView()
          ScrollView { MyExampleView() }
        }
    }
}

struct MyExampleView: View {
    @State private var height = CGFloat.zero
    var body: some View {
        VStack {
            Text("Top Label")
                .background(Color.red)
            
            Text("Custom Sized Label")
                .frame(maxWidth: .infinity)
                .background(GeometryReader {
                    // store half of current width (which is screen-wide)
                    // in preference
                    Color.clear
                        .preference(key: ViewHeightKey.self, 
                            value: $0.frame(in: .local).size.width / 2.0) }) .onPreferenceChange(ViewHeightKey.self) { // read value from preference in state self.height = $0
                }
                .frame(height: height) // apply from stored state
                .background(Color.green)
            
            Text("Bottom Label")
                .background(Color.blue)
        }
        .background(Color.yellow)
    }
}

struct ViewHeightKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

Catatan: ... dan jangan gunakan GeometryReader jika Anda tidak yakin tentang konteks tampilan Anda.