【问题标题】:Swift Delegate Pattern: Delegate = NilSwift 委托模式:委托 = Nil
【发布时间】:2021-11-19 18:20:39
【问题描述】:

我知道这个问题在这里被问了很多,我已经阅读了所有这些问题,但我的问题仍然存在。我想在我的项目中包含委托模式,以便从一个类调用另一个类中的函数。但它也没有在那里工作。所以我创建了一个全新的项目来再次练习该模式。只有3个小时,我就是做不到。我的委托仍然留在我的主 ViewController nil 中。

这是我的 ViewController 代码。它是初始化的 ViewController 也是委托模式的主人,而 buttonViewController 是仆人:


import UIKit

protocol ViewControllerDelegate {
    func printTest(message: String)
}

class ViewController: UIViewController {
    
    var delegate: ViewControllerDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
    }
    

    @IBAction func perform(_ sender: UIButton) {
        let vc = storyboard?.instantiateViewController(withIdentifier: "vc") as! ButtonViewController
        if delegate == nil {
            print("Ist nil")
        }
        delegate?.printTest(message: "Test")
        present(vc, animated: true, completion: nil)
    }
    
}


如果用户按下执行按钮,我想在我的 buttonViewController 中调用一个函数,而不是我想对我的 buttonViewController 执行 segue。

这是我的 ButtonViewController:

import UIKit


class ButtonViewController: UIViewController, ViewControllerDelegate {


    override func viewDidLoad() {
        super.viewDidLoad()

        ViewController().delegate = self
        print("test")
        // Do any additional setup after loading the view.
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */
    
    @IBAction func performsegue(_ sender: UIButton) {
        
    }
    
    func printTest(message: String) {
        print(message + "\(self)")
    }
}

我已经尝试执行一个 segue,首先 viewDidLoad 由我的 ButtonViewController 调用,然后返回到 ViewController,然后通过按钮再次调用 perform 方法,然后查看 delegate 是否仍然为 nil,是的不幸的是仍然为零。

有人可以帮我解决这个问题吗?

最好的问候!

【问题讨论】:

  • 查看本教程:medium.com/@astitv96/…
  • 问题是ViewController().delegate = self是个废话。它创建一个ViewController 的新实例,将delegate 分配给它,然后丢弃它。该实例从未使用过。
  • 你为什么要在这里使用委托?据我了解,你只想在perform 中做vc.printTest(message: "Test"),所以就这样做吧!
  • @Sulthan 我已经考虑过了,但即使我在 viewDidLoad 之外定义我的 ViewController 也不起作用。
  • @Sweeper 这只是一个测试,测试后我想在我的 buttonViewController 中更改图片。

标签: ios swift delegates


【解决方案1】:

代理模式一开始让很多人感到困惑。我发现我的大多数学生一开始都倾向于尝试倒退,这也是我认为这里发生的事情。通常在委托模式中有一个视图控制器(A)呈现另一个(B)。通常,对 B 的某些操作应该在 A 上触发某些功能(在您的情况下,可能按下 B 上的按钮会触发 A 打印某些内容)。

在这种情况下,您将有 UIViewController 的两个子类:AViewControllerBViewController 以及一个协议 BViewControllerDelegate。设置如下:

  1. BViewController 中的按钮被按下时,协议BViewControllerDelegate 将具有您希望在AViewController 上调用的功能。
  2. AViewController 将符合此协议并实现此功能。
  3. BViewController 中,您将定义您的委托属性:weak var delegate: BViewControllerDelegate?
  4. BViewController 的呈现过程中,该属性将由AViewController 的实例自身(AViewController 的实例)设置在BViewController 的实例上。
  5. BViewController 的实例将调用其委托属性上的函数以响应按钮按下。
class AViewController: UIViewController, BViewControllerDelegate {
    // This is 4, this segue action is invoked by a storyboard segue in the storyboard and is responsible for setting up the destination view controller and configuring it as needed (i.e., setting its delegate property)
    @IBSegueAction func makeBViewController(_ coder: NSCoder) -> BViewController {
        let bViewController = BViewController(coder: coder)!
        bViewController.delegate = self
        
        return bViewController
    }

    // Here we accomplish 2 (and also above where we declare conformance to the protocol)    
    func bViewControllerDidPerformAction(viewController: BViewController, message: String) {
        print(message)
    }
}
protocol BViewControllerDelegate: AnyObject {
    // Here we accomplish 1
    func bViewControllerDidPerformAction(viewController: BViewController, message: String)
}

class BViewController: UIViewController {
    // Here is 5
    @IBAction func buttonPressed(_ sender: Any) {
        delegate?.bViewControllerDidPerformAction(viewController: self, message: "Test Message")
    }
    
    // This is 3
    weak var delegate: BViewControllerDelegate?
}

我不是 100% 清楚您要在代码中执行的操作,但我相信您的 ViewController 应该像 AViewController 那样设置,而您的 ButtonViewController 应该像 BViewController 那样设置。此外,代码行 ViewController().delegate = self 什么也不做,因为它创建了一个 ViewController 的新实例并设置了它的委托,但是这个新实例会立即被释放,因为它实际上并不是在其他任何地方使用的那个。

【讨论】:

    【解决方案2】:

    协议/委托模式用于允许实例化的控制器(或其他对象)与创建它的类进行通信。

    因此,在您的场景中,您希望 ViewController 符合 ViewControllerDelegate ...当您实例化 ButtonViewController(具有 ViewControllerDelegate 委托变量)时,您将 ButtonViewController 的委托分配给自我(ViewController)。

    现在,ButtonViewController 可以通过其delegate 调用协议函数。

    protocol ViewControllerDelegate {
        func printTest(message: String)
    }
    
    // make ViewController conform to ViewControllerDelegate
    class ViewController: UIViewController, ViewControllerDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
        
        
        @IBAction func perform(_ sender: UIButton) {
            let vc = storyboard?.instantiateViewController(withIdentifier: "vc") as! ButtonViewController
            
            // set self as the delegate in ButtonViewController
            vc.delegate = self
            
            present(vc, animated: true, completion: nil)
        }
    
        func printTest(message: String) {
            print("printTest in delegate:", message)
        }
        
    }
    
    class ButtonViewController: UIViewController {
        
        var delegate: ViewControllerDelegate?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            print("test")
        }
        
        @IBAction func buttonTap(_ sender: UIButton) {
            // call the delegate
            delegate?.printTest(message: "Sending to delegate!")
        }
    
    }
    

    编辑 -- 这是一个完整的运行示例。 “主”视图控制器有一个按钮和一个标签。点击按钮以显示 ButtonVC。按钮视图控制器有一个文本字段和一个“保存并关闭”按钮。在文本字段中输入一些文本,点击保存按钮,文本将通过ViewControllerDelegate发送到主控制器。

    不需要@IBOutlet@IBAction 连接——只需将空白视图控制器的自定义类分配给ViewController

    // make ViewController conform to ViewControllerDelegate
    class ViewController: UIViewController, ViewControllerDelegate {
        
        let btn: UIButton = {
            let v = UIButton()
            v.setTitle("Show Button VC", for: [])
            v.backgroundColor = .systemRed
            v.setTitleColor(.white, for: .normal)
            v.setTitleColor(.lightGray, for: .highlighted)
            return v
        }()
        let label: UILabel = {
            let v = UILabel()
            v.backgroundColor = .green
            v.numberOfLines = 0
            v.text = "From ButtonVC:\n"
            return v
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            [label, btn].forEach { v in
                v.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(v)
            }
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
                label.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
                label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            ])
            
            btn.addTarget(self, action: #selector(showButtonsVC(_:)), for: .touchUpInside)
        }
        
        @objc func showButtonsVC(_ sender: UIButton) {
            // instantiate Button View Controller
            let vc = ButtonViewController()
            
            // set self as the delegate in ButtonViewController
            vc.delegate = self
            
            // present it
            present(vc, animated: true, completion: nil)
        }
    
        func printTest(message: String) {
            print("Delegate received:", message)
            label.text = "From ButtonVC:\n" + message
        }
        
    }
    
    class ButtonViewController: UIViewController {
        
        var delegate: ViewControllerDelegate?
    
        let textField: UITextField = {
            let v = UITextField()
            v.borderStyle = .roundedRect
            return v
        }()
        let btn: UIButton = {
            let v = UIButton()
            v.setTitle("Save and Close", for: [])
            v.backgroundColor = .systemRed
            v.setTitleColor(.white, for: .normal)
            v.setTitleColor(.lightGray, for: .highlighted)
            return v
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            view.backgroundColor = .systemYellow
            
            [textField, btn].forEach { v in
                v.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(v)
            }
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                textField.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                textField.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
                btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
                btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            ])
            
            btn.addTarget(self, action: #selector(buttonTap(_:)), for: .touchUpInside)
        }
        
        @IBAction func buttonTap(_ sender: UIButton) {
            // call the delegate
            let str = textField.text ?? "No text entered..."
            delegate?.printTest(message: str)
            dismiss(animated: true, completion: nil)
        }
    
    }
    

    【讨论】:

    • 嗨。感谢您的回答,但不幸的是它没有用。
    • @BenPython - 好的,如果没有关于“不起作用”的附加信息,很难知道该说什么。我用一个完整的、可以运行的工作示例编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 2020-06-13
    • 2018-12-31
    • 2018-11-08
    • 2018-02-10
    • 1970-01-01
    相关资源
    最近更新 更多