How to limit length and special characters of UItextField? - ios

I'm able to limit the length using the code below, but I can't seem to find a way to also limit special characters.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let newLength = count(textField.text.utf16) + count(string.utf16) - range.length
if (textField.placeholder == "USERNAME")
{
//Also limit special characters here
return newLength <= 15 // Bool
}
characters I want allowed:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.
I tried following this link but its in objective C and I'm having trouble merging it with my current 15 character limit code above

Alternative to regex, you can first get all the allowed characters into a set:
var charactesAllowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."
var charactersSet = [Character](charactesAllowed)
then try to see if the most recently typed charactes is in this array
var newCharacter = //whatever the character is, ex: "A"
if(contains(charactersSet, newCharacter))
{
println("Allowed")
// Add it into the label text
}

let ACCEPTABLE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
if textField == textFieldNumber{
let characterSet = CharacterSet.init(charactersIn: ACCEPTABLE_CHARACTERS).inverted
maxLength = 11
let currentString: NSString = textField.text! as NSString
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
let filter = string.components(separatedBy: characterSet).joined(separator:"")
return ((string == filter) && newString.length <= maxLength!)
}

Related

How do I limit text lengths for different UITextFields in Swift?

I have an iOS Xcode 7.3 Swift2 project I'm working on. It has different UITextFields that are limited to 3 digits, specifically only numbers. They are assigned to the UITextFieldDelegate and it's working well.
Here is where I limit them:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.characters.count + string.characters.count - range.length
let limitLength = 3
if newLength > limitLength {
return false
}
let numberOnly = NSCharacterSet.init(charactersInString: "0123456789")
let stringFromTextField = NSCharacterSet.init(charactersInString: string)
let strValid = numberOnly.isSupersetOfSet(stringFromTextField)
return strValid
}
However, some of the UITextFields need to be limited to numbers still AND also limited to a single digit, how can I institute this in the section above, only for those specific UITextFields?
The names of the UITextFields that need to be single digits are:
widthInches
lengthInches
I tried placing this after the first guard section with no luck:
guard let text2 = widthInches.text else { return true }
let newLength2 = text2.characters.count + string.characters.count - range.length
let limitLength2 = 3
if newLength2 > limitLength2 {
return false
}
You can also try this code for limit textfield
actually i am using here textfield tag. Because custom textfield.
If you using custom textfield like TextfieldEffect in this condition tag will help you for limit of Textfield.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{
if textField.tag == txtCountryCode.tag{
let maxLength = 4
let currentString: NSString = textField.text!
let newString: NSString =
currentString.stringByReplacingCharactersInRange(range, withString: string)
return newString.length <= maxLength
}
if textField.tag == txtMobileNumber.tag{
let maxLength = 10
let currentString: NSString = textField.text!
let newString: NSString =
currentString.stringByReplacingCharactersInRange(range, withString: string)
return newString.length <= maxLength
}
return true
}
I hope this will help you.
The function shouldChangeCharactersInRange passes in the particular textField as one of its parameters. You can look at that and see if it points to the same instance as the ones you want to shorten, like this:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
var limitLength = 3
if textField == widthInches || textField == lengthInches {
limitLength = 1
}
let newLength = text.characters.count + string.characters.count - range.length
if newLength > limitLength {
return false
}
let numberOnly = NSCharacterSet.init(charactersInString: "0123456789")
let stringFromTextField = NSCharacterSet.init(charactersInString: string)
let strValid = numberOnly.isSupersetOfSet(stringFromTextField)
return strValid
}
Assuming all other requirements are the same (numbers only) this will do the trick.
There are other ways, for example - you could subclass UITextField and add a limitLength field, then use that field in the delegate, but that's probably overkill for just 2 exceptions.
Hello in your func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool the textField param is the textField that has trigger this event so you can check with yours textfields objects and if are equal to one of them then make a different behavior
I hope this helps you,
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return (textField.text?.utf16.count ?? 0) + string.utf16.count - range.length <= TEXT_FIELD_LIMIT
}
This counts the number of characters based on UTF-16 representation, as range.length is given in UTF-16 base. If you need to count the number of characters in other ways, the expression may get longer. If you want only numbers to be input use textField.keyboardType = UIKeyboardTypeDecimalPad . If you want specific textFields then add tags and compare them and if they are equal you can implement your specific code for that.
Check this link for detailed answer :
http://www.globalnerdy.com/2016/05/24/a-better-way-to-program-ios-text-fields-that-have-maximum-lengths-and-accept-or-reject-specific-characters/
update for swift 3 add this class and call it TextField.swift. it will add the limit input on the storyboard.
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self] else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
// Any text field with a set max length will call the limitLength
// method any time it's edited (i.e. when the user adds, removes,
// cuts, or pastes characters to/from the text field).
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.characters.count > maxLength else {
return
}
// If the change in the text field's contents will exceed its maximum
length,
// allow only the first [maxLength] characters of the resulting text.
let selection = selectedTextRange
// text = prospectiveText.substring(with:Range<String.Index>
(prospectiveText.startIndex ..< prospectiveText.index(after: maxLength))
let s = prospectiveText
// Get range 4 places from the start, and 6 from the end.
let c = s.characters;
let r = c.index(c.startIndex, offsetBy: 0)..<c.index(c.endIndex, offsetBy: maxLength - c.count)
text = s[r]
// Access the string by the range.
selectedTextRange = selection
}
}
or download here - >TextField.swift

Swift UITextField Decimal Field Type - No Pasting, 1 Decimal Point Only and Length Limit

I am trying to do some error checking for an text field which will only accept a decimal value. This means that the text field cannot be pasted into, the user can only enter 1 decimal point and my personal preference I want a length limit of 2 characters after the decimal place.
I have got the current code which simply restricts the paste:
//Does not allow pasting into text field
//https://stackoverflow.com/questions/26919854/how-can-i-declare-that-a-text-field-can-only-contain-an-integer
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let invalidCharacters = NSCharacterSet(charactersInString: "0123456789.").invertedSet
return string.rangeOfCharacterFromSet(invalidCharacters, options: [], range: string.startIndex ..< string.endIndex) == nil
}
It works, but I want other features as well. How can I implement these?
To check whether It has a decimal point and limit 2 decimal place you can use like this :-
let str = "456.23" // Your Label Value
let nsStr = NSString(string: str) // Conver label value to NSString
let strSplit = str.characters.split(".") // split label before decimal point and after decimal point
if str.containsString(".") {
//Contains Decimal value
if String(strSplit.last!).characters.count <= 2 {
// Contains 2 or 1 decimal values
} else {
// More than 2 decimal values
}
} else {
//Do not contains Decimal value
}
you can do something like,
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var dotLocation = Int()
let nonNumberSet = NSMutableCharacterSet() //create an empty mutable set
nonNumberSet.formUnionWithCharacterSet(NSCharacterSet.URLQueryAllowedCharacterSet())
nonNumberSet.addCharactersInString("0123456789.")
//allow backspace
if range.length == 0 && string.characters.count == 0
{
return true
}
if string == "."
{
if range.location == 0{
return false
}
if dotLocation == 0 {
dotLocation == range.location
return true
}
else{
return false
}
}
if range.location == dotLocation && string.characters.count == 0{
dotLocation = 0
}
var newStr = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
newStr = (newStr.componentsSeparatedByCharactersInSet(nonNumberSet) as NSArray).componentsJoinedByString("")
textField.text = newStr
return false
}
Hope this will help :)

Issue limiting decimal length [duplicate]

This question already has answers here:
Swift playground - How to convert a string with comma to a string with decimal
(5 answers)
Closed 7 years ago.
That the code I've used to limit to max 1 decimal number in all my UITexField :
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let computationString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
let arrayOfSubStrings = computationString.componentsSeparatedByString(",")
if (arrayOfSubStrings.count == 1 && computationString.characters.count > 2) {
return false
} else if arrayOfSubStrings.count == 2 {
let stringPostDecimal = arrayOfSubStrings[1]
return stringPostDecimal.characters.count <= 1
}
return true
}
But I've just understand that there are two type of Decimal Pad: with dot and with comma.
For example my iOS Simulator use dot and my iPhone use comma.
So how can I fix that problem, stopping both of them?
There's a method componentsSeparatedByCharactersInSet to consider more than one separation character
let commaDotCharacterSet = NSCharacterSet(charactersInString: ",.")
let arrayOfSubStrings = computationString.componentsSeparatedByCharactersInSet(commaDotCharacterSet)
or to include also the decimal separator of the current locale
let localeDecimalSeparator = NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator) as! String
let commaDotLocaleSeparatorCharacterSet = NSCharacterSet(charactersInString: ",." + localeDecimalSeparator)
let arrayOfSubStrings = computationString.componentsSeparatedByCharactersInSet(commaDotLocaleSeparatorCharacterSet)
Try This.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let computationString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
let arrayOfSubStrings = computationString.computationString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: ".,"))
if (arrayOfSubStrings.count == 1 && computationString.characters.count > 2) {
return false
} else if arrayOfSubStrings.count == 2 {
let stringPostDecimal = arrayOfSubStrings[1]
return stringPostDecimal.characters.count <= 1
}
return true
}

How to allow only certain set of numbers in a UITextfield in swift 2.0

I am having a UITextField in which i get the month number as input. I am successful in limiting the no of characters to 2 in the UITextField. But i want users to enter only the values from 1 to 12 and none other than that. This has to be done simultaneously when the user types the numbers i.e in func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool. If i use a simple if condition to check the each character and return false in else part the textfield won't allow me to use clear or retype any other character. someone help me.
Set keyboard type as Number Pad
add this
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
if let text = textField.text {
let newStr = (text as NSString)
.stringByReplacingCharactersInRange(range, withString: string)
if newStr.isEmpty {
return true
}
let intvalue = Int(newStr)
return (intvalue >= 0 && intvalue <= 12)
}
return true
}
You can do it simultaneously by checking the TextField value inside shouldChangeCharactersInRange.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let inputStr = textField.text?.stringByAppendingString(string)
let inputInt = Int(inputStr!)
if inputInt > 0 && inputInt < 13 {
return true
} else {
return false
}
}
=> you can Define limite of char like this:-
#define NUMBERS_ONLY #"1234567890"
#define CHARACTER_LIMIT 2
=> and based on define limit char you can use and try it below method :-
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSUInteger newLength = [textField.text length] + [string length] - range.length;
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:NUMBERS_ONLY] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:#""];
return (([string isEqualToString:filtered])&&(newLength <= CHARACTER_LIMIT));
}
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
// Create an `NSCharacterSet` set which includes everything *but* the digits
let inverseSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
// At every character in this "inverseSet" contained in the string,
// split the string up into components which exclude the characters
// in this inverse set
let components = string.componentsSeparatedByCharactersInSet(inverseSet)
// Rejoin these components
let filtered = components.joinWithSeparator("") // use join("", components) if you are using Swift 1.2
// If the original string is equal to the filtered string, i.e. if no
// inverse characters were present to be eliminated, the input is valid
// and the statement returns true; else it returns false
return string == filtered
}
see this link-- Limit UITextField input to numbers in Swift
Check out this to set Limit the numbers and allow only numbers 0 to 9.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == mobileNumber {
let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
let compSepByCharInSet = string.components(separatedBy: aSet)
let numberFiltered = compSepByCharInSet.joined(separator: "")
let length = (mobileNumber.text?.count)! + string.count - range.length
return string == numberFiltered && length <= LIMIT
}else if textField == userType {
return false
}
return true
}
I just want to post a more simplified answer based on the previous answers.
Tested on Swift 5.1
Considering that you already set textField.keyboardType = .numberPad, then you can do the following:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else {
return true
}
let newStr = (text as NSString).replacingCharacters(in: range, with: string)
guard let intValue = Int(newStr) else {
return true
}
return intValue <= maxNumber // maxNumber: replace with your max number
}
You dont´need to validate that intValue is greater or equal to 0 because in numberPad you can NOT write negative values.

removal of bridgeToObjective-C means I can't use NSRange

I have a viewController where I can enter some text into a textField and tap a done button to save it. I only want the done button to be visible if there is text in the textField. In order to do this, I used the delegate method for the UITexfield which fires when it is about to be edited as shown below. As it passes in an NSRange, I can't put that into stringByReplacingCharactersInRange as swift only allows a Range. Therefor I bridged it which allowed me to use the NSRange given. If you know a way to cast an NSRange as a Range, or even better, if you know a more concise and neater way to check if the text field is empty, please let me know. Thanks a lot.
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let newString = textField.text.bridgeToObjectiveC().stringByReplacingCharactersInRange(range, withString: string)
if (newString == "" ) {
self.doneButton.enabled = false
} else {
self.doneButton.enabled = true
}
return true
}
Here is a func that will take an NSRange and replace a portion of a String:
func replaceRange(nsRange:NSRange, #ofString:String, #withString:String) ->String {
let start = nsRange.location
let length = nsRange.length
let endLocation = start + length
let ofStringLength = countElements(ofString)
if start < 0 || endLocation < start || endLocation > ofStringLength {
return ofString
}
var startIndex = advance(ofString.startIndex, start)
var endIndex = advance(startIndex, length)
var range = Range(start:startIndex, end:endIndex)
var final = ofString.stringByReplacingCharactersInRange(range, withString:withString)
return final
}
var original = "🇪🇸😂This is a test"
var replacement = "!"
var nsRange:NSRange = NSMakeRange(1, 2)
var newString = replaceRange(nsRange, ofString:original, withString:replacement)
println("newString:\(newString)")
Output:
newString:🇪🇸!his is a test
Instead of using bridgeToObjectiveC() simply cast your string to an NSString:
let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
Here's what I like to use:
if ([textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length > 0)
{
// do something with the text that is there ...
}

Resources