Как добавить анимацию в транзитоны между пользовательскими элементами навигации, созданными из AnyView?

Есть ли способ добавить анимацию к переходам между пользовательскими NavigationItems сделан из AnyViewкак описано в этой статье?

Мне все в этом нравится NavigationStack системе, за исключением того, что нельзя добавлять анимированные переходы.

Я так понимаю, проблема связана с стиранием текста some Views с AnyView, как описано в этом ответе, и чтоGroup кажется лучшим выбором для анимации пользовательской навигации по представлениям.

Вместо того, чтобы использовать AnyView и стирание типов, я предпочитаю инкапсулировать условную логику внутри группового представления. Затем возвращаемый тип - Group, который будет правильно анимирован.

У меня закончились идеи, и мне нужна помощь.

Я добавлю свой код для контекста.

SceneDelegate:

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

ContentView:

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

NavigationHost:

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

NavigationStack:

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
}

1 ответ

Решение

Вот обновленные сущности из этой статьи, которые используют простые анимированные переходы между экранами. Все это неуклюже и только для демонстрации, но надеюсь, что это может быть как-то полезно.

Примечание: переходы не работают в предварительном просмотре (по крайней мере, в моем 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))
            }
        }

    }
}
Другие вопросы по тегам