Me gustaría usar .matchedGeometryEffect para lograr una animación de deslizamiento hacia arriba para una imagen cuando se toca. Solo quiero usar esto para animar el cambio de posición, ya que el marco es el mismo para ambas imágenes. Estoy reemplazando la imagen con un rectángulo negro en este ejemplo, ya que también es reproducible con eso. Código simplificado:
struct ContentView: View { @Namespace var namespace @State var isShowingDetail = false var body: some View { if isShowingDetail { VStack { Rectangle() .foregroundColor(.black) .matchedGeometryEffect(id: "image", in: namespace, properties: .position) .aspectRatio(contentMode: .fit) .edgesIgnoringSafeArea(.top) Spacer() } .onTapGesture { changeView() } } else { Rectangle() .foregroundColor(.black) .matchedGeometryEffect(id: "image", in: namespace, properties: .position) .aspectRatio(contentMode: .fit) .cornerRadius(8) .onTapGesture { changeView() } } } private func changeView() { withAnimation(.easeOut(duration: 0.3)){ isShowingDetail.toggle() } } }La vista se desliza hacia arriba, pero no se anima correctamente, ya que tiene un efecto de desvanecimiento. Supongo que está relacionado con la transición de desvanecimiento predeterminada aplicada a cada vista, traté de configurar una personalizada y no pude encontrar una adecuada para este caso.
La ubicación del modificador importa, y para matchedGeometryEffect es aún más importante.
Aquí está la variante fija. Xcode 13.2 / iOS 15.2
var body: some View { if isShowingDetail { VStack { Rectangle() .foregroundColor(.black) .aspectRatio(contentMode: .fit) .edgesIgnoringSafeArea(.top) // !! applied here !! .matchedGeometryEffect(id: "image", in: namespace, properties: .position) Spacer() } .onTapGesture { changeView() } } else { Rectangle() .foregroundColor(.black) .aspectRatio(contentMode: .fit) .cornerRadius(8) // !! applied here !! .matchedGeometryEffect(id: "image", in: namespace, properties: .position) .onTapGesture { changeView() } } }Descubrí la parte de eliminación del efecto de parpadeo/desvanecimiento. Como supuse, tenía que ver con la transición de atenuación predeterminada que se aplica si no hay una transición explícita. Encontré uno adecuado para este caso:
if isShowingDetail { VStack { Rectangle() .foregroundColor(.black) .aspectRatio(contentMode: .fit) .matchedGeometryEffect(id: "image", in: namespace, properties: .position) .edgesIgnoringSafeArea(.top) Spacer() } .transition(.offset()) // applied here .onTapGesture { changeView() } } else { Rectangle() .foregroundColor(.black) .aspectRatio(contentMode: .fit) .matchedGeometryEffect(id: "image", in: namespace, properties: .position) .transition(.offset()) // applied here .onTapGesture { changeView() } }