容器视图架构下,视图控制器间传递Song对象列表的方案咨询
嘿,针对你遇到的容器视图控制器之间的数据传递问题,我整理了几个实用的解决方案,都是iOS开发里常用的思路:
解决方案1:委托模式(Delegate Pattern)
这是iOS中处理父子/兄弟VC通信最经典的方式,利用MainVC作为中间层来传递数据,类型安全且逻辑清晰。
步骤和代码示例:
- 定义协议,规定数据传递的方法
- 在带Google Maps的容器VC中添加委托属性
- MainVC初始化子VC时,将自身设为委托
- 触发数据传递时,通过委托方法通知MainVC,再由MainVC把数据传给表格VC
// 定义数据传递协议 protocol MapContainerVCDelegate: AnyObject { func didSelectSongs(_ songs: [Song]) } // 带Google Maps的容器VC class MapContainerVC: UIViewController { // 弱引用避免循环引用 weak var delegate: MapContainerVCDelegate? // 假设这是触发数据传递的逻辑(比如地图交互事件) func triggerSongPassing() { let selectedSongs: [Song] = // 你的Song列表数据 delegate?.didSelectSongs(selectedSongs) } } // MainVC(容器持有方) class MainVC: UIViewController, MapContainerVCDelegate { var mapContainerVC: MapContainerVC! var tableVC: TableViewController! override func viewDidLoad() { super.viewDidLoad() // 初始化并添加两个子VC到容器视图的代码... mapContainerVC.delegate = self } // 实现协议方法,接收数据并传递给表格VC func didSelectSongs(_ songs: [Song]) { tableVC.updateSongList(songs) } } // 表格视图控制器 class TableViewController: UITableViewController { var songs: [Song] = [] func updateSongList(_ newSongs: [Song]) { songs = newSongs tableView.reloadData() } }
解决方案2:闭包(Closure)传递
和委托模式逻辑类似,但写法更简洁,适合简单的数据传递场景。
// 带Google Maps的容器VC class MapContainerVC: UIViewController { // 定义闭包类型,用于传递Song列表 var onSongsSelected: (([Song]) -> Void)? func triggerSongPassing() { let selectedSongs: [Song] = // 你的数据 onSongsSelected?(selectedSongs) } } // MainVC class MainVC: UIViewController { var mapContainerVC: MapContainerVC! var tableVC: TableViewController! override func viewDidLoad() { super.viewDidLoad() // 初始化子VC... // 注入闭包,直接将数据传递给表格VC mapContainerVC.onSongsSelected = { [weak self] songs in self?.tableVC.updateSongList(songs) } } }
如果你的应用有较多跨VC的数据共享需求,可以创建一个可观察的数据模型,让两个子VC都持有模型的引用,通过监听模型变化来更新UI。这里用Combine框架举例(iOS 13+支持):
import Combine // 共享数据模型,用@Published标记可观察属性 class SongDataModel: ObservableObject { @Published var selectedSongs: [Song] = [] } // MainVC class MainVC: UIViewController { // 创建单例或实例化模型,传递给两个子VC let songDataModel = SongDataModel() var mapContainerVC: MapContainerVC! var tableVC: TableViewController! override func viewDidLoad() { super.viewDidLoad() // 初始化子VC时传递模型 mapContainerVC.songDataModel = songDataModel tableVC.songDataModel = songDataModel } } // 带Google Maps的容器VC class MapContainerVC: UIViewController { var songDataModel: SongDataModel! func triggerSongPassing() { let selectedSongs: [Song] = // 你的数据 // 直接更新模型数据,表格VC会自动监听变化 songDataModel.selectedSongs = selectedSongs } } // 表格视图控制器 class TableViewController: UITableViewController { var songDataModel: SongDataModel! private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() // 监听模型数据变化,更新UI songDataModel.$selectedSongs .receive(on: DispatchQueue.main) .sink { [weak self] songs in self?.songs = songs self?.tableView.reloadData() } .store(in: &cancellables) } }
关于NotificationCenter的修复建议
如果你之前尝试的NotificationCenter没生效,大概率是观察者注册时机不对或者类型转换错误,可以调整成这样:
// 带Google Maps的容器VC发送通知 func triggerSongPassing() { let selectedSongs: [Song] = // 你的数据 NotificationCenter.default.post( name: NSNotification.Name("SongsSelectedNotification"), object: selectedSongs ) } // 表格视图控制器注册/接收通知 class TableViewController: UITableViewController { var songs: [Song] = [] override func viewDidLoad() { super.viewDidLoad() // 在viewDidLoad中注册观察者 NotificationCenter.default.addObserver( self, selector: #selector(handleSongsSelected(_:)), name: NSNotification.Name("SongsSelectedNotification"), object: nil ) } @objc private func handleSongsSelected(_ notification: Notification) { // 确保类型转换正确 guard let songs = notification.object as? [Song] else { return } self.songs = songs tableView.reloadData() } // 记得在销毁时移除观察者,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } }
整体来说,推荐优先使用委托或闭包,这两种方式类型安全、逻辑清晰;共享数据模型适合复杂的跨VC数据场景;NotificationCenter适合松散耦合的跨模块通信,但要注意内存管理。
内容的提问来源于stack exchange,提问作者Tubul
相关产品推荐
相关产品推荐

