Search code examples
iosswiftuicontrol

iOS: Best practice for recreating addTarget() in Swift?


I'm creating a custom UIControl subclass, and want to add a addTarget() function, similar to UIButton. How do I do this properly? So far I have this (it works, but not sure if is a good solution):

import UIKit

class RichLabel: UIControl {

    var labelTarget: Any?
    var labelAction: Selector?

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func addTarget(_ target: Any?, action: Selector) {
        self.labelTarget = target
        self.labelAction = action
    }

    func buttonAction() {

        if let labelTarget = labelTarget, let labelAction = labelAction {               
         let _ = (labelTarget as AnyObject).perform(labelAction, with: self)
    }
}

The target is added from another view, like this:

var richLabel: RichLabel {
    let label = RichLabel()
    label.setAttributedText(title: string)
    label.addTarget(self, action: #selector(richLabelAction(label:)))
    return label
}

Solution

  • If you inherit from UIControl, you don't need to do anything to addTarget. Callers will use addTarget on your custom UIControl the same way they would any other control. All your custom control has to do is decide when it wants to call those actions, and call sendActions(for:).

    If your goal is to have this RichLabel class function just like a button, I would add a UITapGestureRecognizer to it, and in the gesture recognizer, call self.sendActions(for: .touchUpInside).

    class RichLabel: UIControl {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
            addGestureRecognizer(tapGesture)
        }
    
        required init?(coder _: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        @objc func labelTapped() {
            // When this happens, any object that called `addTarget`
            // for the .touchUpInside event will get its callback triggered
            sendActions(for: .touchUpInside)
        }
    
    }