【问题标题】:How to animate transition between views in SwiftUI?如何在 SwiftUI 中为视图之间的过渡设置动画?
【发布时间】:2020-03-21 05:10:14
【问题描述】:

我有以下看法。

import SwiftUI

struct AppView: View {
    @EnvironmentObject var appStore: AppStore

    var body: some View {
        ZStack {
            Color.blue

            if .game == self.appStore.appMode {
                GameView()
            } else if .options == self.appStore.appMode {
                OptionsView()
            } else {
                MenuView()
            }
        }
    }
}

我想动画子视图之间的切换。 我已经看到一些使用状态作为一个开关的示例,但我依赖的不止一个。 我还尝试使用onAppearonDisappear 在子视图中应用动画。这行得通,但是当视图未显示时,onDisappear 不再执行。此外,我想让它适用于所有视图。

有没有办法在不使用多个状态的情况下做到这一点?我只想使用.transition.animation

目前我最好的解决方案是这个。

import SwiftUI

struct AppView: View {
    @EnvironmentObject var appStore: AppStore

    var body: some View {
        ZStack {
            Color.blue

            if .game == self.appStore.appMode {
                GameView()
                .transition(.scale)
                .zIndex(1) // to keep the views on top, however this needs to be changed when the active child view changes.
            } else if .options == self.appStore.appMode {
                OptionsView()
                .transition(.scale)
                .zIndex(2)
            } else {
                MenuView()
                .transition(.scale)
                .zIndex(3)
            }
        }
    }
}

每个视图转换器上。

.onTapGesture {
    withAnimation(.easeInOut(duration: 2)) {
        self.appStore.appMode = .game
    }
}

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    通过以下方法,您可以根据需要修改appMode(onAppear、onTapGesture 等)。当然,动画持续时间可以任意设置(实际上也可以设置过渡类型,但有些过渡在预览中表现不佳,应在模拟器或真实设备上进行测试)。

    演示:(第一次闪烁只是预览启动,然后是 onAppear 的两个转换,然后是 onTap)

    使用 Xcode 11.4 / iOS 13.4 测试 - 可以在预览版和模拟器中使用。

    代码:用 cmets inline 标记的重要部分。

    var body: some View {
        ZStack {
            // !! to keep background deepest, `cause it affects removing transition
            Color.blue.zIndex(-1)
    
            // !! keep any view in explicit own `if` (don't use `else`)
            if .game == self.appStore.appMode {
                GameView()
                    .transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
            }
    
            if .options == self.appStore.appMode {
                OptionsView()
                    .transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
            }
    
            if .menu == self.appStore.appMode {
                MenuView()
                    .transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
            }
        }
    }
    

    更新:对于.slide(可能还有其他移动转换),状态更改应包含在显式withAnimation 中,如下所示

    withAnimation {
        self.appStore.appMode = new_mode_here
    }
    

    注意:这是预览不支持的转换之一 - 在模拟器中测试。

    类似过渡的示例

        ...
        if .menu == self.appStore.appMode {
            Text("MenuView").frame(width: 300, height: 100).background(Color.red)
                .transition(AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(.easeInOut(duration: 1)))
        }
    }
    .onTapGesture {
        withAnimation {
            let next = self.appStore.appMode.rawValue + 1
            self.appStore.appMode = next > 2 ? .game : AppStore.AppMode(rawValue: next)!
        }
    }
    
    

    【讨论】:

    • 谢谢,它运行良好。但是,我玩弄了代码并且有两个新问题。 1. 为什么.slide 动画不起作用? 2. 为什么不能省略“AnyTransition”?
    • 如果它对您完美运行,请问您为什么不接受答案?
    • 我还在理解答案中。但是,你追求的是分数,就是这样。给你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 2011-09-01
    • 2020-09-16
    • 1970-01-01
    • 2020-03-22
    • 2022-07-17
    相关资源
    最近更新 更多