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

如何通过UIPanGestureRecognizer调整contentView的高度?

通过UIPanGestureRecognizer固定底部调整contentView高度

问题背景

你的contentView已设置Auto Layout约束(trailing、leading、bottom固定,高度固定),并嵌入了子视图控制器。当前拖动时整个contentView会移动,需要实现底部固定,仅通过手势调整高度的效果。

解决方案步骤

  1. 关联高度约束的IBOutlet:在Storyboard中把contentView的固定高度约束拖到代码中,作为可修改的属性。
  2. 记录初始高度:手势开始时保存当前的高度约束值,而非视图的center。
  3. 动态调整高度约束:根据手势的拖动偏移量计算新高度,同时限制高度的最小/最大值。
  4. 手势结束的回弹处理:根据拖动速度判断是否回弹到预设的极限高度,并用动画过渡。

修改后的完整代码

import UIKit

class YourViewController: UIViewController {
    // 连接Storyboard中contentView的高度约束
    @IBOutlet weak var contentViewHeightConstraint: NSLayoutConstraint!
    // 子视图控制器(根据你的实际代码定义)
    var childViewController: UIViewController!
    
    // 可根据需求自定义最小/最大高度
    private let minContentViewHeight: CGFloat = 100
    private let maxContentViewHeight: CGFloat = 400
    private var initialHeight: CGFloat = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        prepareGesture()
    }
    
    func prepareGesture() {
        let gesture = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:)))
        // 将手势添加到子视图控制器的view上,确保能接收拖动事件
        childViewController.view.addGestureRecognizer(gesture)
    }

    @objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
        let translation = gestureRecognizer.translation(in: childViewController.view).y
        let velocity = gestureRecognizer.velocity(in: childViewController.view).y
        
        switch gestureRecognizer.state {
        case .began: gestureBegan()
        case .changed: gestureChanged(translation: translation)
        case .cancelled, .ended: gestureEnded(velocity: velocity)
        default: break
        }
    }

    func gestureBegan() {
        // 记录手势开始时的初始高度
        initialHeight = contentViewHeightConstraint.constant
    }
    
    func gestureChanged(translation: CGFloat) {
        // 计算新高度:向上拖动(translation.y为负)增加高度,向下拖动(translation.y为正)减少高度
        var newHeight = initialHeight - translation
        // 限制高度在最小/最大值范围内,避免超出布局边界
        newHeight = max(minContentViewHeight, min(newHeight, maxContentViewHeight))
        // 更新高度约束
        contentViewHeightConstraint.constant = newHeight
        // 实时刷新布局
        view.layoutIfNeeded()
    }

    func gestureEnded(velocity: CGFloat) {
        var targetHeight = contentViewHeightConstraint.constant
        
        // 根据拖动速度判断回弹逻辑
        if velocity < -300 {
            // 快速向上拖,自动展开到最大高度
            targetHeight = maxContentViewHeight
        } else if velocity > 300 {
            // 快速向下拖,自动收缩到最小高度
            targetHeight = minContentViewHeight
        } else {
            // 慢速拖动时,根据当前高度自动贴近最近的极限值
            let midHeight = (minContentViewHeight + maxContentViewHeight) / 2
            targetHeight = targetHeight > midHeight ? maxContentViewHeight : minContentViewHeight
        }
        
        // 动画过渡到目标高度
        UIView.animate(withDuration: 0.3) {
            self.contentViewHeightConstraint.constant = targetHeight
            self.view.layoutIfNeeded()
        }
    }
}

关键说明

  • 约束优先:始终通过修改Auto Layout约束的constant值来调整高度,这样能保证bottom约束持续生效,contentView底部固定不动。
  • 坐标系映射:iOS中y轴向下为正,所以向上拖动时translation.y为负数,用initialHeight - translation计算新高度,符合“上拖增高、下拖减矮”的直觉。
  • 边界限制:必须设置最小/最大高度,防止contentView高度异常导致布局错乱。
  • 动画生效:约束更新后要在动画闭包中调用layoutIfNeeded(),才能让高度变化以动画形式呈现。

内容的提问来源于stack exchange,提问作者mb0

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:36:26