首页 > 解决方案 > SwiftUI Hero Animation (matchedGeometryEffect) 问题

问题描述

我一直在网上查看不同的教程,试图拼凑出一个“简单”的英雄动画。图像位于 LazyHGrid 中,点击时应展开至第二张图像。我试图尽可能地压缩和简化下面的代码。

我已经让它在网格之外工作,只有一张图像。

import SwiftUI

struct Item: Hashable {
    let id = UUID()
    var text = "TEXT"
}


struct ContentView: View {
    
    var items = [Item(text: "One"), Item(text: "Two"), Item(text: "Three"), Item(text: "Four"), Item(text: "Five"), Item(text: "Six"), Item(text: "Seven"),Item(text: "Eight"),Item(text: "Nine")]
    
    @State private var isShowingDetail = false
    @State private var selectedItemID: UUID? = nil
    
    @Namespace var animation: Namespace.ID
    
    var body: some View {
        ZStack {
            ScrollView(.horizontal) {
                LazyHGrid(rows: [GridItem(.flexible())], spacing: 10, content: {
                    ForEach(items,id: \.id, content: { item in
                        VStack {
                            Image("Image2")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(width: 200)
                                .matchedGeometryEffect(id: item.id, in: animation)
                                .onTapGesture {
                                    withAnimation(.spring()){
                                        self.selectedItemID = item.id
                                        isShowingDetail.toggle()
                                    }
                                }
                            Text(item.text)
                        }
                    })
                })
            }
            
            if isShowingDetail {
                VStack {
                    Image("Image1")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 300)
                        .matchedGeometryEffect(id: selectedItemID, in: animation)
                        .onTapGesture {
                            withAnimation(.spring()){
                                selectedItemID = nil
                               isShowingDetail.toggle()
                            }
                        }
                    Spacer()
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


在此处输入图像描述

标签: swiftui

解决方案


看起来你selectedItemIDisShowingDetail正在互相争斗(并且是多余的,因为selectedItemID是可选的。我已经注释掉了几行,它似乎正在工作:

struct ContentView: View {
    
    @State var items = [Item(text: "One"), Item(text: "Two"), Item(text: "Three"), Item(text: "Four"), Item(text: "Five"), Item(text: "Six"), Item(text: "Seven"),Item(text: "Eight"),Item(text: "Nine")]
    
    //@State private var isShowingDetail = false // <----- here
    @State private var selectedItemID: UUID? = nil
    
    @Namespace var animation: Namespace.ID
    
    var body: some View {
        ZStack {
            ScrollView(.horizontal) {
                LazyHGrid(rows: [GridItem(.flexible())], spacing: 10, content: {
                    ForEach(items,id: \.id, content: { item in
                        VStack {
                            Image("0")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(width: 200)
                                .matchedGeometryEffect(id: item.id, in: animation)

                                .onTapGesture {
                                    withAnimation(.spring()){
                                        self.selectedItemID = item.id
                                        //isShowingDetail.toggle()  // <----- here
                                    }
                                }
                            Text(item.text)
                        }
                    })
                })
            }
            
            if let selectedItemID = selectedItemID {  // <----- here
                VStack {
                    Image("1")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 300)
                        .matchedGeometryEffect(id: selectedItemID, in: animation)
                        .onTapGesture {
                            withAnimation(.spring()){
                                self.selectedItemID = nil
                               //isShowingDetail.toggle()  // <----- here
                            }
                        }
                    Spacer()
                }
            }
        }
    }
}

需要注意的是,如果在细节图像缩小时再次点击原始图像,似乎可能会进入一个有趣的状态。不确定这是代码的错误还是 matchGeometryEffect 本身的错误。


推荐阅读