IOS10 CollectionView 新的复用机制之坑:动态修改Cell内容

实习过程中遇到的坑,写下来分享和备用。
开发环境为XCode 8.2.1 Swift 3.0

简化一下需要实现的效果为:点击导航栏上的按钮后修改Cell上某个控件的状态,在Demo里将它简化为修改Cell的背景色。看上去是一个很简单的实现,那么先用本以为可行的方法在Demo中实现:

CollectionViewController中部分的代码:

    private var colorChange: UIColor?

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
        guard let colorChange = colorChange else { return cell }
        // Cell被复用时修改颜色
        cell.backgroundColor = colorChange
        return cell
    }
    
    func changeCell(with color: UIColor) {
        guard let collectionView = collectionView else { return }
        colorChange = color
        // 修改屏幕中显示的Cell的颜色
        for cell in collectionView.visibleCells {
            cell.backgroundColor = color
        }
    }

外部调用的代码:

    // 导航栏color按钮点击回调
    @IBAction func changeClick(_ sender: Any) {
        guard let collectionVC = collectionVC else { return }
        collectionVC.changeCell(with: UIColor.blue)
    }

通过修改可见Cell以及在复用方法中加入修改颜色的操作来保证所有Cell的颜色均被修改,接着在模拟器中运行:

IOS10 CollectionView 新的复用机制之坑:动态修改Cell内容_第1张图片
d_1.gif

可以看到其中有一排的Cell颜色并没有改变,当我们将那排Cell移出屏幕后再拖会才看到它的颜色发生了应有的变化。这说明那排的Cell并没有进行复用,也没有被visibleCells的取到。

本着实现需求第一的要义,我们先来想办法把切换所有Cell颜色的效果做出来。首先可以肯定那一排Cell没有走复用方法,意味着他们没有进入复用队列,既然没有进入复用队列说明它们一定还在屏幕上,那么试着取出屏幕上collectionview所有的subviews来获取这些Cell,修改changeCell方法:

    func changeCell(with color: UIColor) {
        guard let collectionView = collectionView else { return }
        colorChange = color
        // 修改部分:
        for subview in collectionView.subviews {
            if let cell = subview as? UICollectionViewCell {
                cell.backgroundColor = color
            }
        }
    }

经测试,修改这部分代码后所有的Cell都会被修改颜色,那么至少在效果实现上我们已经成功了。但是使用subviews并不是一件很文明的方式,我又尝试了用一个Set集合保存indexPath后再changeCell中遍历indexPahts来获取Cell的方法,但这么做的结果和visiableCells是相同的,就不展示代码过程了。

深究其中原因,得知是IOS 10中苹果修改了collectionview的复用机制,来保证更流畅的滑动效果,具体的修改部分可以自行搜索,对于这片文章中的坑有用的部分可以总结为: 离开屏幕的Cell并不会马上进入复用队列,而会被保存一段时间,在用户突然滑回屏幕的时候显示,这部分Cell不会再走一遍复用周期,以保证用户体验上的一个提升。

我暂时没有想到subviews以外的方法在新的复用机制下取到离屏却没有进入复用队列那部分Cell的方式,如果不想用这种方法,或者说现在大部分的项目都还需要兼容IOS 9,我们可以设置isPrefetchingEnabled 为false的方法来关闭新的复用机制(其实叫 预加载)

    override func viewDidLoad() {
        if #available(iOS 10.0, *) {
            collectionView?.isPrefetchingEnabled = false
        }
    }

这样设定之后离开屏幕的Cell便会马上进入复用队列了。但同时也就失去了新机制带来的更好的滑动体验,具体抉择可以根据项目自行考虑。

你可能感兴趣的:(IOS10 CollectionView 新的复用机制之坑:动态修改Cell内容)