如何在SwiftUI中为视图之间的过渡设置动画?

时间:2020-03-21 05:10:14

标签: swift swiftui

我有以下看法。

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
    }
}

1 个答案:

答案 0 :(得分:3)

通过以下方法,您可以根据需要修改appMode(onAppear,onTapGesture等)。当然,动画持续时间可以随意设置(以及过渡的种类,实际上,但是某些过渡在Preview中表现不佳,应在Simulator或真实设备上进行测试)。

演示 :(首先闪烁只是预览启动,然后是onAppear和onTap的两个过渡)

demo

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

代码:重要部分带有内嵌注释。

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)!
    }
}

demo2