首先,需求是做一个选择颜色的小组件。如下图所示
需要滑动展示,8种颜色,一页显示4个,共两页。
我选择了UICollectionView来做。因为颜色图片是直接用Graphics画的,就直接把绘制出来的CGImage赋值给了UICollectionViewCell的contentView.layer.contents属性。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: UICollectionViewCell.reuseIdentifier, for: indexPath)
let colorType = viewModel!.colors[indexPath.section][indexPath.item]
if colorType == viewModel!.selectedColor {
cell.contentView.layer.contents = cgImage(for: .selected, colorType: colorType)
}else {
cell.contentView.layer.contents = cgImage(for: .normal, colorType: colorType)
}
return cell
}
主要的代理方法如下:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.contentView.layer.contents = cgImage(for: .selected,
colorType: viewModel!.colors[indexPath.section][indexPath.item])
viewModel!.selectedColor = viewModel!.colors[indexPath.section][indexPath.item]
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.contentView.layer.contents = cgImage(for: .normal,
colorType: viewModel!.colors[indexPath.section][indexPath.item])
}
看上去很完美,我也是这么认为的。可当我愉快的选来选去的时候,出现了这种情况:
我第一反应是,难道didDeselect方法没有执行?经过断点发现,这个方法是执行了,indexPath也是正确传递了上一次被选中cell的位置。但是在didDeselect方法中使用
collectionView.cellForItem(at: indexPath)
得到的结果是nil。出现不知道的问题了怎么办?看文档。
cellForItem(at:)
的官方说明是 "Returns the visible cell object at the specified index path."好吧,我承认我的基础知识很烂。我是个智障。。。
因为集合视图会创建最大可见cell数 maxVisibled+1个cell,滑动中,超出这个范围的cell会被放进缓冲区,即将出现的第maxVisibled+2个cell才会调用
cellForItemAt indexPath:
方法获取,可能从缓冲池取,可能重新创建(缓冲池里没有可复用的cell时)。但是本例中 [1, 0] 和 [0, 3] 这两个位置的cell很尴尬,因为它们永远不会被放进缓冲区,不会被重新获取,状态就不会被重置。cellForItem(at: indexPath)
又无法取到不可见的cell,所以它们一旦被选中就无法取消了(除非在本页中再次选中它,再在本页中选中其他cell)。解决办法也很简单,增加一个变量持有被选中的cell来维护是否选中状态就行了。不说了,我看文档去了。。。
好吧,我又回来了。。。
我发现压根不用这么麻烦。。直接重写UICollectionViewCell的 isSelected 属性的didSet方法,在这个方法里更换图片就行了。。。因为这里的UICollectionView的allowsMultipleSelection是false,并不允许多选,所以选择其他cell时,会自动取消上一个被选中cell的选中状态。。。