如何将动画添加到由 AnyView 制作的自定义导航项之间的过渡?

How do I add Animations to Transitons between custom NavigationItems made from AnyView?

有没有办法将动画添加到自定义 NavigationItem 之间的过渡,如 this article 中所述,由 AnyView 制作?

我喜欢这个 NavigationStack 系统的一切,除了无法添加任何动画过渡。

我知道问题与 AnyView 类型擦除 some View 有关,如 中所述,并且 Group 似乎更好动画自定义视图导航的选择。

Rather that using AnyView and type-erasure, I prefer to encapsulate the conditional logic inside of a Group view. Then the type you return is Group, which will animate properly.

我 运行 没有想法,我需要一些帮助。

我会为上下文添加我的代码。

场景代理:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
    let contentView = ContentView().environmentObject(UserData())
...
}

内容视图:

struct ContentView: View {
    @EnvironmentObject var userData: UserData   
    var body: some View {
        NavigationHost()
            .environmentObject(NavigationStack(
                NavigationItem(view: AnyView(
                    (userData.isSignedIn ? AnyView(HomeView()) : AnyView(RegisterView1(profile: Profile.default)) )
                    .transition(.move(edge: .trailing))
                    .animation(Animation.linear(duration: 1))))))
                    //this animation works for some reason, but only this one.
            .environmentObject(UserData())
    }
}

导航主机:

struct NavigationHost: View{
    @EnvironmentObject var navigation: NavigationStack
    @EnvironmentObject var userData: UserData

    var body: some View {
            ZStack {
                    self.navigation.currentView.view
                        .environmentObject(self.userData)
                        .environmentObject(self.navigation)
                    ...
                }
            }
    }
}

导航堆栈:

final class NavigationStack: ObservableObject {
    @Published var viewStack: [NavigationItem] = []
    @Published var currentView: NavigationItem

    init(_ currentView: NavigationItem ){
        print("Navigation Stack Initialized")
        self.currentView = currentView
    }

    func back() {
        if viewStack.count == 0 {
            return
        }
        let last = viewStack.count - 1
        currentView = viewStack[last]
        viewStack.remove(at: last)
    }

    navigation.advance(NavigationItem(AnyView))
    func advance(_ view: NavigationItem) {
        viewStack.append( currentView )
        currentView = view
    }

    func home() {
       currentView = NavigationItem( view: AnyView(HomeView()) )
       viewStack.removeAll()
    }

}

struct NavigationItem{
    var view: AnyView
}

此处更新了该文章中的实体,以在屏幕之间采用简单的动画转换。所有这些都是粗糙的,仅用于演示,但希望它能以某种方式提供帮助。

注意:过渡在预览版中不起作用(至少在我的 Xcode 11.2/iOS 13.2 中),因此在模拟器或真实设备中进行了测试。

外观如下:

代码如下:

final class NavigationStack: ObservableObject {
    enum Direction {
        case root
        case forward
        case backward
    }

    @Published var viewStack: [NavigationItem] = []
    @Published var currentView: NavigationItem
    @Published var navigate: Direction = .root

    init(_ currentView: NavigationItem ){
        self.currentView = currentView
    }
    func unwind(){
        if viewStack.count == 0{
            return
        }
        let last = viewStack.count - 1
        currentView = viewStack[last]
        withAnimation {
            self.navigate = .backward
        }
        viewStack.remove(at: last)
    }
    func advance(_ view:NavigationItem){
        viewStack.append( currentView)
        currentView = view
        withAnimation {
            self.navigate = .forward
        }
    }
    func home( ){
        currentView = NavigationItem(view: AnyView(HomeView()))
        viewStack.removeAll()
        withAnimation {
            self.navigate = .root
        }
    }
}

struct NavigationHost: View{
    @EnvironmentObject var navigation: NavigationStack

    var body: some View {
        ZStack(alignment: .topLeading) {
            if navigation.navigate == .root {
                self.navigation.currentView.view
            }
            else if navigation.navigate == .backward {
                self.navigation.currentView.view
                    .transition(.move(edge: .leading))
            }
            if navigation.navigate == .forward {
                self.navigation.currentView.view
                    .transition(.move(edge: .trailing))
            }
        }

    }
}