SwiftUI dopasowanyGeometry + LazyVStack = awaria
Oct 18 2020
Skonstruowanie tego przykładu zajęło mi godziny i nie jestem pewien, czy robię coś nie tak, czy jest błąd powodujący awarię aplikacji podczas używania matchedGeometry+ LazyVStack.
Na poniższym filmie aplikacja uległa awarii, gdy kliknąłem trzeci prostokąt (który nie był widoczny po uruchomieniu aplikacji). Awaria znika, jeśli zastąpię LazyVStackją VStack, ale oczywiście chcę leniwie ładować swoje rzeczy.
Wersja Xcode: Wersja 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
}
}
}
}
}
}
Odpowiedzi
Asperi Oct 18 2020 at 14:06
Problem polega na tym, że niszczysz ScrollViewłamanie dopasowanego układu.
Oto wariant stały. Testowane z 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
}
}
}
}
}
}
}