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

如何在禁用Bounce的ScrollView中添加下拉刷新功能

方案1:通过UIPanGestureRecognizer监听下拉手势

这种方法不依赖ScrollView的滚动能力,哪怕内容不足以滚动也能触发下拉检测。

  • 给ScrollView添加拖拽手势识别器,同时准备自定义刷新视图:
private var panStartY: CGFloat = 0
private let refreshThreshold: CGFloat = 60 // 触发刷新的下拉距离
private var isRefreshing = false
private var refreshView: UIActivityIndicatorView!

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.bounces = false
    
    // 添加自定义刷新视图到ScrollView顶部
    refreshView = UIActivityIndicatorView(style: .medium)
    refreshView.frame = CGRect(x: scrollView.center.x - 15, y: -60, width: 30, height: 30)
    scrollView.addSubview(refreshView)
    
    // 添加拖拽手势
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    panGesture.delegate = self
    scrollView.addGestureRecognizer(panGesture)
}
  • 实现手势处理逻辑:
@objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
    let translation = gesture.translation(in: scrollView)
    
    switch gesture.state {
    case .began:
        panStartY = scrollView.contentOffset.y
    case .changed:
        // 仅处理从顶部开始的下拉动作
        if panStartY <= 0 && translation.y > 0 {
            let pullDistance = min(translation.y, refreshThreshold)
            // 让刷新视图跟随下拉动作移动
            refreshView.transform = CGAffineTransform(translationX: 0, y: pullDistance)
            
            // 达到阈值时标记为准备刷新状态
            if pullDistance >= refreshThreshold && !isRefreshing {
                isRefreshing = true
                refreshView.startAnimating()
            }
        }
    case .ended, .cancelled:
        if isRefreshing {
            // 触发刷新逻辑
            performRefresh()
            // 固定刷新视图在顶部位置
            UIView.animate(withDuration: 0.3) {
                self.refreshView.transform = CGAffineTransform(translationX: 0, y: self.refreshThreshold)
            }
        } else {
            // 未达到阈值,回弹隐藏刷新视图
            UIView.animate(withDuration: 0.3) {
                self.refreshView.transform = .identity
            }
        }
    default:
        break
    }
}

// 解决手势冲突,允许ScrollView原有手势和自定义手势同时识别
extension YourViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}
  • 刷新完成后恢复状态:
private func performRefresh() {
    // 这里替换为你的实际刷新逻辑,比如网络请求
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        self.endRefreshing()
    }
}

private func endRefreshing() {
    isRefreshing = false
    refreshView.stopAnimating()
    UIView.animate(withDuration: 0.3) {
        self.refreshView.transform = .identity
    }
}
方案2:动态调整ScrollView的contentSize

让ScrollView始终具备微小的滚动能力,这样scrollViewDidScroll就能触发,同时保持bounces = false。

  • 初始化时设置contentSize:
scrollView.bounces = false
// 确保contentHeight至少比ScrollView高度大1pt,保证能触发滚动事件
let contentHeight = yourActualContentHeight // 比如tableView.contentSize.height或自定义内容高度
scrollView.contentSize = CGSize(width: scrollView.bounds.width, height: max(scrollView.bounds.height + 1, contentHeight))
  • 在scrollViewDidScroll中检测下拉偏移:
private let refreshThreshold: CGFloat = -60
private var isRefreshing = false

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // 下拉到阈值位置且未在刷新状态时,触发刷新
    if !isRefreshing && scrollView.contentOffset.y <= refreshThreshold {
        isRefreshing = true
        beginRefreshing()
    }
}

private func beginRefreshing() {
    // 固定ScrollView偏移,显示刷新状态
    UIView.animate(withDuration: 0.3) {
        self.scrollView.contentOffset = CGPoint(x: 0, y: self.refreshThreshold)
    }
    // 执行刷新逻辑
    performRefresh()
}

private func endRefreshing() {
    isRefreshing = false
    // 恢复ScrollView偏移
    UIView.animate(withDuration: 0.3) {
        self.scrollView.contentOffset = .zero
    }
}

private func performRefresh() {
    // 替换为你的实际刷新逻辑
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        self.endRefreshing()
    }
}

注意:这种方法需要确保contentSize始终大于ScrollView的bounds高度,哪怕内容为空也要设置成scrollView.bounds.height + 1,否则scrollViewDidScroll还是不会触发。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:44:59