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

如何重写scrollViewDidEndDecelerating并获取当前可见Cell?

问题描述

我有一个包含UICollectionView的视图控制器,代码如下:

class TagViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { 
    @IBOutlet weak var collectionView: UICollectionView! 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
        return posts.count 
    } 

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
        let post = posts[indexPath.row] 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SNPostViewCell 
        cell.isVideo = post.isVideo 
        cell.mediaURL = URL(string: post.mediaURL) 
        cell.backgroundColor = UIColor.black 
        return cell 
    } 

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 
        let post = posts[indexPath.row] 
        if post.isVideo == true { 
            self.performSegue(withIdentifier: "playVideo", sender: nil) 
        } else { 
            print("is image. might go full screen one day here") 
        } 
        let bin = self.bins[post.timeStamp] 
        print(bin) 
    } 

    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 
        //Error: method cannot override from its superclass 
        <#code#> 
    } 
} 

虽然我能重写那些UICollectionView相关方法,但重写scrollViewDidEndDecelerating时提示无法重写父类方法。

于是我创建了一个继承自UICollectionViewController的自定义集合视图控制器,这里能正确重写scrollViewDidEndDecelerating方法,但遇到了新问题:我需要获取当前Cell的索引,而这个索引对应的数据源(比如posts数组)只在原来的TagViewController里定义,我不知道该把这个重写方法放在哪里,以及如何成功获取对应的数据索引。

自定义集合视图控制器的代码片段:

class PagingCollectionViewController: UICollectionViewController { 
    ... 
    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 
        if (self.collectionView.contentOffset.y < 0) { 
            self.collectionView.contentOffset = CGPointMake(self.collectionView.contentOffset.x, 0.0); 
        } 
        for cell in yourCollectionViewname.visibleCells() as [UICollectionViewCell] { 
            let indexPath = yourCollectionViewname.indexPathForCell(cell as UICollectionViewCell) 
        } 
    } 
    ... 
} 
解决方案

其实你完全不用切换到UICollectionViewController就能解决问题!先给你理清第一个报错的原因:scrollViewDidEndDecelerating不是UIViewController的父类方法,它是UIScrollViewDelegate协议里的方法——而UICollectionViewDelegate已经继承了UIScrollViewDelegate,所以你只需要去掉override关键字,直接实现这个方法就行!

方案一:直接在原ViewController中处理滚动事件

这是最省心的方案,不用重构代码,还能直接访问当前视图控制器里的数据源:

class TagViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { 
    @IBOutlet weak var collectionView: UICollectionView! 

    // 其他已有的方法保持不变...

    // 去掉override,直接实现UIScrollViewDelegate的方法
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 
        // 先处理偏移量的修正逻辑
        if scrollView.contentOffset.y < 0 {
            scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
        }
        
        // 获取当前可见的cell及其索引
        for cell in collectionView.visibleCells {
            if let indexPath = collectionView.indexPath(for: cell) {
                // 直接用你的posts数组获取对应数据
                let post = posts[indexPath.row]
                print("当前可见的帖子: \(post)")
                
                // 也能直接访问bins字典
                if let bin = bins[post.timeStamp] {
                    print("对应的bin数据: \(bin)")
                }
            }
        }
    } 
} 

方案二:如果坚持使用UICollectionViewController

如果你还是想基于UICollectionViewController来实现,那只需要给它添加数据源属性,把原视图控制器里的数据传递过去即可:

首先修改自定义集合视图控制器:

// 替换PostType和BinType为你实际的类型
class PagingCollectionViewController: UICollectionViewController { 
    // 添加数据源属性
    var posts: [PostType] = []
    var bins: [String: BinType] = [:]

    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 
        if scrollView.contentOffset.y < 0 {
            scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
        }
        
        for cell in collectionView.visibleCells {
            if let indexPath = collectionView.indexPath(for: cell) {
                let post = posts[indexPath.row]
                print("当前帖子: \(post)")
                
                if let bin = bins[post.timeStamp] {
                    print("对应的bin数据: \(bin)")
                }
            }
        }
    } 

    // 别忘了实现UICollectionView的数据源方法,逻辑和原TagViewController一致
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return posts.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let post = posts[indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuseIdentifier", for: indexPath) as! SNPostViewCell
        cell.isVideo = post.isVideo
        cell.mediaURL = URL(string: post.mediaURL)
        cell.backgroundColor = .black
        return cell
    }
}

然后在TagViewController里跳转或初始化这个控制器时,传递数据:

// 比如在segue跳转时传递
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let pagingVC = segue.destination as? PagingCollectionViewController {
        pagingVC.posts = self.posts
        pagingVC.bins = self.bins
    }
}

// 如果是代码创建控制器
let pagingVC = PagingCollectionViewController(collectionViewLayout: UICollectionViewFlowLayout())
pagingVC.posts = self.posts
pagingVC.bins = self.bins
navigationController?.pushViewController(pagingVC, animated: true)

总结

优先选方案一,因为它不需要重构现有代码,还能直接访问原视图控制器的数据源,避免了数据传递的麻烦。核心就是记住:scrollViewDidEndDecelerating是协议方法,不是父类的重写方法,所以不用加override关键字。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:41:59