如何从作为FirstViewController容器视图的SecondViewController访问前者?
嘿,这个场景在iOS开发里太常见啦!当SecondViewController作为FirstViewController的容器视图时,反向访问的需求确实很普遍,我给你分享几种实用的实现方案,你可以根据自己的业务场景来选:
方案1:代理模式(最推荐,符合iOS设计规范)
这是Apple官方最提倡的组件通信方式,耦合度低,逻辑清晰,还能避免内存泄漏。步骤很简单:
- 首先在
SecondViewController里定义一个协议,声明你需要从Second调用First的方法:
protocol SecondVCDelegate: AnyObject { func secondVCDidTriggerAction(_ secondVC: SecondViewController, data: String?) }
- 然后在
SecondViewController里添加一个弱引用的代理属性(必须加weak,防止循环引用):
class SecondViewController: UIViewController { weak var delegate: SecondVCDelegate? // 比如某个按钮点击时触发代理方法 @IBAction func notifyFirstVCAction(_ sender: UIButton) { delegate?.secondVCDidTriggerAction(self, data: "来自SecondVC的消息") } }
- 接下来回到
FirstViewController,遵守这个协议并实现方法,同时在获取到SecondViewController实例时把自己设为代理:
class FirstViewController: UIViewController, SecondVCDelegate { var secondVC: SecondViewController? // 假设你是通过prepare(for:sender:)获取容器视图的VC override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let vc = segue.destination as? SecondViewController { secondVC = vc vc.delegate = self // 设置代理 } } // 实现代理方法 func secondVCDidTriggerAction(_ secondVC: SecondViewController, data: String?) { print("收到SecondVC的消息:\(data ?? "")") // 这里处理FirstVC的逻辑,比如更新UI、调用方法等 } }
方案2:闭包(Block)回调
如果觉得代理的代码有点繁琐,闭包也是个不错的选择,写法更简洁:
- 在
SecondViewController里定义一个可选的闭包属性:
class SecondViewController: UIViewController { var onActionTriggered: ((String?) -> Void)? @IBAction func notifyFirstVCAction(_ sender: UIButton) { onActionTriggered?("来自SecondVC的闭包消息") } }
- 在
FirstViewController里给闭包赋值,注意用[weak self]避免循环引用:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let vc = segue.destination as? SecondViewController { secondVC = vc vc.onActionTriggered = { [weak self] message in guard let self = self else { return } print("收到闭包消息:\(message ?? "")") // 处理FirstVC的逻辑 } } }
方案3:弱引用直接持有父VC
这种方式最直接,但要特别注意内存管理:
- 在
SecondViewController里添加一个弱引用的FirstViewController属性:
class SecondViewController: UIViewController { weak var parentFirstVC: FirstViewController? @IBAction func callFirstVCMethod(_ sender: UIButton) { parentFirstVC?.doSomething() // 直接调用FirstVC的方法 } }
- 在
FirstViewController里获取到SecondViewController后赋值:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let vc = segue.destination as? SecondViewController { secondVC = vc vc.parentFirstVC = self } }
注意:一定要用
weak修饰这个属性!否则FirstVC和SecondVC会互相持有,导致内存泄漏。
方案4:通知中心(NSNotificationCenter)
这种方式耦合度最低,但不够直观,适合跨多层级的通信,一般不推荐用在这种明确的容器父子场景:
- 在
FirstViewController里注册通知:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(handleSecondVCNotification(_:)), name: NSNotification.Name("SecondVCActionNotification"), object: nil) } @objc func handleSecondVCNotification(_ notification: Notification) { if let data = notification.userInfo?["data"] as? String { print("收到通知消息:\(data)") // 处理逻辑 } } // 记得在deinit里移除观察者 deinit { NotificationCenter.default.removeObserver(self) }
- 在
SecondViewController里发送通知:
@IBAction func sendNotification(_ sender: UIButton) { NotificationCenter.default.post(name: NSNotification.Name("SecondVCActionNotification"), object: nil, userInfo: ["data": "来自SecondVC的通知消息"]) }
总结一下:如果是简单的通信,闭包或者弱引用属性都可以;如果是比较复杂的交互,推荐用代理模式,扩展性更好;通知中心尽量少用在这种父子容器的场景,避免代码逻辑分散不好维护。
内容的提问来源于stack exchange,提问作者Dmytrii G.
相关产品推荐
相关产品推荐

