diff --git a/Sources/SwiftNavigation/UINavigationPath.swift b/Sources/SwiftNavigation/UINavigationPath.swift index 14d03bff2..135bb0b55 100644 --- a/Sources/SwiftNavigation/UINavigationPath.swift +++ b/Sources/SwiftNavigation/UINavigationPath.swift @@ -8,7 +8,7 @@ public struct UINavigationPath: Equatable { public var elements: [Element] = [] @_spi(Internals) - public enum Element: Equatable { + public enum Element: Hashable { case eager(AnyHashable) case lazy(Lazy) @@ -52,6 +52,15 @@ public struct UINavigationPath: Equatable { return CodableRepresentation.Element(eager) == lazy } } + + public func hash(into hasher: inout Hasher) { + switch self { + case let .eager(value), let .lazy(.element(value)): + hasher.combine(value) + case let .lazy(.codable(value)): + hasher.combine(value.decode()) + } + } } /// The number of elements in this path. diff --git a/Sources/UIKitNavigation/Navigation/NavigationStackController.swift b/Sources/UIKitNavigation/Navigation/NavigationStackController.swift index 54cc23f48..529b7980b 100644 --- a/Sources/UIKitNavigation/Navigation/NavigationStackController.swift +++ b/Sources/UIKitNavigation/Navigation/NavigationStackController.swift @@ -19,6 +19,7 @@ } } } + private var didPop: Set = [] private let pathDelegate = PathDelegate() private var root: UIViewController? @@ -62,6 +63,10 @@ super.delegate = pathDelegate + interactivePopGestureRecognizer?.addTarget( + self, action: #selector(interactivePopGestureRecognizerAction) + ) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) { traitOverrides.push = UIPushAction { [weak self] value in self?._push(value: value) @@ -154,6 +159,17 @@ } } + @objc private func interactivePopGestureRecognizerAction( + _ gesture: UIScreenEdgePanGestureRecognizer + ) { + guard + gesture.state == .began, + let last = path.last, + !viewControllers.compactMap(\.navigationID).contains(last) + else { return } + didPop.insert(last) + } + fileprivate func viewController( for navigationID: UINavigationPath.Element ) -> UIViewController? { @@ -310,6 +326,29 @@ } } + extension NavigationStackController: UINavigationBarDelegate { + public func navigationBar( + _ navigationBar: UINavigationBar, shouldPop item: UINavigationItem + ) -> Bool { + if let navigationID = viewControllers + .first(where: { $0.navigationItem == item })? + .navigationID + { + didPop.insert(navigationID) + } + return true + } + + public func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) { + path.removeAll(where: { didPop.contains($0) }) + didPop.removeAll() + } + + public func navigationBar(_ navigationBar: UINavigationBar, didPush item: UINavigationItem) { + didPop.removeAll() + } + } + extension UIViewController { @available(iOS, deprecated: 17, renamed: "traitCollection.push") @available(macOS, deprecated: 14, renamed: "traitCollection.push")