eivindml
eivindml

Reputation: 2539

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
}

Upvotes: 2

Views: 841

Answers (2)

Connor Neville
Connor Neville

Reputation: 7361

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)
    }

}

Upvotes: 3

creeperspeak
creeperspeak

Reputation: 5521

Since UIControl already gives you access to func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControlEvents) you can just use that. For example, if you have the label added to a UIViewController, and want that viewController to be able to react to something like editingDidBegin you could add this code to your viewController:

@IBOutletWeak var richLabel: RichLabel!

override func viewDidLoad() {
    super.viewDidLoad()
    self.richLabel.addTarget(self, action: #selector(labelEditingDidBegin(_:)), for: UIControlEvents.editingDidBegin)
}

@objc func labelEditingDidBegin(_ sender: RichLabel) {
    print(sender.text)
}

I'm not sure if your RichLabel allows editing, etc, but you can observe whichever UIControlEvent you need, react to that, and then execute whatever code you need in your Selector function.

Upvotes: 3

Related Questions