[Swift] How to properly subclass UITextfield and work with its delegate

Consider this:

class ViewController: UIViewController, UITextFieldDelegate  {
    var textfield: UITextField = {
        let tf = UITextField(frame: CGRect(x: 0, y: 0, width: 140, height: 30))
    }()

    override func viewDidLoad() {
        textfield.delegate = self
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        print("Inside textfield") 
    }
}


When I tap inside the textfield, textFieldDidBeginEditing() is called because the delegate handles that.

What happens if I want to subclass UITextField? How do I handle a tap event inside the subclassed textfield within the subclass? So for example:

class MyTextField: UITextField, UITextFieldDelegate {
    required override init(frame: CGRect) {
        self.delegate = self // CAN'T DO THIS!!!
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        print("Inside subclassed textfield")
        // This function is NOT executed when I tap inside this subclassed textfield!!
    }
}


The textFieldDidBeginEditing() within the subclass never gets called, I guess because the delegate is not properly set, and I don't know how to set it. It's not possible to do: self.delegate = self

Do I need to create a protocol for MyTextField? If so, how do I hook it all up? What would the code using MyTextField have to do? If I do:

    var subTF = MyTextField(....)
    subTF.delegate = self


Is that .delegate for class UITextField?? or is it for the subclass MyTextField?

Replies

Your init is not correct.

You miss call to super.

    required override init(frame: CGRect) {
          super.init(frame: frame)
          delegate = self
    }

why do you add required ? What is the compiler error you get where // CAN'T DO THIS!!!

You should also need the init(coder):

     required init?(coder: NSCoder) {
          super.init(coder: coder)
          delegate = self
     }

With this, textFieldDidBeginEditing works as expected.

  • @Claude31 I fixed the init() stuff, it's working now. It wasn't working before because I had the ViewController code which uses MyTextField to also have a UITextFieldDelegate:

    class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { let myTF = MyTextField( .... ) myTF.delegate = self }    func textFieldDidBeginEditing(_ textField: UITextField) {     print("XXXXX"). // This is printed instead of "Inside subclassed textfield" within the subclass.   }  ``` So, if the subclassed textfield's delegate is set to self within the calling code (ViewController in this case), the textFieldDidBeginEditing() inside the subclass is never called. In general, is there a way to trigger textFieldDidBeginEditing() in both the calling code as well as the one inside the subclass?
  • Oops, sorry, I didn't know formatting is off inside a comment!

Add a Comment

Thanks for feedback. Don't forget to close the thread.

As for your next question :

the ViewController code which uses MyTextField to also have a UITextFieldDelegate: 

class ViewController: UIViewController, UITextFieldDelegate { 
  override func viewDidLoad() { 
    let myTF = MyTextField( .... ) 
    myTF.delegate = self 
  } 

  func textFieldDidBeginEditing(_ textField: UITextField) {
    print("XXXXX"). // This is printed instead of "Inside subclassed textfield" within the subclass.  
} 

So, if the subclassed textfield's delegate is set to self within the calling code (ViewController in this case), the textFieldDidBeginEditing() inside the subclass is never called.

In general, is there a way to trigger textFieldDidBeginEditing() in both the calling code as well as the one inside the subclass?

Sequence happens as follows:

  • in viewDidLoad, all subviews are loaded.
  • hence the subclassed textfield's delegate is set to itself 
  • but then, you execute code in viewDidLoad and override the delegate to be the class (ViewController)
  • So only this one will be triggered

I do not know of direct mechanism to achieve what you want. And if that was possible, that could make debugging a bit hard.

What you could do:

  • remove the delegate declaration in ViewController for myTF
  • in the MyTextField subclass, post a new notification in textFieldDidBeginEditing
  • make ViewController class subscribe to this notification and handle it.
  • The key point is that an object can have only one delegate ; so you cannot trigger textFieldDidBeginEditing() in both the calling code as well as the one inside the subclass by invoking the delegate. But notification as I described will allow to get a similar result.

  • @Claude31 Thanks for your explanation. It seems to me that, in general, when a subclass makes use of the delegate, it runs the risk of having the calling code hijack the functionality of the subclass either deliberately or unintentionally by also using the delegate. Something to be mindful of.

  • Yes, need to be careful. You are not the only one to have met the issue. This may be of interest for you: https://stackoverflow.com/questions/35763244/implementing-a-delegate-method-in-a-subclass-whose-superclass-conforms-to-that-p

@Claude31 How do I "close the thread" for my question?

  • You did it perfectly by marking the correct answer. That "closes" the thread, showing readers that you got what you needed.

Add a Comment