SwiftUI ScrollView VStack GeometryReader ถูกละเว้น

Oct 10 2020

ฉันต้องการใช้ ScrollView นอก VStack เพื่อให้เนื้อหาของฉันสามารถเลื่อนได้หาก VStack ขยายเกินขนาดหน้าจอ ตอนนี้ฉันต้องการใช้GeometryReaderภายใน VStack และมันทำให้เกิดปัญหาซึ่งฉันสามารถแก้ไขได้โดยการตั้งค่าเฟรม GeometryReader ซึ่งไม่ได้ช่วยอะไรฉันเลยเพราะฉันใช้โปรแกรมอ่านเพื่อกำหนดขนาดมุมมอง

นี่คือรหัสที่ไม่มี ScrollView และใช้งานได้ดี:

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

ผลลัพธ์ในภาพต่อไปนี้:

ป้ายกำกับขนาดที่กำหนดเองควรมีความกว้างเต็ม แต่กว้างครึ่งหนึ่งสำหรับความสูง ตอนนี้ถ้าฉันห่อรหัสเดียวกันใน ScrollView สิ่งนี้จะเกิดขึ้น:

ไม่เพียง แต่ทำให้ทุกอย่างเล็กลง แต่ความสูงของ Custom Sized Label จะถูกละเลยไป ถ้าฉันตั้งค่าความสูงของ GeometryReader ฉันสามารถปรับพฤติกรรมนั้นได้ แต่ฉันต้องการให้ GeometryReader มีขนาดใหญ่พอ ๆ กับเนื้อหา ฉันจะบรรลุเป้าหมายนี้ได้อย่างไร?

ขอบคุณ

คำตอบ

1 Asperi Oct 10 2020 at 20:02

ควรเข้าใจว่าGeometryReaderไม่ใช่เครื่องมือวิเศษเพียงแค่อ่านพื้นที่ว่างในบริบทหลักปัจจุบัน แต่ ... ScrollViewไม่มีพื้นที่ว่างของตัวเองเป็นศูนย์เนื่องจากกำหนดพื้นที่ที่ต้องการจากเนื้อหาภายใน ... ดังนั้นการใช้GeometryReaderที่นี่ คุณมีวงจร - เด็กถามผู้ปกครองสำหรับขนาด แต่ผู้ปกครองคาดหวังขนาดจากเด็ก ...

นี่คือวิธีแก้ปัญหาที่เป็นไปได้สำหรับสถานการณ์ของคุณเครื่องมือที่เหมาะสมนี่คือการตั้งค่ามุมมอง จัดทำและทดสอบด้วย 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()
    }
}

หมายเหตุ: ... และอย่าใช้ GeometryReader หากคุณไม่แน่ใจเกี่ยวกับบริบทที่มุมมองของคุณอยู่