Swift 4:无Storyboard下从Collection View Cell传值至另一视图控制器
无Storyboard下CollectionViewCell向HomeViewController传数据并复用模型的实现方案
嘿,这个问题在无Storyboard开发初期确实挺常见的,我来给你一步步拆解,保证能复用你的数据模型,还能顺畅完成数据传递~
前提确认:你的数据模型已经是独立可复用的
首先确保你的数据模型(比如叫ItemModel)是一个独立的结构体/类,这样不管是在CollectionViewCell还是HomeViewController里都能直接引用,比如:
// 可复用的数据模型类/结构体 struct ItemModel { let title: String let content: String let imageURL: String? // 其他你需要的属性 }
方案1:通过CollectionView的选中事件传递数据(最常用)
这种方式适用于点击整个Cell跳转的场景,步骤如下:
1. 在CollectionView所在的ViewController中实现选中代理方法
首先让你的VC遵守UICollectionViewDelegate协议,然后实现didSelectItemAt方法:
extension YourCollectionVC: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // 1. 获取选中Cell对应的数据模型 let selectedItem = yourDataSourceArray[indexPath.item] // 2. 无Storyboard方式初始化HomeViewController let homeVC = HomeViewController() // 3. 将模型数据传递给HomeVC homeVC.selectedItem = selectedItem // 4. 执行跳转(根据你的导航结构选push或present) navigationController?.pushViewController(homeVC, animated: true) // 如果是模态跳转:present(homeVC, animated: true) } }
注:yourDataSourceArray是你用来填充CollectionView的数据源数组,类型为[ItemModel]
2. 在HomeViewController中接收并使用数据
在HomeVC里定义一个接收数据的属性,然后在viewDidLoad中用这个数据填充UI:
class HomeViewController: UIViewController { // 定义接收数据的属性,类型就是你的复用模型 var selectedItem: ItemModel? // 假设你有这些UI元素(无Storyboard下需要自己创建布局) private let titleLabel = UILabel() private let contentLabel = UILabel() override func viewDidLoad() { super.viewDidLoad() setupUI() populateData() } // 初始化UI(无Storyboard下的UI创建逻辑) private func setupUI() { view.backgroundColor = .white // 配置titleLabel titleLabel.font = .systemFont(ofSize: 20, weight: .bold) titleLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(titleLabel) // 配置contentLabel contentLabel.font = .systemFont(ofSize: 16) contentLabel.numberOfLines = 0 contentLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(contentLabel) // 添加布局约束 NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16), contentLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), contentLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16) ]) } // 用传递过来的模型数据填充UI private func populateData() { guard let item = selectedItem else { return } titleLabel.text = item.title contentLabel.text = item.content // 如果有图片,还可以在这里加载imageURL对应的图片 } }
方案2:通过Cell内部按钮触发传递数据(如果是Cell内按钮跳转)
如果是点击Cell里的某个按钮(比如详情按钮)才跳转,那需要用闭包把事件从Cell传递到VC,步骤如下:
1. 在CollectionViewCell中定义闭包属性
class YourCollectionViewCell: UICollectionViewCell { // 定义闭包,用来传递点击事件和对应的数据模型 var didTapDetailButton: ((ItemModel) -> Void)? // 存储当前Cell对应的模型 private var currentItem: ItemModel? // 配置Cell的方法,接收模型数据 func configure(with item: ItemModel) { currentItem = item // 填充Cell的UI,比如设置label文本等 titleLabel.text = item.title } // 按钮点击事件 @IBAction func detailButtonTapped(_ sender: UIButton) { guard let item = currentItem else { return } // 触发闭包,把数据传递出去 didTapDetailButton?(item) } }
2. 在CollectionView的cellForItemAt方法中绑定闭包
extension YourCollectionVC: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellID", for: indexPath) as! YourCollectionViewCell let item = yourDataSourceArray[indexPath.item] cell.configure(with: item) // 绑定闭包,处理跳转逻辑 cell.didTapDetailButton = { [weak self] selectedItem in guard let self = self else { return } let homeVC = HomeViewController() homeVC.selectedItem = selectedItem self.navigationController?.pushViewController(homeVC, animated: true) } return cell } }
注:[weak self]是为了避免循环引用,一定要加上哦~
额外优化建议
- 如果想避免可选值的不确定性,可以给HomeVC写一个自定义初始化方法,强制传入模型数据:
class HomeViewController: UIViewController { let selectedItem: ItemModel // 这里用非可选类型 // 自定义初始化方法 init(selectedItem: ItemModel) { self.selectedItem = selectedItem super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") // 无Storyboard下不需要实现这个 } // 后续的UI填充逻辑就不用处理可选值了 private func populateData() { titleLabel.text = selectedItem.title contentLabel.text = selectedItem.content } }
这样初始化的时候就必须传入模型,更安全:
let homeVC = HomeViewController(selectedItem: selectedItem)
内容的提问来源于stack exchange,提问作者Chandler Long
相关产品推荐
相关产品推荐

