You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift3:UITableView嵌套UICollectionView首次点击传值为nil求助

问题分析与解决

嘿,这个问题我一眼就看穿了——你在点击事件里的执行顺序搞反啦!咱们一步步拆解修复:

问题根源

你当前在collectionView(_:didSelectItemAt:)里的代码逻辑是先触发跳转的performSegue(也就是调用didSelectAction!()),然后才给vcInstance的musicName和musicUrl赋值。但performSegue会立刻触发prepare(for:sender:)方法,这时候你的两个属性还没被赋值,所以首次点击时传递给播放页面的就是nil;第二次点击时,因为第一次点击已经把属性填上了,所以能拿到值,但这纯属巧合,逻辑是完全错误的。

解决方案1:快速调整执行顺序(最省心的临时修复)

只需要把赋值代码放到触发didSelectAction之前,这样跳转的时候属性已经有正确的值了:

修改CategoryRow里的collectionView(_:didSelectItemAt:)方法:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // 先给ViewController的属性赋值
    let trackName = RemoteModel.sharedInstanceRemoteModel.singleGuidedTrandingTrackname[indexPath.row]
    let trackUrl = RemoteModel.sharedInstanceRemoteModel.singleGuidedTrandingTrackurl[indexPath.row]
    
    vcInstance?.musicName = trackName
    vcInstance?.musicUrl = trackUrl
    
    // 再触发跳转动作
    didSelectAction!()
}

解决方案2:更优雅的传值方式(推荐长期使用)

上面的方法虽然能解决问题,但依赖ViewController的属性还是有点耦合。更靠谱的方式是直接把选中的音乐数据通过闭包参数传递,再作为sender传给segue,完全避免属性依赖:

步骤1:修改CategoryRow的闭包定义

把原来无参数的闭包改成带音乐数据的:

class CategoryRow : UITableViewCell {
    @IBOutlet var collectionView: UICollectionView!
    var vcInstance: newTableViewController?
    var parentViewController: UIViewController? = nil
    // 修改闭包类型,添加两个参数
    var didSelectAction: ((String, String) -> Void )?
}

步骤2:更新CollectionView点击事件

在collectionView(_:didSelectItemAt:)里直接传递选中的数据:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let trackName = RemoteModel.sharedInstanceRemoteModel.singleGuidedTrandingTrackname[indexPath.row]
    let trackUrl = RemoteModel.sharedInstanceRemoteModel.singleGuidedTrandingTrackurl[indexPath.row]
    
    // 把数据传给闭包
    didSelectAction?(trackName, trackUrl)
}

步骤3:在TableViewController中接收数据并跳转

修改cellForRowAt里的闭包实现,同时可以封装一个数据结构来传递:

// 先在newTableViewController里定义一个简单的结构体,用来封装音乐数据
struct MusicTrack {
    let name: String
    let url: String
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell : CategoryRow = tableView.dequeueReusableCell(withIdentifier: "cell")as! CategoryRow
    cell.vcInstance = self
    cell.parentViewController = self
    
    cell.didSelectAction = { [weak self] trackName, trackUrl in
        guard let self = self else { return }
        let musicTrack = MusicTrack(name: trackName, url: trackUrl)
        // 把封装好的数据作为sender传递给segue
        self.performSegue(withIdentifier: "homePlayer", sender: musicTrack)
    }
    
    return cell
}

步骤4:在prepareForSegue中接收数据

直接从sender里获取音乐数据,不用依赖ViewController的属性:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "homePlayer" {
        let playerVC = segue.destination as! PlayerViewController
        // 从sender里解析出音乐数据
        if let musicTrack = sender as? MusicTrack {
            playerVC.UrlAudio = musicTrack.url
            playerVC.songTittle = musicTrack.name
        }
    }
}

这样修改后,不仅彻底解决了首次点击的nil问题,代码的耦合度也更低,后续维护起来会方便很多。

内容的提问来源于stack exchange,提问作者Chetan Lodhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:58:12