Is there a way to assign delegate to more than one object of the same class just one time in Swift?

Viewed 309

For example, I put 20 buttons in a view controller, they all inherited frome CommonButton which was inherited from UIButton, and the CommonButton conform a protocol called ButtonDelegate, now I want to assign the delegate to the buttons in view controller. Instead of making 20 buttons' IBOutlet and assign the delegate to each button one by one, is there a way that can simplify the assignment?

3 Answers

There is no explicit way. You can store 20 buttons into an array for productively use next time.

buttons = [btn0, ......., btn19]

for btn in buttons {
    btn.delegate = self
}

Another way, you can make static delegate. When the button event will fire, check your static delegate type, and control everything.

import UIKit

class ViewController1: UIViewController {
    var buttons: [CustomButton] = [CustomButton]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        loadUI()
    }
    
    func loadUI()  {
        buttons = (0..<5).map({ (index) -> CustomButton in
            let button = CustomButton()
            button.tag = index
            button.setTitle("\(index)", for: .normal)
            button.setTitleColor(.black, for: .normal)
            button.backgroundColor = UIColor.green
            return button
        })
        
        for (index, button) in buttons.enumerated()  {
            view.addSubview(button)
            button.translatesAutoresizingMaskIntoConstraints = false
            button.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
            if index == 0 {
                button.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            }else {
                button.leftAnchor.constraint(equalTo: (buttons[index-1]).rightAnchor).isActive = true
            }
            if index == buttons.count - 1 {
                button.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
            }
            button.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            button.widthAnchor.constraint(equalTo: (buttons[0]).widthAnchor).isActive = true
            button.addTarget(self, action: #selector(self.buttonEvent(_:)), for: .touchUpInside)
        }
        
        CustomButton.delegate = self
    }
    
    @objc func buttonEvent(_ button: UIButton) {
        if let viewController = CustomButton.delegate as? ViewController1 {
            print("tag: \(button.tag) viewController: \(viewController)")
        }
    }
}

extension ViewController1: CustomButtonProtocol {
}

protocol CustomButtonProtocol {
}

class CustomButton: UIButton {
    public static var delegate: CustomButtonProtocol?
}

There are a couple of approaches:

  1. Personally, I'd make the delegate property an @IBOutlet and then I could specify the delegate right in IB’s “Connection Inspector”, not having to do anything in code at all, e.g.

    @objc protocol CustomButtonDelegate: class { ... }
    
    class CustomButton: UIButton {
    
        @IBOutlet weak var delegate: CustomButtonDelegate?
    
        ...
    }
    

    enter image description here

  2. Alternatively, if you really want to do this programmatically, when you create outlets for the buttons in IB, you can also create a “outlet collection”:

    @IBOutlet var buttons: [CustomButton]!
    

    Then you can control-drag from the control in IB to this outlet collection in the “Assistant” view.

    enter image description here

    Once you do that, you can iterate through the outlet collection:

    for button in buttons {
        button.delegate = self
    }
    

    Clearly, you can manually populate the array of buttons yourself, but again, I would hook up the buttons to this “outlet collection” array right in IB.

You can use IBOutletCollection with different tags for your buttons.

Related