Viper架构中处理UICollectionViewCell交互事件的最佳实现方案是什么
Viper架构下UICollectionViewCell交互事件实现方案
核心规则:Viper中所有用户交互事件最终由Presenter处理,Cell、ViewController都属于View层,仅负责事件捕获和转发,不包含任何业务逻辑。
1. 定义Cell的事件出口
可以选择闭包或者代理两种方式,这里优先推荐更简洁的闭包方案,替换MVC中Cell的代理协议:
class StoriesCollectionViewCell: UICollectionViewCell { var story: Story? // 点击事件回调,将需要的参数对外抛出 var didTapUserCallback: ((User) -> Void)? // 内部捕获Cell上的点击事件,比如用户头像的点击手势触发后调用 @objc private func userAvatarTapped() { guard let user = story?.user else { return } didTapUserCallback?(user) } }
如果习惯用代理实现,逻辑和MVC中一致,只是代理的实现方ViewController收到事件后不处理,直接转发即可。
2. 扩展Presenter的输入协议,新增事件处理方法
在StoriesPresenterInput协议中新增对应交互的处理入口:
protocol StoriesPresenterInput: AnyObject { func viewDidLoad() // 新增处理用户点击事件的方法 func didSelectUser(user: User) }
3. ViewController绑定Cell事件,转发给Presenter
修改cellForItemAt方法,绑定Cell的回调,收到事件后直接转发给Presenter:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "storiesCell", for: indexPath) as! StoriesCollectionViewCell cell.story = stories[indexPath.item] // 注意加[weak self]避免循环引用 cell.didTapUserCallback = { [weak self] user in self?.presenter?.didSelectUser(user: user) } return cell }
4. Presenter实现业务逻辑
Presenter中实现对应的事件处理方法,根据业务需求调用Router做页面跳转,或者调用Interactor做数据请求、埋点等操作:
class StoriesPresenter: StoriesPresenterInput { weak var view: StoriesViewOutput? var router: StoriesRouterProtocol? var interactor: StoriesInteractorInput? func didSelectUser(user: User) { // 示例1:调用路由跳转到用户主页 router?.pushToUserProfilePage(user: user) // 示例2:调用交互器记录点击埋点 interactor?.trackUserClickEvent(user: user) } }
优化建议
如果Cell上有多个交互事件(比如点赞、点击内容、点击用户),可以把事件封装成枚举统一传递,避免定义多个闭包属性:
// 定义Cell所有的交互事件类型 enum StoryCellAction { case tapUser(User) case tapLike(Story) case tapContent(Story) } // Cell中统一用一个回调传递事件 var actionCallback: ((StoryCellAction) -> Void)?
内容的提问来源于stack exchange,提问作者Bobby Redjeans
相关产品推荐
相关产品推荐

