UICollectionView自定义Cell如何通过Segue传值到PlayerViewController
问题修复方案
你现有代码存在几个核心逻辑错误:
- 代理方法未携带选中的
Video对象,无法传递点击的视频数据 - 代理遵守方错误:跳转逻辑属于控制器职责,不应由目标页
PlayerViewController遵守代理执行跳转 prepare(for:sender:)方法位置错误:该方法仅在发起跳转的源控制器上触发,写在目标控制器里永远不会执行- 传值逻辑错误:不能在跳转前直接访问目标控制器的UI控件(此时控件未初始化,为nil),且
sender参数未传递选中的视频对象
分步修改步骤
1. 修改代理协议,补充视频参数
把原来无参的代理方法改成带Video参数,用于传递点击的视频对象:
import UIKit protocol FirstPlaylistCollectionViewDelegate: AnyObject { // 新增Video参数用于传递选中的视频 func playVideo(_ video: Video) }
2. 修改CollectionView的点击回调,传递选中视频
在FirstPlaylistCollectionView的didSelectItemAt方法里,取出选中的视频,通过代理传递出去:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedVideo = firstPlaylist[indexPath.row] // 把选中的视频传给代理方 self.delegate2?.playVideo(selectedVideo) print("selected video \(selectedVideo) with \(collectionView)! DONE!") }
3. 在持有CollectionView的主控制器中遵守代理
你添加三个横向CollectionView的主ViewController(下称HomeViewController)才是执行跳转的正确位置,不要在PlayerViewController里实现代理:
- 首先在主控制器初始化
FirstPlaylistCollectionView的位置,设置代理:
// 初始化FirstPlaylistCollectionView后补充这行 firstPlaylistCollectionView.delegate2 = self
- 然后在主控制器中实现代理方法,触发跳转,把选中的视频作为
sender参数传入:
extension HomeViewController: FirstPlaylistCollectionViewDelegate { func playVideo(_ video: Video) { // sender直接传选中的video对象,不要传self performSegue(withIdentifier: "homeToPlayer", sender: video) } }
4. 实现跳转前的传值逻辑
把原来写在PlayerViewController里的prepare(for:sender:)方法删掉,移到主控制器HomeViewController中,仅给目标控制器的属性赋值,不要直接操作UI:
// 该方法必须写在发起跳转的HomeViewController中 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard segue.identifier == "homeToPlayer", let playerVC = segue.destination as? PlayerViewController, let selectedVideo = sender as? Video else { return } // 只给目标控制器的存储属性赋值,不要直接访问UI控件 playerVC.video = selectedVideo }
5. 清理PlayerViewController中的错误代码
删掉PlayerViewController里错误的prepare方法、错误的代理扩展,在viewDidLoad中用传递过来的video数据更新UI:
import UIKit import WebKit class PlayerViewController: UIViewController { @IBOutlet weak var handleArea: UIView! @IBOutlet weak var openCloseArrow: UIImageView! @IBOutlet weak var titleOfVideoLabel: UILabel! // 补上你原来缺失的标题控件输出口 var video: Video? override func viewDidLoad() { super.viewDidLoad() // 等页面加载完成再给UI赋值 titleOfVideoLabel.text = video?.title // 其余播放器初始化、加载视频逻辑写在这里 } }
补充:纯代码跳转方案(不用Storyboard Segue)
如果你后续要完全去掉Storyboard,直接在代理方法里初始化控制器传值即可,不用写performSegue和prepare方法:
// 主控制器里的代理方法 func playVideo(_ video: Video) { let playerVC = PlayerViewController() playerVC.video = video // 导航栈跳转用这个 navigationController?.pushViewController(playerVC, animated: true) // 模态弹出用这个 // present(playerVC, animated: true) }
注意:如果用纯代码初始化
PlayerViewController,原来的@IBOutlet会失效,需要把相关UI控件改成纯代码布局实现。
内容的提问来源于stack exchange,提问作者V777
相关产品推荐
相关产品推荐

