You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI实现iPad Pro应用侧滑飞入动画方案咨询

实现SwiftUI版iPad Pro解锁式飞入动画

要实现类似iPad Pro解锁时应用从右侧依次飞入的效果,核心是利用偏移+透明度动画,配合逐元素延迟打造依次入场的节奏。下面针对你的DashboardView给出具体修改方案:

步骤1:移除TabView,改用直观布局容器

删掉原代码中的TabView,改用ScrollView嵌套VStack(或Grid,按需调整)展示所有卡片按钮,无需左右滑动即可直接点击操作。

步骤2:添加入场动画逻辑

给每个卡片设置初始状态(偏移至屏幕右侧+完全透明),在视图加载完成后,通过带递增延迟的动画让卡片依次从右侧飞入并显现。

修改后的完整代码

struct DashboardView: View {
    
    @EnvironmentObject var dashboardModel: DashboardModel
    @EnvironmentObject var gameModel: GameModel
    
    @Binding var isConnected: Bool
    
    // 控制动画触发状态,初始为未激活
    @State private var isAnimated = false
    
    var body: some View {
        
        NavigationView {
         
            ZStack {
                
                LinearGradient(gradient: Gradient(colors: [.black, .gray, .black]), startPoint: .topLeading, endPoint: .bottomTrailing)
                
                ScrollView {
                    VStack(spacing: 20) {
                        // 遍历卡片数据,为每个卡片绑定动画
                        ForEach(Array(dashboardModel.cards.enumerated()), id: \.element.id) { index, card in
                            NavigationLink(destination: getDestination(for: index)) {
                                CardView(data: card)
                            }
                            .isDetailLink(false)
                            // 初始状态:偏移至屏幕右侧+完全透明
                            .offset(x: isAnimated ? 0 : UIScreen.main.bounds.width)
                            .opacity(isAnimated ? 1 : 0)
                            // 递增延迟实现依次飞入效果,可调整0.1数值改变节奏
                            .animation(.interpolatingSpring(stiffness: 100, damping: 15).delay(Double(index) * 0.1), value: isAnimated)
                        }
                    }
                    .padding()
                }
                
            }
            .navigationTitle("Herzlich Willkommen zur Spielerfassung")
            .toolbar {
                
                ToolbarItem(placement: .navigationBarLeading) {
                  
                    Button {
                        
                        withAnimation(.spring()){
                            
                            gameModel.currentTeam = nil
                            gameModel.saisons = [Saison]()
                            
                        }
                        
                    } label: {
                        
                        Image(systemName: "house.fill")
                            .resizable()
                            .scaledToFit()
                            .frame(width: 35, height: 35)
                        
                    }
                }
                
                if gameModel.offlineChangeCounter > 0 {
                 
                    ToolbarItem(placement: .navigationBarTrailing) {
                      
                        Button {
                            
                            if isConnected {
                                
                                gameModel.pushOfflineData()
                                gameModel.offlineChangeCounter = 0
                                
                            } else {
                                
                                gameModel.errorText = "Sorgen Sie für eine aktive Internetverbindung."
                                gameModel.showErrorToast = true
                            }
                            
                        } label: {
                            
                            Image(systemName: "icloud.and.arrow.up")
                                .resizable()
                                .scaledToFit()
                                .foregroundColor(.white)
                                .frame(width: 35, height: 35)
                            
                        }
                    }
                }
                
                if !isConnected {
                 
                    ToolbarItem(placement: .navigationBarTrailing) {
                      
                        Image(systemName: "wifi.slash")
                            .resizable()
                            .scaledToFit()
                            .foregroundColor(.red)
                            .frame(width: 35, height: 35)
                    }
                }
                
            }
            .toolbarColorScheme(.dark, for: .navigationBar)
            .toolbarBackground(.visible, for: .navigationBar)
            .ignoresSafeArea()
            // 视图出现时触发动画
            .onAppear {
                isAnimated = true
            }
            
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .simpleToast(isPresented: $gameModel.showErrorToast, options: SimpleToastOptions(alignment: .bottom, hideAfter: 3, showBackdrop: false, animation: .easeIn, modifierType: .slide)) {
            
            gameModel.showErrorToast = false
            gameModel.errorText = ""
            
        } content: {
            
            ErrorToastView()
                .padding(.bottom, 0)
            
        }
    }
    
    // 统一管理卡片对应的跳转目标,替代原硬编码逻辑
    private func getDestination(for index: Int) -> some View {
        switch index {
        case 0:
            return AnyView(PlayerView())
        case 1:
            return AnyView(FastGamedayView()) // 取消注释即可启用
        case 2:
            return AnyView(SaisonsView())
        case 3:
            return AnyView(GamedayView())
        case 4:
            return AnyView(HistoryView())
        case 5:
            return AnyView(StatisticsView())
        default:
            return AnyView(EmptyView())
        }
    }
}

关键细节说明

  • 动画手感:使用interpolatingSpring弹簧动画模拟系统原生弹性效果,可调整stiffness(刚度)和damping(阻尼)参数改变动画快慢和回弹幅度。
  • 延迟节奏:通过Double(index) * 0.1给每个卡片设置递增延迟,数值越小入场速度越快,可按需调整。
  • 布局灵活度:如果需要网格排版,可将VStack替换为LazyVGrid,适配不同屏幕尺寸。
  • 代码整洁性:新增getDestination方法统一管理跳转视图,避免大量硬编码的NavigationLink重复逻辑。

内容的提问来源于stack exchange,提问作者schmale96

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 06:47:11