2

有没有办法将动画添加到本文中描述的自定义NavigationItems之间的转换?AnyView

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

我知道问题与使用some Views 的类型擦除有关AnyView,如本答案中所述,这Group似乎是为自定义视图导航设置动画的更好选择。

而不是使用 AnyView 和类型擦除,我更喜欢将条件逻辑封装在组视图中。然后你返回的类型是 Group,它会正确地动画。

我的想法已经用完了,我需要一些帮助。

我将添加我的代码作为上下文。

场景委托:

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
}

4

1 回答 1

6

这是该文章中更新的实体,以在屏幕之间采用简单的动画过渡。所有这些都是沙哑的,仅用于演示,但希望它可以以某种方式有所帮助。

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

这是它的外观: SwiftUI 动画自定义导航项

这是代码:

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

    }
}
于 2019-11-28T11:13:36.650 回答