Swift中如何将CollectionView单元格选中状态传递至ViewController
嘿,我来帮你搞定这个问题!你已经在自定义StoryViewCell里监听到了isSelected的变化,但没法把这个状态传递到负责数据保存的ViewController,这里有几个靠谱的方案,你可以根据自己的场景选择:
方案一:闭包(Closure)—— 轻量直观的选择
这个方案很适合简单的场景,代码量少还直观。你可以在自定义单元格里定义一个闭包属性,当isSelected状态变化时触发闭包,ViewController在创建单元格的时候绑定这个闭包就能拿到状态了。
首先修改你的StoryViewCell:
class StoryViewCell: UICollectionViewCell { // 定义闭包属性,用来传递选中状态 var selectionHandler: ((Bool) -> Void)? override var isSelected: Bool { didSet { print("Selected") // 触发闭包,把当前选中状态传出去 selectionHandler?(isSelected) } } }
然后在ViewController的cellForItemAt方法里绑定闭包:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryViewCell", for: indexPath) as! StoryViewCell // 绑定闭包,拿到选中状态后处理数据保存 cell.selectionHandler = { [weak self] isSelected in guard let self = self else { return } // 这里可以根据indexPath找到对应的数据模型,更新状态 self.yourDataArray[indexPath.item].isSelected = isSelected // 调用数据保存方法 self.saveData() } return cell }
注意用
[weak self]避免循环引用哦!
方案二:代理模式(Delegate)—— 适合复杂交互场景
如果你的项目里有很多类似的单元格交互,代理模式会更清晰,扩展性也更强。
首先定义一个代理协议:
protocol StoryViewCellDelegate: AnyObject { func storyViewCell(_ cell: StoryViewCell, didChangeSelection isSelected: Bool) }
然后修改StoryViewCell:
class StoryViewCell: UICollectionViewCell { // 定义代理属性,记得用weak避免循环引用 weak var delegate: StoryViewCellDelegate? override var isSelected: Bool { didSet { print("Selected") // 通知代理状态变化 delegate?.storyViewCell(self, didChangeSelection: isSelected) } } }
接下来让ViewController遵循这个代理协议,并实现方法:
class YourViewController: UIViewController, StoryViewCellDelegate { // ...其他代码 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryViewCell", for: indexPath) as! StoryViewCell // 设置代理为当前ViewController cell.delegate = self return cell } // 实现代理方法,处理选中状态变化 func storyViewCell(_ cell: StoryViewCell, didChangeSelection isSelected: Bool) { // 通过cell拿到对应的indexPath guard let indexPath = yourCollectionView.indexPath(for: cell) else { return } // 更新数据模型状态 yourDataArray[indexPath.item].isSelected = isSelected // 保存数据 saveData() } }
方案三:利用UICollectionView内置代理方法—— 符合UIKit设计规范
其实UICollectionView本身就提供了处理选中和取消选中的代理方法,完全不用在单元格里额外监听isSelected,直接在ViewController里实现这些方法就好,这也是UIKit推荐的做法。
首先确保你的ViewController遵循UICollectionViewDelegate,然后实现以下方法:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // 标记对应数据为选中状态 yourDataArray[indexPath.item].isSelected = true // 调用保存方法 saveData() } func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { // 标记对应数据为未选中状态 yourDataArray[indexPath.item].isSelected = false // 调用保存方法 saveData() }
如果需要支持多选,记得把collectionView的
allowsMultipleSelection属性设为true哦!
方案四:通知中心(NotificationCenter)—— 不推荐但可用
这个方案耦合性比较高,除非必要不建议使用,但还是提一下:
在StoryViewCell的isSelected didSet里发送通知:
override var isSelected: Bool { didSet { print("Selected") // 发送通知,携带选中状态和cell信息 NotificationCenter.default.post( name: NSNotification.Name("StoryCellSelectionChanged"), object: self, userInfo: ["isSelected": isSelected] ) } }
然后在ViewController里注册通知(记得在deinit里移除):
class YourViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 注册通知 NotificationCenter.default.addObserver( self, selector: #selector(handleSelectionChange(_:)), name: NSNotification.Name("StoryCellSelectionChanged"), object: nil ) } @objc func handleSelectionChange(_ notification: Notification) { guard let cell = notification.object as? StoryViewCell, let isSelected = notification.userInfo?["isSelected"] as? Bool, let indexPath = yourCollectionView.indexPath(for: cell) else { return } yourDataArray[indexPath.item].isSelected = isSelected saveData() } deinit { // 移除通知,避免内存泄漏 NotificationCenter.default.removeObserver(self) } }
个人更推荐方案一或者方案三,方案一灵活轻量,方案三符合系统设计,代码更简洁。根据你的项目需求选就行啦!
内容的提问来源于stack exchange,提问作者Max Kraev

