Max Character Limit for TextField - ios

I am looking to cap the amount of characters a user can type into a textfield at 14. Here is the code that I have found documentation on.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = userNameTextField.text?.characters.count ?? 0
if (range.length + range.location > currentCharacterCount){
return false
}
let newLength = currentCharacterCount + string.characters.count - range.length
return newLength <= 14
}
but I do not feel that I am implementing this correctly. I have set
userNameTextField.delegate = self
in the viewDidLoad, and I am conforming to the UITextFieldDelegate protocol.

You state you are using Swift 3. The signature of many methods changed in Swift 3. You need to use:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
}
Not the old signature posted in your question.
If it's still not being called, then you never set the text field's delegate property.

Try this instead:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let currentString: NSString = (textField.text ?? "") as NSString
let newString = currentString.replacingCharacters(in: range, with: string)
return newString.characters.count <= 14
}

Try this for swift 3:
let limit=4;
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = txtSMSCode.text else { return true }
let newLength = text.characters.count + string.characters.count - range.length
return newLength <= limit
}

Related

Disallowing empty input

How do we disallow empty input on textField?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let oldText = textField.text!
let stringRange = Range(range, in:oldText)!
let newText = oldText.replacingCharacters(in: stringRange, with: string)
doneBarButton.isEnabled = !newText.isEmpty
return true
}
Is there other ways? or using other textField delegate methods to do this?
Yes, this is basically how you do it.
Alternatively, if all you want to know if the field is empty or not, just figure out the length of the result:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let length = (textField.text?.count ?? 0) + string.count - range.length
doneBarButton.isEnabled = length > 0
return true
}
You may also want to set enablesReturnKeyAutomatically for the text field to true, so that the keyboard doesn’t enable the “return” key unless there is text in the field.

Swift 4 shouldChangeCharactersIn only show length = 12

I have textfield ,shouldChangeCharactersIn codes im using barcode, system and my barcode codes == 12 characters. I want to show only 12 characters in my string if barcode small 12 characters or big must be return false or clean text field . my codes under below. When I using barcode if my barcodes 12 characters working fine , but if I read 12 character low don't clear textfield ,
How can I fix it ?
My codes under below.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let length = (Mytext.text?.characters.count)! - range.length + string.characters.count
if length == 13 {
print("Mytext=\(Mytext.text!)")
Mytext.text = ""
return true
}else {
return true
}
}
Output when read 12 characters barcode shows success !
When read 12 characters low don't clear Mytext field , and when I read again adding +
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (string != "" && textField.text?.count == 12) {
print("Mytext = \(textField.text!)")
textField.text = ""
return false
}
return true
}
you have to set replacement string to "" blank string
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let length = (Mytext.text?.characters.count)! - range.length + string.characters.count
if length == 13 {
print("Mytext=\(Mytext.text!)")
Mytext.text = ""
return true
}else {
return true
}
}

EXC_BREAKPOINT 0x00000000e7ffdefe in Crashlytics

I'm getting the following error in Crashlytics
LoginViewController.swift line 277
LoginViewController.textField(UITextField,
shouldChangeCharactersInRange : _NSRange, replacementString : String)
-> Bool
There's the code in my project:
// MARK: - UITextFieldDelegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == txtSearch && string != "\n"{
var substring: String = textField.text!
substring = (substring as NSString).stringByReplacingCharactersInRange(range, withString: string)
self.searchAutocompleteEntriesWithSubstring(substring)
}
return true;
}
This error ocurrs only sometimes
You are force unwrapping your textField.text! which may be nil. Try below code:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == txtSearch && string != "\n"{
if let text = textField.text {
let substring = (text as NSString).stringByReplacingCharactersInRange(range, withString: string)
self.searchAutocompleteEntriesWithSubstring(substring)
}
}
return true
}

How do I cut off the text view after 20 characters?

I am trying to limit the number of characters inside the text view to 20. After 20 it should instead have "...". The function is not firing and I am setting the delegate correctly.
Animal class
cell.pn.text = np[indexPath.row]
cell.pn.selectable = false
cell.pn.delegate = self
Extension of Animal class
extension Animal : UITextViewDelegate{
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
return textView.text.characters.count + (text.characters.count - range.length) <= 20
}
}
Try this:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let text = textField.text
let newLength = text.characters.count + string.characters.count - range.length
return newLength <= 20
}
You can use something like this:
if displayName.characters.count > 20 {
displayName = (displayName as NSString).substringToIndex(20)
displayName.appendContentsOf("...")
}

How shouldChangeCharactersInRange works in Swift?

I'm using shouldChangeCharactersInRange as a way of using on-the-fly type search.
However I'm having a problem, shouldChangeCharactersInRange gets called before the text field actually updates:
In Objective C, I solved this using using below:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * searchStr = [textField.text stringByReplacingCharactersInRange:range withString:string];
return YES;
}
However, I've tried writing this in Swift:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let txtAfterUpdate:NSString = self.projectSearchTxtFld.text as NSString
txtAfterUpdate.stringByReplacingCharactersInRange(range, withString: string)
self.callMyMethod(txtAfterUpdate)
return true
}
The method still gets called before I get a value?
Swift 4, Swift 5
This method doesn't use NSString
// MARK: - UITextFieldDelegate
extension MyViewController: UITextFieldDelegate {
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
if let text = textField.text,
let textRange = Range(range, in: text) {
let updatedText = text.replacingCharacters(in: textRange,
with: string)
myvalidator(text: updatedText)
}
return true
}
}
Note. Be careful when you use a secured text field.
stringByReplacingCharactersInRange return a new string, so how about:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
self.callMyMethod(txtAfterUpdate)
}
return true
}
Swift 3 & 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let textFieldText: NSString = (textField.text ?? "") as NSString
let txtAfterUpdate = textFieldText.replacingCharacters(in: range, with: string)
callMyMethod(txtAfterUpdate)
return true
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
callMyMethod("")
return true
}
Swift 2.2
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let textFieldText: NSString = textField.text ?? ""
let txtAfterUpdate = textFieldText.stringByReplacingCharactersInRange(range, withString: string)
callMyMethod(txtAfterUpdate)
return true
}
func textFieldShouldClear(textField: UITextField) -> Bool {
callMyMethod("")
return true
}
Though the textField.text property is an optional, it cannot be set to nil. Setting it to nil is changed to empty string within UITextField. In the code above, that is why textFieldText is set to empty string if textField.text is nil (via the nil coalescing operator ??).
Implementing textFieldShouldClear(_:) handles the case where the text field's clear button is visible and tapped.
In Swift 3 it would look like this:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text: NSString = (textField.text ?? "") as NSString
let resultString = text.replacingCharacters(in: range, with: string)
return true
}
shouldChangeCharactersIn is called on every key press.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// get the current text, or use an empty string if that failed
let currentText = textField.text ?? ""
// attempt to read the range they are trying to change, or exit if we can't
guard let stringRange = Range(range, in: currentText) else { return false }
// add their new text to the existing text
let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
// make sure the result is under 16 characters
return updatedText.count <= 16
}
shouldChangeCharactersInRange
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool { }
This function is called when changes are made but UI is not updated and waiting for your choice
Take a look at returned bool value
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
If you return true - it means that iOS accept changes(text, caret...)
If you return false - it means that you are responsible for all this stuff
Swift 3
If you want to pre-process the characters the user typed or pasted, the following solution workes like a charm
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let strippedString = <change replacements string so it fits your requirement - strip, trim, etc>
// replace current content with stripped content
if let replaceStart = textField.position(from: textField.beginningOfDocument, offset: range.location),
let replaceEnd = textField.position(from: replaceStart, offset: range.length),
let textRange = textField.textRange(from: replaceStart, to: replaceEnd) {
textField.replace(textRange, withText: strippedString)
}
return false
}
Find it here: https://gist.github.com/Blackjacx/2198d86442ec9b9b05c0801f4e392047
This is essentially #Vyacheslav's answer independently arrived at for my own use case, just in case the stylistic approach resonates :-)
func textField(_ textField: UITextField, shouldChangeCharactersIn nsRange: NSRange, replacementString: String) -> Bool {
let range = Range(nsRange, in: textField.text!)!
let textWouldBecome = textField.text!.replacingCharacters(in: range, with: replacementString)
if textWouldBecome != eventModel.title {
self.navigationItem.setHidesBackButton(true, animated: true)
} else {
self.navigationItem.setHidesBackButton(false, animated: true)
}
return true
}
Replace eventModel.title with whatever you're checking for the change against obviously.
To get the exact text in the my UITextField component in Swift 3.0 I used:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let enteredTxt = textField.text! + string
doSomethingWithTxt(enteredTxt) //some custom method
}

Resources