SwiftUI:动画过渡

SwiftUI: Animate Transitions

如何为不同视图之间的幻灯片切换设置动画?

在下面的示例代码中,我制作了一个选择器,用于选择要显示的视图,我的目标是实现类似于 NavigationLink 转换的转换。 目前它根本没有动画。 如果我将 .animation(.easeInOut(duration: 2)) 修饰符添加到 ZStack 它会为淡入淡出动画设置动画 2 秒,但我不明白为什么。

struct ContentView: View {

    enum WhichScreen: String, CaseIterable {
        case red, blue, green, yellow
    }

    @State private var whichScreen = WhichScreen.red

    var body: some View {
        VStack {
            Picker("screen", selection: $whichScreen.animation()) {
                ForEach(WhichScreen.allCases, id: \.self) { value in
                    Text(value.rawValue).tag(value)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            ZStack {
                Color.black
                switch whichScreen {
                case .red:
                    Color.red
                case .blue:
                    Color.blue
                case .green:
                    Color.green
                case .yellow:
                    Color.yellow
                }
            }
            .transition(.slide)
        }
    }
}

要获得您正在寻找的幻灯片动画,.transition 需要仅应用于您要滑动的部分(例如,本例中的非黑色):

ZStack {
    Color.black
    Group {
        switch whichScreen {
        case .red:
            Color.red
        case .blue:
            Color.blue
        case .green:
            Color.green
        case .yellow:
            Color.yellow
        }
    }.transition(.slide)
}

如果您希望它与 NavigationLink 转换完全一样,您还需要多做一步,事实上,现在 'old' 或 'previous' 视图在转换前消失。如果你想把它包含在堆栈中,你可以这样做:

struct ContentView: View {
    
    enum WhichScreen: String, CaseIterable {
        case red, blue, green, yellow
    }
    
    @State private var whichScreen = WhichScreen.red
    @State private var previousScreen : WhichScreen? = .none
    
    var body: some View {
        VStack {
            Picker("screen", selection: $whichScreen.animation()) {
                ForEach(WhichScreen.allCases, id: \.self) { value in
                    Text(value.rawValue).tag(value)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            .onChange(of: whichScreen) { [whichScreen] _ in
                previousScreen = whichScreen
            }
            ZStack {
                Color.black
                if let previousScreen = previousScreen {
                    switch(previousScreen) {
                    case .red:
                        Color.red
                    case .blue:
                        Color.blue
                    case .green:
                        Color.green
                    case .yellow:
                        Color.yellow
                    }
                    
                }
                Group {
                    switch whichScreen {
                    case .red:
                        Color.red
                    case .blue:
                        Color.blue
                    case .green:
                        Color.green
                    case .yellow:
                        Color.yellow
                    }
                }.transition(.slide)
            }
        }
    }
}

您真的需要黑色的 ZStack(在您的示例代码段中永远看不到)吗?没有它,转换就可以开箱即用(在模拟器中,而不是在 SwiftUI 预览中):

struct ContentView: View {

    enum WhichScreen: String, CaseIterable {
        case red, blue, green, yellow
    }

    @State private var whichScreen = WhichScreen.red

    var body: some View {
        VStack {
            Picker("screen", selection: $whichScreen.animation()) {
                ForEach(WhichScreen.allCases, id: \.self) { value in
                    Text(value.rawValue).tag(value)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            Group {
                switch whichScreen {
                case .red:
                    Color.red
                case .blue:
                    Color.blue
                case .green:
                    Color.green
                case .yellow:
                    Color.yellow
                }
            }
            .transition(.slide)
        }
    }

}