Несоответствие анимации NavigationStack (пользовательский пакет)
Я создал оболочку вокруг API стека навигации с заменой root и функциями push и remove.
Есть одна ошибка, которую я не мог преодолеть некоторое время. Есть одна проблема, которая не соответствует всплывающим окнам и выталкивает анимацию после определенных операций навигации.
Чтобы было легче понять, позвольте мне объяснить некоторые необходимые сведения.
Это основная точка входа пакета, который экспортирует navigationStack со всеми настройками.
public struct AppBuilder: View {
@ObservedObject var navigationHandler : NavigationHandler
public init(initial: PageRouteInfo) {
navigationHandler = NavigationHandler(initial: initial)
}
public var body: some View {
NavigationStack(path:$navigationHandler.stack) {
EmptyView()
.navigationDestination(for: PageRouteInfo.self) { routeInfo in
AnyView(routeInfo.view.transition(.asymmetric(insertion: .move(edge: .leading), removal: .identity)))
.navigationBarBackButtonHidden(routeInfo.isInitial )
}
}
.environmentObject(navigationHandler)
}
}
Это модель навигации, которая соответствует хешируемому протоколу для заполнения им стека.
public struct PageRouteInfo: Hashable {
let view: any View
let isInitial: Bool
let id : String = UUID().uuidString
public init( view: any View) {
self.view = view
self.isInitial = false
}
public init(view: any View, isInitial : Bool) {
self.view = view
self.isInitial = isInitial
}
public static func == (lhs: PageRouteInfo, rhs: PageRouteInfo) -> Bool {
lhs.hashValue == rhs.hashValue
}
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
public mutating func makeFirst() -> PageRouteInfo {
return PageRouteInfo(view: view, isInitial: true)
}
}
И это класс, который управляет всеми элементами навигации и содержит стек @Published.
import Foundation
import SwiftUI
@available(iOS 16.0, *)
@available(macOS 13.0, *)
public class NavigationHandler: Navigator {
@Published public var stack: [PageRouteInfo] = [] {
didSet {
print(stack.map { "Name : \($0.view)" })
}
}
public init(initial: PageRouteInfo) {
stack.append(initial)
}
public func push(destionation: any DeepRoutes) {
stack.append(destionation.toItem())
}
public func pop() {
if isNotLast {
stack.removeLast()
}
}
public func popToRoot() {
stack.removeLast((1 ... stack.count - 1).count)
}
public func pushAndRemoveUntil(destionation: any DeepRoutes) {
var route = destionation.toItem()
stack.append(route.makeFirst())
if stack.last != route {
withDelay {
self.stack.removeFirst((0 ..< (self.stack.count - 1)).count)
}
}
}
public func replaceRoot(with: any DeepRoutes) {
var route = with.toItem()
if isNotLast {
stack.insert(route.makeFirst(), at: 0)
stack.remove(at: 1)
} else {
stack.insert(route.makeFirst(), at: 1)
withDelay { self.stack.remove(at: 0) }
}
}
}
private extension NavigationHandler {
var isNotLast: Bool {
return stack.count > 1
}
}
private extension NavigationHandler {
func withDelay(_ callback: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
callback()
}
}
}
С учетом всего сказанного, push, pop и popToRoot работают как шарм. Но когда дело доходит до pushAndRemoveUntil и replaceRoot, все идет наперекосяк.
Например, если я использую pushAndRemoveUntil, он работает, как и предполагалось, помимо анимации. Он помещает новый вид в стек без кнопки «Назад» и удаляет остальные. Но если вы используете что-либо после запуска ошибки, в случае нажатия она исчезает мгновенно, или если вы снова используете pushAndRemoveUntil, она идет с всплывающей анимацией. То же самое с функцией replaceRoot.