-1

I am a swift beginner. I implemented this functionality in ViewController, but I don't want to write such code in every ViewController. I want to implement it through a protocol, but something is wrong.

import UIKit

@objc protocol TextFieldAutoHiddenKeyboard: class {
    var textFieldAutoHidenLenth: UInt {get set} 
}

extension TextFieldAutoHiddenKeyboard where Self: UIViewController {

    func autoHiddenKeyboardWhenFillUpTextFiled(textField textF: UITextField, autoHidenLenth: UInt) {
        textFieldAutoHidenLenth = autoHidenLenth

        let textFieldDidChangeActionName = "textFieldDidChange(textField:)"
        let textFieldDidChangeAction = Selector(textFieldDidChangeActionName)
        textF.addTarget(self, action: textFieldDidChangeAction, for: .editingChanged)

        let dismissKeyboardActionName = "dismissKeyboard"
        let dismissKeyboardAction = Selector(dismissKeyboardActionName)
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: dismissKeyboardAction)
        view.addGestureRecognizer(tap)
      //textF.addTarget(self, action: #selector(textFieldDidChange(textField:autoHidenLenth:)), for: .editingChanged)
    }

    func textFieldDidChange(textField: UITextField) {
        if let text = textField.text {
            if text.count == textFieldAutoHidenLenth {
                textField.resignFirstResponder()
            }
        }
    }

    func dismissKeyboard() {
        view.endEditing(true)
    }

    //@objc func textFieldDidChange(textField: UITextField, autoHidenLenth: UInt) {
    //    if let text = textField.text {
    //        if text.count == autoHidenLenth {
    //           textField.resignFirstResponder()
    //        }
    //    }
    //}
}
  1. Why my app crash. EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  2. By creating an Action using a Selector, #selector(textFieldDidChange(textField:autoHidenLenth:)), I don't know how to pass the second argument to function. For example, the parameter autoHidenLenth.
  3. How to implement this function correctly through Protocol?

Demo picture
(source: recordit.co)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
  • Would you please provide the full crashlog. – Faysal Ahmed Aug 08 '18 at 05:33
  • You need to just take TextField delegate method and check inputted length of greater than 6 or is is not backspace character then don't allow to add it to textfield. – PiyushRathi Aug 08 '18 at 05:37
  • You don't need any of this code. Just subclass viewController and try what i have posted as an answer. – Rakesha Shastri Aug 08 '18 at 06:04
  • @Lal Krishna and @Rakesha Shastri answer is really helping me. I try to implement this function using the delegate method ` func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { } `.I think it is very unsatisfactory. Don't forget that users may paste content through the phone clipboard. – UninhibitedSoul Aug 08 '18 at 07:30
  • You can prevent the user's copy paste from working if the result is more than 6 characters. I have updated my answer with this logic. – Rakesha Shastri Aug 08 '18 at 08:58

4 Answers4

0

You have to implement a delegate method for this:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
         let  char = string.cString(using: String.Encoding.utf8)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
        print("Backspace was pressed")
        return true
    }
    if((textField.text?.length)! == 6){
        self.view.endEditing(true)
        return true
    }
    return true
    }

and dont forget to assign Delegate

textfield.delegate = self
Taimoor Suleman
  • 1,588
  • 14
  • 29
0

You are using wrong delegate of textfield. you should use delegate of textfield which is respond on change of character not field change.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let length = (yourtextfield.text?.characters.count)! - range.length + string.characters.count
    if length == 6 {

       yourtextfield.resignFirstResponder()
        return true

    }else {
        return true
    }    
}
Rakesh Patel
  • 1,673
  • 10
  • 27
  • OP clearly wants the textField dismissed after the sixth character has been entered. So he is using the right delegate. This is wrong. – Rakesha Shastri Aug 08 '18 at 05:51
  • As @RakeshaShastri say,I wants the textField dismissed after the sixth character has been entered. I try you code.[Test you code](http://g.recordit.co/sstEUUkHXb.gif). And [This is I want](http://g.recordit.co/JhyTVQSkTr.gif). – UninhibitedSoul Aug 08 '18 at 06:48
  • @ UninhibitedSoul just change length == 6 to 7 and you will get your desired output. – Rakesh Patel Aug 08 '18 at 06:57
  • @ Rakesh Patel I don't think so.Imagine if you have a six-digit verification code, would you enter one more number after entering the verification code? This is unreasonable – UninhibitedSoul Aug 08 '18 at 07:34
  • @ UninhibitedSoul its depend on your requirement. I only write a code to dismiss keyboard after a 6 character get by textfield. – Rakesh Patel Aug 08 '18 at 07:58
0

You should not construct the selector with a string literal but rather use #selector(textFieldDidChange(textField:)), then you will see that a selector method needs to be exposed to Objective C.
Thus you should be able to fix it by writing:

@objc func textFieldDidChange(textField: UITextField) {
    if let text = textField.text {
        if text.count == textFieldAutoHidenLenth {
            textField.resignFirstResponder()
        }
    }
}
Robin M.
  • 96
  • 1
  • 5
  • As for the second / third problem: why don't you create a subclass rather than a protocol for this functionality? – Robin M. Aug 08 '18 at 05:53
0

As you need to enable this feature for multiple textfields, you can use this.

References: Link1, Link2

private var autoHiddenLengthAssociationKey: UInt8 = 0

extension UITextField {

    private var autoHiddenLenth: Int? {
        get {
            return objc_getAssociatedObject(self, &autoHiddenLengthAssociationKey) as? Int
        }
        set {
            objc_setAssociatedObject(self, &autoHiddenLengthAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN)
        }
    }

    func enableAutoHidingFeature(maxLength: Int) {
        autoHiddenLenth = maxLength
        self.addTarget(self, action: #selector(textDidChange(textField:)), for: .editingChanged)
    }

    @objc func textDidChange(textField: UITextField) {

        guard let maxLength = autoHiddenLenth, let text = textField.text else { return }

        if text.count == maxLength {
            textField.resignFirstResponder()
        }
    }
}

You can enable this by calling:

textField.enableAutoHidingFeature(maxLength: 6)
Lal Krishna
  • 15,485
  • 6
  • 64
  • 84