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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:28:34