152 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Swift
		
	
	
	
		
		
			
		
	
	
			152 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Swift
		
	
	
	
|  | // | |||
|  | //  CLNavigationController.swift | |||
|  | //  Crush | |||
|  | // | |||
|  | //  Created by Leon on 2025/7/12. | |||
|  | // | |||
|  | 
 | |||
|  | import UIKit | |||
|  | 
 | |||
|  | protocol NavigationControllerDelegate: NSObjectProtocol { | |||
|  |     func popGestureRecognizerShouldBegin() -> Bool | |||
|  | } | |||
|  | 
 | |||
|  | extension NavigationControllerDelegate { | |||
|  |     func popGestureRecognizerShouldBegin() -> Bool { | |||
|  |         return true | |||
|  |     } | |||
|  | } | |||
|  | 
 | |||
|  | class CLNavigationController: UINavigationController { | |||
|  | 
 | |||
|  |     weak var popDelegate: NavigationControllerDelegate? | |||
|  | 
 | |||
|  |     private(set) var pan: UIPanGestureRecognizer! | |||
|  | 
 | |||
|  |     override func viewDidLoad() { | |||
|  |         super.viewDidLoad() | |||
|  |         navigationBar.isHidden = true | |||
|  |         modalPresentationStyle = .fullScreen | |||
|  |         addFullScreenPan() | |||
|  |     } | |||
|  | 
 | |||
|  |     override func pushViewController(_ viewController: UIViewController, animated: Bool) { | |||
|  |         if viewControllers.count > 0 { | |||
|  |             if viewController is CLBaseViewController { | |||
|  |                 let vc = viewController as! CLBaseViewController | |||
|  |                 vc.hiddenBackButton(of: false) | |||
|  |             } | |||
|  |             viewController.hidesBottomBarWhenPushed = true | |||
|  |         } | |||
|  |         // 如果viewController已经在navigationStack中,则不进行push | |||
|  |         if viewControllers.contains(viewController) { | |||
|  |             return | |||
|  |         } | |||
|  | 
 | |||
|  |         super.pushViewController(viewController, animated: animated) | |||
|  |     } | |||
|  | 
 | |||
|  |     override open var childForStatusBarStyle: UIViewController? { | |||
|  |         return topViewController | |||
|  |     } | |||
|  | 
 | |||
|  |     private func addFullScreenPan() { | |||
|  |         // 1.获取系统的Pop手势 | |||
|  |         guard let systemGes = interactivePopGestureRecognizer else { return } | |||
|  |         // 2.获取手势添加到的View中 | |||
|  |         guard let gesView = systemGes.view else { return } | |||
|  |         // 3.取出target | |||
|  |         let targets = systemGes.value(forKey: "_targets") as? [NSObject] | |||
|  |         guard let targetObjc = targets?.first else { return } | |||
|  |         guard let target = targetObjc.value(forKey: "target") else { return } | |||
|  |         // 4.取出action | |||
|  |         let action = Selector(("handleNavigationTransition:")) | |||
|  |         // 5. 创建自己的pan手势 | |||
|  |         pan = UIPanGestureRecognizer() | |||
|  |         gesView.addGestureRecognizer(pan) | |||
|  |         pan.addTarget(target, action: action) | |||
|  |         pan.delegate = self | |||
|  |     } | |||
|  | 
 | |||
|  |     /// 关闭全屏 保留系统手势 | |||
|  |     func disabledFullScreenPan() { | |||
|  |         pan.isEnabled = false | |||
|  |         interactivePopGestureRecognizer?.delegate = self | |||
|  |     } | |||
|  | 
 | |||
|  |     /// 开启全屏 | |||
|  |     func enabledFullScreenPan() { | |||
|  |         pan.isEnabled = true | |||
|  |     } | |||
|  | 
 | |||
|  |     /// 关闭全屏 和 系统 | |||
|  |     func disabledPopGesture() { | |||
|  |         pan.isEnabled = false | |||
|  |         interactivePopGestureRecognizer?.isEnabled = false | |||
|  |     } | |||
|  | 
 | |||
|  |     /// 开启全屏 和 系统 | |||
|  |     func enabledPopGesture() { | |||
|  |         pan.isEnabled = true | |||
|  |         interactivePopGestureRecognizer?.isEnabled = true | |||
|  |     } | |||
|  | } | |||
|  | 
 | |||
|  | extension CLNavigationController: UIGestureRecognizerDelegate { | |||
|  |     func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { | |||
|  |         if viewControllers.count <= 1 { | |||
|  |             return false | |||
|  |         } | |||
|  |         if value(forKey: "_isTransitioning") as? Bool ?? false { | |||
|  |             return false | |||
|  |         } | |||
|  |         let translation = gestureRecognizer.location(in: gestureRecognizer.view) | |||
|  |         if translation.x <= 0 { | |||
|  |             return false | |||
|  |         } | |||
|  | 
 | |||
|  |         if let popDelegate = popDelegate { | |||
|  |             return popDelegate.popGestureRecognizerShouldBegin() | |||
|  |         } | |||
|  |         return true | |||
|  |     } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | // MARK: - 解决全屏滑动时的手势冲突 | |||
|  | 
 | |||
|  | extension UIScrollView: @retroactive UIGestureRecognizerDelegate { | |||
|  |     // 当UIScrollView在水平方向滑动到第一个时,默认是不能全屏滑动返回的,通过下面的方法可实现其滑动返回。 | |||
|  |     override open func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { | |||
|  |         if panBack(gestureRecognizer: gestureRecognizer) { | |||
|  |             return false | |||
|  |         } | |||
|  |         return true | |||
|  |     } | |||
|  | 
 | |||
|  |     public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool { | |||
|  |         if panBack(gestureRecognizer: gestureRecognizer) { | |||
|  |             return true | |||
|  |         } | |||
|  |         return false | |||
|  |     } | |||
|  | 
 | |||
|  |     func panBack(gestureRecognizer: UIGestureRecognizer) -> Bool { | |||
|  |         if gestureRecognizer == panGestureRecognizer { | |||
|  |             let point = panGestureRecognizer.translation(in: self) | |||
|  |             let state = gestureRecognizer.state | |||
|  | 
 | |||
|  |             // 设置手势滑动的位置距屏幕左边的区域 | |||
|  |             let locationDistance = UIScreen.main.bounds.size.width | |||
|  | 
 | |||
|  |             if state == UIGestureRecognizer.State.began || state == UIGestureRecognizer.State.possible { | |||
|  |                 let location = gestureRecognizer.location(in: self) | |||
|  |                 if point.x > 0, location.x < locationDistance, contentOffset.x <= 0 { | |||
|  |                     return true | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  |         return false | |||
|  |     } | |||
|  | } |