vcvcvcvc
vcvcvcvc

Reputation: 167

Calling resignFirstResponder doesn't work within overrides firstResponder

How to focus activated UITextField with UIResponder?

Code below:

//  customTextField.swift
import UIKit
class customTextField: UITextField {

override func becomeFirstResponder() -> Bool {
    if self.tag == 1 {
        self.borderStyle = .Line
        self.superview?.viewWithTag(2)?.resignFirstResponder()
        self.superview?.viewWithTag(3)?.resignFirstResponder()
    } else if self.tag == 2 {
        self.borderStyle = .Line
        self.superview?.viewWithTag(1)?.resignFirstResponder()
        self.superview?.viewWithTag(3)?.resignFirstResponder()            
    } else {
        self.borderStyle = .Line
        self.superview?.viewWithTag(1)?.resignFirstResponder()
        self.superview?.viewWithTag(2)?.resignFirstResponder()
    }
    return true
}
override func resignFirstResponder() -> Bool {
    print("This is called")
    self.borderStyle = .None
    super.resignFirstResponder()
    return true
}}

I would like to check which textfield is the firstResponder through View's tag. And then if other TextField should be unfocused by resignFirstResponder.

But, the resignFirstResponder within the becomefirstResponder's code block isn't even called when I touch a textField in the view.

Upvotes: 3

Views: 3653

Answers (1)

SwiftArchitect
SwiftArchitect

Reputation: 48514

Do not break the superclass: Pass messages along

In the process of overwriting system messages, like becomeFirstResponder, you may inadvertently disable the parent class. The safest, no-op becomeFirstResponder is this:

override func becomeFirstResponder() -> Bool {
    return super.becomeFirstResponder()
}

Omitting super.becomeFirstResponder() will compromise UITextField.

Also let the original logic (when to become or not become a responder) take its course. Always responding true or false changes the behavior of the parent class, which may have unwanted and unfortunate side effects.

    override func becomeFirstResponder() -> Bool {
        let becomeFirstResponder = super.becomeFirstResponder()
        // Do stuff
        return becomeFirstResponder
    }

Do not replicate the OS

No need to keep track of the current responder.
Let the OS message you of who is the responder: attempting to do the tracking otherwise may actually not work when the onscreen keyboard gets dismissed without a new field being tapped, and is bound to break. Instead, listen to resignFirstResponder in the same manner:

override func resignFirstResponder() -> Bool {
    return super.resignFirstResponder()
}

Less code, more flexibility

Putting both rules together, and using .RoundedRect & .Line for an explicit redraw (.None has no effect), your entire CustomTextField could look like so:

class CustomTextField: UITextField {

    override func becomeFirstResponder() -> Bool {
        let becomeFirstResponder = super.becomeFirstResponder()
        if becomeFirstResponder {
            self.borderStyle = .line
        }
        return becomeFirstResponder
    }

    override func resignFirstResponder() -> Bool {
        let resignFirstResponder = super.resignFirstResponder()
        if resignFirstResponder {
            self.borderStyle = .roundedRect
        }
        return resignFirstResponder
    }
}

Notice that no action is taken if super.becomeFirstResponder() or super.resignFirstResponder() did not succeed.


Demo

Animation


► Find this solution on GitHub and additional details on Swift Recipes.

Upvotes: 8

Related Questions