SwiftUI matchedGeometry + LazyVStack = crash
Oct 18 2020
Cela m'a pris des heures pour construire cet exemple, et je ne suis pas sûr si je fais quelque chose de mal ou si un bogue plante l'application lors de l'utilisation de matchedGeometry+ LazyVStack.
Dans la vidéo ci-dessous, l'application s'est plantée lorsque j'ai cliqué sur le troisième rectangle (qui n'était pas visible au démarrage de l'application). Crash disparaît si je remplace LazyVStackpar VStack, mais je veux évidemment charger mes affaires paresseusement.
Version Xcode: Version 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
}
}
}
}
}
}
Réponses
Asperi Oct 18 2020 at 14:06
Le problème est que vous détruisez ScrollViewla mise en page correspondante.
Voici une variante fixe. Testé avec 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
}
}
}
}
}
}
}