如何实现ViewController左边缘拖拽返回 支持悬浮与动画恢复效果?
实现左滑拖拽关闭ViewController的方案
核心思路
通过自定义UIPanGestureRecognizer监听左侧边缘的拖拽动作,根据拖拽距离和状态实时调整视图位置,松手后判断是否达到滑动阈值,决定关闭视图或恢复全屏状态,完全匹配App Store的交互逻辑。
具体实现步骤
1. 封装通用手势逻辑(给任意ViewController扩展)
创建UIViewController的扩展,把手势添加、拖拽处理逻辑封装成通用方法,任意ViewController都能直接调用:
import UIKit extension UIViewController { func addLeftSwipeDismissGesture() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handleLeftSwipeDismiss(_:))) panGesture.delegate = self view.addGestureRecognizer(panGesture) } @objc private func handleLeftSwipeDismiss(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let screenWidth = UIScreen.main.bounds.width // 仅响应向右拖拽(左滑动作对应视图右移) guard translation.x >= 0 else { return } switch gesture.state { case .began: // 开始拖拽时清除现有动画,避免冲突 view.layer.removeAllAnimations() case .changed: // 实时跟随拖拽动作更新视图状态 let translateTransform = CGAffineTransform(translationX: translation.x, y: 0) // 附加透明度和缩放效果,增强悬浮视觉反馈 let scaleFactor = 1 - (translation.x / screenWidth) * 0.1 view.transform = translateTransform.scaledBy(x: scaleFactor, y: scaleFactor) view.alpha = 1 - (translation.x / screenWidth) * 0.3 case .ended, .cancelled: let dismissThreshold = screenWidth * 0.3 if translation.x >= dismissThreshold { // 滑动超过阈值,执行关闭动画 UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { self.view.transform = CGAffineTransform(translationX: screenWidth, y: 0) self.view.alpha = 0 } completion: { _ in // 根据视图呈现方式选择关闭逻辑 if self.presentingViewController != nil { self.dismiss(animated: false) } else if self.navigationController != nil { self.navigationController?.popViewController(animated: false) } } } else { // 未达阈值,动画恢复全屏 UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) { self.view.transform = .identity self.view.alpha = 1 } } default: break } } } // 实现代理,限制仅从左侧边缘触发手势 extension UIViewController: UIGestureRecognizerDelegate { public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else { return true } let touchPoint = panGesture.location(in: view.window) // 仅允许屏幕左侧20pt范围内的触摸触发手势 return touchPoint.x <= 20 } }
2. 在目标ViewController中启用手势
只需在ViewController的viewDidLoad中调用一行代码即可:
override func viewDidLoad() { super.viewDidLoad() addLeftSwipeDismissGesture() }
关键细节说明
- 触发范围控制:通过
gestureRecognizerShouldBegin限制仅左侧边缘20pt内的触摸能触发手势,避免页面内其他拖拽操作冲突。 - 拖拽反馈优化:拖拽过程中同步调整视图的位移、缩放和透明度,模拟App Store的悬浮拖拽质感。
- 阈值可调:默认以屏幕宽度30%作为关闭阈值,可根据产品需求修改
dismissThreshold的比例。 - 动画适配:关闭和恢复动画使用
curveEaseOut曲线,保证过渡自然;关闭动画完成后再执行dismiss/pop,避免动画错位。
内容的提问来源于stack exchange,提问作者Mel
相关产品推荐
相关产品推荐

