You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:20:17