SwiftUI phù hợpGeometry + LazyVStack = crash

Oct 18 2020

Tôi đã mất hàng giờ đồng hồ để xây dựng ví dụ này và tôi không chắc liệu mình có đang làm sai điều gì hay có lỗi làm sập ứng dụng khi sử dụng matchedGeometry+ hay không LazyVStack.

Trong video bên dưới, ứng dụng đã bị lỗi khi tôi nhấp vào hình chữ nhật thứ ba (không hiển thị khi ứng dụng khởi động). Crash sẽ biến mất nếu tôi thay thế LazyVStackbằng VStack, nhưng rõ ràng là tôi muốn tải những thứ của mình một cách lười biếng.

Phiên bản Xcode: Phiên bản 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
                        }
                    }
            }
        }
    }
}

Trả lời

Asperi Oct 18 2020 at 14:06

Vấn đề là bạn phá ScrollViewvỡ bố cục phù hợp.

Đây là biến thể cố định. Đã kiểm tra với 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
                        }
                    }
                        }
            }
        }
    }
}