SwiftUI matchedGeometry + LazyVStack = crash

Oct 18 2020

มันเอาฉันชั่วโมงเพื่อสร้างตัวอย่างนี้และฉันไม่แน่ใจว่าฉันกำลังทำอะไรผิดพลาดหรือมีข้อผิดพลาดแอปขัดข้องเมื่อใช้+matchedGeometryLazyVStack

ในวิดีโอด้านล่างแอปขัดข้องเมื่อฉันคลิกที่สี่เหลี่ยมผืนผ้าที่สาม (ซึ่งมองไม่เห็นเมื่อแอปเริ่มทำงาน) ข้อขัดข้องจะหายไปหากฉันแทนที่LazyVStackด้วยVStackแต่เห็นได้ชัดว่าฉันต้องการขี้เกียจโหลดสิ่งต่างๆ

เวอร์ชัน Xcode: เวอร์ชัน 12.0.1 (12A7300)

struct ContentView: View {
    @Namespace var namespace
    @State var selected: Int?


    var body: some View {
        ZStack {
            VStack { 
                Text("Cool rectangles")
                
                if selected == nil {
                    ScrollView(.vertical, showsIndicators: false) {
                        BoxList(namespace: namespace, selected: $selected)
                    }
                }
            }
            
            if let id = selected {
                Rectangle()
                    .foregroundColor(.red)
                    .matchedGeometryEffect(id: id, in: namespace)
                    .onTapGesture {
                        withAnimation{
                            selected = nil
                        }
                    }

            }

        }
    }
}

struct BoxList: View {
    let namespace: Namespace.ID
    @Binding var selected: Int?
    
    var body: some View {
        LazyVStack {
            ForEach(0..<10){ item in
                Rectangle()
                    .matchedGeometryEffect(id: item, in: namespace)
                    .frame(width: 200, height: 200)
                    .onTapGesture {
                        withAnimation {
                            selected = item
                        }
                    }
            }
        }
    }
}

คำตอบ

Asperi Oct 18 2020 at 14:06

ปัญหาคือคุณทำลายScrollViewเค้าโครงที่ตรงกันหมด

นี่คือตัวแปรคงที่ ทดสอบด้วย Xcode 12 / iOS 14

struct ContentView: View {
    @Namespace var namespace
    @State var selected: Int?


    var body: some View {
        ZStack {
            VStack {
                Text("Cool rectangles")
                
                  ScrollView(.vertical, showsIndicators: false) {
                        BoxList(namespace: namespace, selected: $selected)
                  }.opacity(selected == nil ? 1 : 0)
            } // << or place here opacity modifier here
            
            if let id = selected {
                Rectangle()
                    .foregroundColor(.red)
                    .matchedGeometryEffect(id: id, in: namespace)
                    .onTapGesture {
                        withAnimation{
                            selected = nil
                        }
                    }

            }

        }
    }
}

struct BoxList: View {
    let namespace: Namespace.ID
    @Binding var selected: Int?
    
    var body: some View {
        LazyVStack {
            ForEach(0..<10){ item in
                    if item == selected {
                Color.clear     // placeholder to avoid duplicate match id run-time warning
                    .frame(width: 200, height: 200)
                    } else {
                Rectangle()
                    .matchedGeometryEffect(id: item, in: namespace)
                    .frame(width: 200, height: 200)
                    .onTapGesture {
                        withAnimation {
                            selected = item
                        }
                    }
                        }
            }
        }
    }
}