Swift无Storyboard场景下如何在两个控制器间回传数据
无Storyboard下Swift控制器B向A回传数据的实现方案
嘿,在无Storyboard的Swift项目里,从控制器B回传数据给主控制器A,我常用三种靠谱的方式,结合你的场景给你拆解清楚,代码都是直接能用的:
1. 闭包(Closure):简单场景首选
闭包是最直观的方式,代码量少,适合单次数据回传的简单场景,比如B完成某个操作后把数据传给A。
控制器B的代码
class ControllerB: UIViewController { // 定义闭包,参数类型根据你要传递的数据调整(比如这里用String) var onDataPassed: ((String) -> Void)? // 模拟用户触发回传的操作(比如点击确定按钮) @objc private func didTapConfirm() { let dataToSend = "从B传回的用户数据" // 调用闭包传递数据 onDataPassed?(dataToSend) // 关闭B控制器 dismiss(animated: true) } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white // 添加测试按钮 let confirmBtn = UIButton(type: .system) confirmBtn.setTitle("完成并传回数据", for: .normal) confirmBtn.addTarget(self, action: #selector(didTapConfirm), for: .touchUpInside) confirmBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(confirmBtn) } }
控制器A的代码(跳转并接收数据)
class ControllerA: UIViewController { var currentUser: User? // 你的用户属性 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 你的原有检查逻辑 guard let user = currentUser else { let setupProfileVC = SetupProfileViewController() let navVC = UINavigationController(rootViewController: setupProfileVC) present(navVC, animated: true) return } // 用户存在时的逻辑 } // 模拟跳转到B的操作 @objc private func didTapGoToControllerB() { let controllerB = ControllerB() // 设置闭包,接收B传回的数据 controllerB.onDataPassed = { [weak self] receivedData in guard let self = self else { return } // 处理传回的数据,比如更新currentUser self.currentUser?.nickname = receivedData // 刷新UI或者做其他操作 self.view.setNeedsLayout() print("收到B的数据:\(receivedData)") } // 无Storyboard方式present B(如果是导航栈用push) present(controllerB, animated: true) } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .lightGray // 添加跳转按钮 let goToBBtn = UIButton(type: .system) goToBBtn.setTitle("跳转到控制器B", for: .normal) goToBBtn.addTarget(self, action: #selector(didTapGoToControllerB), for: .touchUpInside) goToBBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(goToBBtn) } }
2. 代理(Delegate):复杂场景首选
如果需要多个回调(比如完成、取消、失败等),代理模式会让代码结构更清晰,适合团队协作的复杂场景。
第一步:定义代理协议
// 定义代理协议,@objc和optional可选,根据需要调整 @objc protocol ControllerBDelegate: NSObjectProtocol { // 必须实现的回传方法 func controllerB(_ controller: ControllerB, didFinishWithData data: String) // 可选的取消回调 @objc optional func controllerBDidCancel(_ controller: ControllerB) }
控制器B的代码
class ControllerB: UIViewController { // 用weak修饰避免循环引用 weak var delegate: ControllerBDelegate? @objc private func didTapConfirm() { let dataToSend = "从B传回的用户数据" // 调用代理方法传递数据 delegate?.controllerB(self, didFinishWithData: dataToSend) dismiss(animated: true) } @objc private func didTapCancel() { // 调用可选的取消回调 delegate?.controllerBDidCancel?(self) dismiss(animated: true) } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let confirmBtn = UIButton(type: .system) confirmBtn.setTitle("完成", for: .normal) confirmBtn.addTarget(self, action: #selector(didTapConfirm), for: .touchUpInside) confirmBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(confirmBtn) let cancelBtn = UIButton(type: .system) cancelBtn.setTitle("取消", for: .normal) cancelBtn.addTarget(self, action: #selector(didTapCancel), for: .touchUpInside) cancelBtn.frame = CGRect(x: 80, y: 280, width: 250, height: 50) view.addSubview(cancelBtn) } }
控制器A的代码(遵守代理并接收数据)
class ControllerA: UIViewController, ControllerBDelegate { var currentUser: User? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 原有检查逻辑 guard let user = currentUser else { let setupProfileVC = SetupProfileViewController() let navVC = UINavigationController(rootViewController: setupProfileVC) present(navVC, animated: true) return } } @objc private func didTapGoToControllerB() { let controllerB = ControllerB() controllerB.delegate = self // 设置代理 present(controllerB, animated: true) } // 实现代理的必须方法 func controllerB(_ controller: ControllerB, didFinishWithData data: String) { print("通过代理收到B的数据:\(data)") currentUser?.nickname = data view.setNeedsLayout() } // 实现可选的取消回调(可选) func controllerBDidCancel(_ controller: ControllerB) { print("用户取消了操作") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .lightGray let goToBBtn = UIButton(type: .system) goToBBtn.setTitle("跳转到控制器B", for: .normal) goToBBtn.addTarget(self, action: #selector(didTapGoToControllerB), for: .touchUpInside) goToBBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(goToBBtn) } }
3. 通知中心(NotificationCenter):跨层级传递数据
如果A和B不是直接跳转关系,或者需要跨多个控制器传递数据,可以用通知中心,但要注意管理观察者避免内存泄漏。
第一步:定义通知名称
// 用扩展统一管理通知名称,避免硬编码 extension Notification.Name { static let controllerBDidPassData = Notification.Name("ControllerBDidPassDataNotification") }
控制器A的代码(添加观察者接收通知)
class ControllerA: UIViewController { var currentUser: User? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .lightGray // 添加通知观察者,监听自定义通知 NotificationCenter.default.addObserver( self, selector: #selector(handleDataPassedNotification(_:)), name: .controllerBDidPassData, object: nil ) let goToBBtn = UIButton(type: .system) goToBBtn.setTitle("跳转到控制器B", for: .normal) goToBBtn.addTarget(self, action: #selector(didTapGoToControllerB), for: .touchUpInside) goToBBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(goToBBtn) } @objc private func handleDataPassedNotification(_ notification: Notification) { // 从通知的userInfo里取出数据 if let data = notification.userInfo?["userData"] as? String { print("通过通知收到B的数据:\(data)") currentUser?.nickname = data view.setNeedsLayout() } } @objc private func didTapGoToControllerB() { let controllerB = ControllerB() present(controllerB, animated: true) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 原有检查逻辑 guard let user = currentUser else { let setupProfileVC = SetupProfileViewController() let navVC = UINavigationController(rootViewController: setupProfileVC) present(navVC, animated: true) return } } deinit { // 一定要移除观察者,避免内存泄漏 NotificationCenter.default.removeObserver(self) } }
控制器B的代码(发送通知传递数据)
class ControllerB: UIViewController { @objc private func didTapConfirm() { let dataToSend = "从B传回的用户数据" // 发送通知,携带数据放在userInfo里 NotificationCenter.default.post( name: .controllerBDidPassData, object: nil, userInfo: ["userData": dataToSend] ) dismiss(animated: true) } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let confirmBtn = UIButton(type: .system) confirmBtn.setTitle("完成并传回数据", for: .normal) confirmBtn.addTarget(self, action: #selector(didTapConfirm), for: .touchUpInside) confirmBtn.frame = CGRect(x: 80, y: 200, width: 250, height: 50) view.addSubview(confirmBtn) } }
方案选择建议
- 简单单次回传:优先用闭包,代码简洁易维护。
- 多回调复杂场景:用代理,代码结构清晰,适合团队协作。
- 跨层级/无直接关联:用通知中心,但要注意管理观察者避免内存泄漏。
内容的提问来源于stack exchange,提问作者Kira
相关产品推荐
相关产品推荐

