iOS 屏幕间数据传输:从新手到专家
\n\n每个刚开始为 iOS 编写代码的人,迟早都会遇到一个难题:“如何将字符串、数组或对象传递到另一个屏幕?”这个问题非常普遍,以至于在 Stack Overflow 上它获得了数千次浏览和数十种答案。这并非偶然——iOS 的架构设计使得每个屏幕(ViewController)都独立运行,数据需要在它们之间有意识地传递。
\n\n在本文中,我们将探讨在 Swift 中视图控制器之间传输数据的 5 种主要方法。您将了解何时使用 segue,何时使用委托或闭包,以及为什么 NotificationCenter 并不总是坏事。开始吧!
\n\n为什么这会是问题?
\niOS 使用严格的 MVC(Model-View-Controller)架构。每个 ViewController 负责其屏幕的一部分,并且无法直接访问相邻控制器的数据。这是为了安全性和模块化而设计的,但对于开发者来说,这意味着需要显式地将数据从点 A“传递”到点 B。
\n\n想象一下:您有一个用户列表,并且希望在点击某个单元格时显示详细信息。您点击——新屏幕打开。如何传递用户 ID?这就是魔法的开始。
\n\n方法 1:通过 Storyboard Segue 传输数据(最简单)
\n最直接的方法是使用 prepare(for:sender:)。您只需在跳转前准备数据。非常适合简单的应用程序和原型。
// 在源 ViewController 中\noverride func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n if segue.identifier == "showDetail" {\n guard let destinationVC = segue.destination as? DetailViewController else { return }\n destinationVC.userID = 42 // 传递数字\n destinationVC.userName = "安娜" // 传递字符串\n }\n}\n\n// 在 DetailViewController 中\nvar userID: Int = 0\nvar userName: String = ""\n\n\n优点: 代码最少,所有内容集中在一处。 缺点: 与 Storyboard 紧密耦合,难以测试,容易在标识符中混淆。
\n\n方法 2:程序化导航与数据传输(进阶)
\n当您厌倦了 segues 并希望从代码中完全控制时,请使用控制器的直接实例。
\n\n// 在源 ViewController 中\nfunc showDetail() {\n let storyboard = UIStoryboard(name: "Main", bundle: nil)\n guard let detailVC = storyboard.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController else { return }\n \n detailVC.userID = 42\n detailVC.userName = "安娜"\n \n navigationController?.pushViewController(detailVC, animated: true)\n}\n\n\n这种方法提供了完全控制:您可以从网络加载数据、验证数据,然后再跳转。非常适合复杂逻辑。
\n\n方法 3:委托(Delegate Pattern)——反向通信
\n如果需要将数据传回怎么办?例如,用户在第二个屏幕上选择了日期,我们希望将其返回到第一个屏幕。这时委托就派上用场了。
\n\n步骤 1:创建协议
\nprotocol DatePickerDelegate: AnyObject {\n func didSelectDate(_ date: Date)\n}\n\n\n步骤 2:在子控制器中添加委托
\nclass DatePickerViewController: UIViewController {\n weak var delegate: DatePickerDelegate?\n \n @IBAction func saveButtonTapped(_ sender: UIButton) {\n let selectedDate = datePicker.date\n delegate?.didSelectDate(selectedDate)\n dismiss(animated: true)\n }\n}\n\n\n步骤 3:在父控制器中实现委托
\nclass MainViewController: UIViewController, DatePickerDelegate {\n func didSelectDate(_ date: Date) {\n print("收到日期: \\(date)")\n // 更新 UI\n }\n \n override f