求助:Swift中实现交互式Push View Controller的方案
实现从右侧边缘拖动的交互式Push View Controller
方案一:纯Swift手动实现交互式Push
你当前的代码只是触发push动作,没有跟随拖动进度的交互动画。要实现带进度的交互式push,需要自定义导航控制器的转场逻辑,结合手势进度控制动画:
1. 自定义导航控制器(处理手势与转场)
class InteractivePushNavController: UINavigationController, UINavigationControllerDelegate { private var interactiveTransition: UIPercentDrivenInteractiveTransition? override func viewDidLoad() { super.viewDidLoad() delegate = self } // 指定push转场的动画器 func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return operation == .push ? PushTransitionAnimator() : nil } // 关联交互式转场 func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return interactiveTransition } // 给子控制器添加右侧边缘拖动手势 func setupRightEdgePan(for vc: UIViewController) { let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) pan.edges = .right vc.view.addGestureRecognizer(pan) } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let progress = abs(translation.x) / view.bounds.width switch gesture.state { case .began: interactiveTransition = UIPercentDrivenInteractiveTransition() // 让子控制器提供要push的目标VC if let targetVC = (topViewController as? PushTargetProvider)?.getTargetViewController() { pushViewController(targetVC, animated: true) } case .changed: interactiveTransition?.update(progress) case .ended, .cancelled: let velocity = gesture.velocity(in: view) // 进度过半或拖动速度足够时完成push,否则取消 if progress > 0.5 || velocity.x < -500 { interactiveTransition?.finish() } else { interactiveTransition?.cancel() } interactiveTransition = nil default: interactiveTransition?.cancel() interactiveTransition = nil } } } // 协议:让子控制器提供要push的目标页面 protocol PushTargetProvider { func getTargetViewController() -> UIViewController? }
2. 自定义转场动画器
class PushTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.35 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else { transitionContext.completeTransition(false) return } let container = transitionContext.containerView toVC.view.frame = CGRect(x: container.bounds.width, y: 0, width: container.bounds.width, height: container.bounds.height) container.addSubview(toVC.view) UIView.animate(withDuration: transitionDuration(using: transitionContext)) { fromVC.view.frame = CGRect(x: -container.bounds.width * 0.3, y: 0, width: container.bounds.width, height: container.bounds.height) toVC.view.frame = container.bounds } finished: { success in fromVC.view.frame = container.bounds // 恢复原位置 transitionContext.completeTransition(success && !transitionContext.transitionWasCancelled) } } }
3. 在子控制器中使用
class SourceVC: UIViewController, PushTargetProvider { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white // 绑定导航控制器的手势 if let nav = navigationController as? InteractivePushNavController { nav.setupRightEdgePan(for: self) } } func getTargetViewController() -> UIViewController? { let targetVC = TargetVC() targetVC.view.backgroundColor = .lightGray targetVC.title = "目标页面" return targetVC } } class TargetVC: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "目标页面" } }
方案二:在Swift中使用RTInteractivePush
这个OC库可以直接在Swift中调用,只需两步:
1. 配置桥接文件
如果项目没有桥接文件,新建一个命名为你的项目名-Bridging-Header.h的文件,在Build Settings的Objective-C Bridging Header中设置文件路径,然后添加:
#import <RTInteractivePush/RTInteractivePush.h>
2. 启用交互式push
在导航控制器中开启功能并设置触发边缘:
// 示例在AppDelegate中配置 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let nav = UINavigationController(rootViewController: SourceVC()) nav.interactivePushEnabled = true nav.interactivePushEdge = .right // 设置为右侧边缘触发 window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = nav window?.makeKeyAndVisible() return true }
或者在单个控制器的viewDidLoad中设置:
override func viewDidLoad() { super.viewDidLoad() navigationController?.interactivePushEnabled = true navigationController?.interactivePushEdge = .right }
内容的提问来源于stack exchange,提问作者A5omic
相关产品推荐
相关产品推荐

