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

如何实现UICollectionView左滑无限制,临时禁用右滑选中答案后恢复?

实现UICollectionView的滑动限制:必须完成当前页作答才能右滑

这个需求在答题类APP里很常见,我之前做类似项目时也遇到过,核心思路是借助UICollectionView的父类UIScrollView的代理方法,拦截滚动行为并区分滑动方向——针对向右滑动(进入下一页)做权限校验,向左滑动则完全放开限制。

核心实现步骤

  1. 开启UICollectionView的分页模式,确保每页占满屏幕
  2. 维护当前页码和每页的作答状态
  3. 通过UIScrollView代理方法拦截滚动,判断方向并校验权限
  4. 用户完成作答后更新状态,允许向右滑动

完整代码示例

import UIKit

// 自定义Cell的回调协议,用于通知ViewController用户选中了答案
protocol AnswerCellDelegate: AnyObject {
    func userDidSubmitAnswer(in cell: AnswerCollectionViewCell)
}

class AnswerCollectionViewCell: UICollectionViewCell {
    weak var delegate: AnswerCellDelegate?
    
    // 假设这是用户选中答案的按钮或交互控件的点击事件
    @IBAction func selectAnswerButtonTapped(_ sender: UIButton) {
        delegate?.userDidSubmitAnswer(in: self)
    }
}

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate, AnswerCellDelegate {
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    // 总页数,替换成你的实际页数
    private let totalPages = 5
    // 记录每页是否已作答
    private var pageAnswerStatus: [Bool] = []
    // 当前页码
    private var currentPageIndex = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 初始化作答状态数组,默认所有页未作答
        pageAnswerStatus = Array(repeating: false, count: totalPages)
        
        // 配置CollectionView
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.isPagingEnabled = true
        collectionView.showsHorizontalScrollIndicator = false
        
        // 设置FlowLayout,确保每个Cell占满屏幕
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.itemSize = view.bounds.size
        flowLayout.minimumLineSpacing = 0
        flowLayout.scrollDirection = .horizontal
        collectionView.collectionViewLayout = flowLayout
        
        // 注册Cell(如果是Storyboard创建可忽略)
        collectionView.register(AnswerCollectionViewCell.self, forCellWithReuseIdentifier: "AnswerCell")
    }
    
    // MARK: - UICollectionView DataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return totalPages
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnswerCell", for: indexPath) as! AnswerCollectionViewCell
        cell.delegate = self
        // 可以根据pageAnswerStatus[indexPath.item]配置Cell的状态(比如显示已作答标记)
        return cell
    }
    
    // MARK: - UIScrollView Delegate
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let currentOffsetX = scrollView.contentOffset.x
        let targetOffsetX = targetContentOffset.pointee.x
        let pageWidth = scrollView.bounds.width
        
        // 判断滑动方向:目标偏移大于当前偏移 → 向右滑动(下一页)
        if targetOffsetX > currentOffsetX {
            // 检查当前页是否已作答
            if !pageAnswerStatus[currentPageIndex] {
                // 阻止滚动,强制拉回当前页位置
                targetContentOffset.pointee = CGPoint(x: currentOffsetX, y: 0)
                // 提示用户必须完成当前页作答
                showAnswerRequiredTip()
            } else {
                // 允许滚动,更新当前页码
                currentPageIndex = Int((currentOffsetX + pageWidth) / pageWidth)
            }
        } else if targetOffsetX < currentOffsetX {
            // 向左滑动(上一页),直接允许,更新当前页码
            let previousPageIndex = Int((currentOffsetX - pageWidth) / pageWidth)
            currentPageIndex = max(previousPageIndex, 0) // 避免页码小于0
        }
    }
    
    // 滚动结束后确保页码准确(处理快速滚动等边界情况)
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        currentPageIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width)
    }
    
    // MARK: - AnswerCellDelegate
    func userDidSubmitAnswer(in cell: AnswerCollectionViewCell) {
        // 获取当前Cell的索引
        guard let indexPath = collectionView.indexPath(for: cell) else { return }
        // 更新该页的作答状态
        pageAnswerStatus[indexPath.item] = true
        // 可以给用户一个完成提示,比如Cell上打个勾
    }
    
    // 提示用户必须完成当前页作答
    private func showAnswerRequiredTip() {
        let alert = UIAlertController(title: "温馨提示", message: "请先完成当前页面的作答哦", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "知道了", style: .default))
        present(alert, animated: true)
    }
}

关键细节说明

  • 滚动拦截逻辑:通过scrollViewWillEndDragging方法修改targetContentOffset,这是最精准的拦截方式,不会影响用户的滑动手感,只是在不允许滚动时将页面拉回原位。
  • 作答状态维护:用数组pageAnswerStatus记录每一页的作答情况,比单个布尔值更灵活,支持用户返回上一页后再次查看已作答的页面。
  • 页码准确性:在scrollViewDidEndDecelerating里更新当前页码,避免快速滚动导致的页码计算错误。
  • 用户体验:拦截滚动时弹出提示,让用户明确知道不能滑动的原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:40:19