how to set the maximum 4 digit number in uitextfield using swift3.0 - swift3

I use this code but it should return anyone. But I need to use this two scenarios. So how can I change this? Anyone help me. I restrict a (.0 symbol and at the same time only allowed 4 digit amount. How can i do this.???
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = amountField.text else { return true }
let newLength = text.characters.count + string.characters.count - range.length
return newLength <= 4 // Bool
let ACCEPTABLE_CHARACTERS = "1234567890"
let cs = CharacterSet(charactersIn: ACCEPTABLE_CHARACTERS).inverted
let filtered: String = string.components(separatedBy: cs).joined(separator: "")
return string == filtered
}

Possible solution for your issue.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var newLength = string.utf16.count - range.length
if let text = textfield.text {
newLength = text.utf16.count + string.utf16.count - range.length
}
let characterSet = NSMutableCharacterSet()
characterSet.addCharacters(in: "1234567890")
if string.rangeOfCharacter(from: characterSet.inverted) != nil || newLength > 4 {
return false
}
return true
}

Related

Characters Validation of the Special Characters in the Text field

I want to change the background color of the view, while the text field has the Special characters like 123!#%^&() etc otherwise background colors should be the same color.
I have implemented but changing according to the each character it should not be like this if in entire text field any special character means color has to change.
Here is my code.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let specialCharacters = "!~`##$%^&*-+();:={}[],.<>?\\/\"\'"
let searchText = textField.text! + string
//
let character = CharacterSet(charactersIn: specialCharacters)
if (string.rangeOfCharacter(from: character) != nil){
print("matched")
self.view.backgroundColor = UIColor.gray
}else
{
self.view.backgroundColor = UIColor.white
}
Please find below working solutions Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
                let specialCharacters = "!~`##$%^&*-+();:={}[],.<>?\\/\"\'"
                var searchText = textField.text! + string
              
                let characterSet = CharacterSet(charactersIn: specialCharacters)
                
                if string == "" {
                        searchText.removeLast()
                }
                
                if (string.rangeOfCharacter(from: characterSet) != nil) {
                        print("matched")
                        self.view.backgroundColor = UIColor.gray
                        textField.textColor = UIColor.white
                        return true
                } else  if (searchText.rangeOfCharacter(from: characterSet) == nil  ) {
                        self.view.backgroundColor = UIColor.white
                        textField.textColor = UIColor.black
                        return true
                }
                return true
        }

textfield validation for password and email

I want to implement the textfield validation for two textfielfd separately but I m unable to do so I have uplide delegates for both but they get applied for both the text fields for eg. email accepts only 10 chars how can we apply it separately for both email and mobile no separately Here is the code I have written:
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
if textField == textfieldemail
{
let providedEmailAddress = self.textfieldemail.text
let isEmailAddressValid = isValidEmailAddress(emailAddressString: providedEmailAddress!)
if isEmailAddressValid
{
print("Email address is valid")
}
else {
self.displayAlertMessage(messageToDisplay: "Email address is not valid")
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let charsLimit = 10
let startingLength = textField.text?.characters.count ?? 0
let lengthToAdd = string.characters.count
let lengthToReplace = range.length
let newLength = startingLength + lengthToAdd - lengthToReplace
return newLength <= charsLimit
}
You can do that by giving tag to textfield and check that tag inside the fun to identify the textfield uniquely.
Assign UITextFieldDelegate to your respective ViewController
set delegate for the textField
emailIDTxtFld.delegate = self
mobileNoTxtFld.delegate = self
set tag for the textField
emailIDTxtFld.tag = 1001
mobileNoTxtFld.tag = 1002
Now implement these methods
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if textField.tag == 1002{
//For MobileNo it will accept only 10 char
if newString.characters.count > 10{
return false
}else{
return true
}
}else if textField.tag == 1002{
//your logic
}
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.tag == 1001{
if textField.isValidEmail(){
// Code
}else{
print("Please Enter Vlaid Email ID")
}
}else if textField.tag == 1002{
if textField.text?.characters.count == 10{
// code
}else{
//code
}
}

UICollectionView reloadData() crashes after emtying datasource dictionary

I have a UICollectionView which takes the results of an API search. The search is triggered by the following code. The results are appended to a dictionary [[String: Any]] and I call self.collectionView.reloadData() after my query completes.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var newValue = textField.text!
let location = min(range.location, newValue.characters.count)
let startIndex = newValue.characters.index(newValue.startIndex, offsetBy: location)
let endIndex = newValue.characters.index(newValue.startIndex, offsetBy: location + range.length)
let newRangeValue = Range<String.Index>(startIndex ..< endIndex)
newValue.replaceSubrange(newRangeValue, with: string)
searchView.searchFieldValueChanged(newValue)
return true
}
Then, if I want to change the search string and search again I want to empty the dictionary and call reloadData() again I get an app crash.
The error is
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist:
Here is my datasource implementation
var searchResults = [[String: Any]]()
let layout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
layout.estimatedItemSize.height = 200
layout.estimatedItemSize.width = 200
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
return layout
}()
collectionView = UICollectionView(frame: self.frame, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(LanesCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView.backgroundColor = .yellow // Constants.APP_BACKGROUND_COLOR
collectionView.alwaysBounceVertical = true
collectionView.clipsToBounds = true
collectionView.translatesAutoresizingMaskIntoConstraints = false
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if searchResults.count == 0 {
collectionView.alpha = 0.0
} else {
collectionView.alpha = 1.0
}
return searchResults.count
}
after query
func parseMoreData(jsonData: [String: Any]) {
let items = jsonData["items"] as! [[String: Any]]
self.collectionView.layoutIfNeeded()
self.collectionView.reloadData()
}
}
func searchFieldValueChanged(_ textValue: String) {
searchResults = []
This looks fixed by using this instead of layoutIfNeeded()
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()

Swift 3: How to apply attributes to parts of string

So the method I used in Swift 2 no longer works because of changes in Swift 3, regarding String indices and ranges. Previously I had
func configureLabel(defaultColor: UIColor, highlightColor: UIColor, boldKeyText: Bool) {
if let index = self.text?.characters.indexOf(Character("|")) {
self.text = self.text!.stringByReplacingOccurrencesOfString("|", withString: "")
let labelLength:Int = Int(String(index))! // Now returns nil
var keyAttr: [String:AnyObject] = [NSForegroundColorAttributeName: highlightColor]
var valAttr: [String:AnyObject] = [NSForegroundColorAttributeName: defaultColor]
if boldKeyText {
keyAttr[NSFontAttributeName] = UIFont.systemFontOfSize(self.font.pointSize)
valAttr[NSFontAttributeName] = UIFont.systemFontOfSize(self.font.pointSize, weight: UIFontWeightHeavy)
}
let attributeString = NSMutableAttributedString(string: self.text!)
attributeString.addAttributes(keyAttr, range: NSRange(location: 0, length: (self.text?.characters.count)!))
attributeString.addAttributes(valAttr, range: NSRange(location: 0, length: labelLength))
self.attributedText = attributeString
}
}
Basically I would be able to take a string like "First Name:| Gary Oak" and have all the parts before and after the | character be different colors, or make part of it bold, but the line I commented above no longer returns a value, which breaks everything else afterwards. Any ideas on how to do this?
In Swift 3 you can use something like this:
func configureLabel(defaultColor: UIColor, highlightColor: UIColor, boldKeyText: Bool) {
if let index = self.text?.characters.index(of: Character("|")) {
self.text = self.text!.replacingOccurrences(of: "|", with: "")
let position = text.distance(from: text.startIndex, to: index)
let labelLength:Int = Int(String(describing: position))!
var keyAttr: [String:AnyObject] = [NSForegroundColorAttributeName: defaultColor]
var valAttr: [String:AnyObject] = [NSForegroundColorAttributeName: highlightColor]
if boldKeyText {
keyAttr[NSFontAttributeName] = UIFont.systemFont(ofSize: self.font.pointSize)
valAttr[NSFontAttributeName] = UIFont.systemFont(ofSize: self.font.pointSize, weight: UIFontWeightHeavy)
}
let attributeString = NSMutableAttributedString(string: self.text!)
attributeString.addAttributes(keyAttr, range: NSRange(location: 0, length: (self.text?.characters.count)!))
attributeString.addAttributes(valAttr, range: NSRange(location: 0, length: labelLength))
self.attributedText = attributeString
}
}
the main idea that using let position = text.distance(from: text.startIndex, to: index) you got not the integer representation of string position but the string Index value. Using text.distance(from: text.startIndex, to: index) you can find int position for string Index

allow only letters for User Name

I'm trying to limit the users First_Name to allow only letters so it should respond with an error for numbers or special characters after clicking on send_button. I found some examples here but because they are build on older swift version I'm having problems to make it work. Until now I've managed to read the First Name and throw an error if the first element on this textfield is not a letter but the code allow things like this (First name = "J123g") or ("Mark##$") and I don't want this to be the case.
func isOneLetter(in text: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: "[a-zA-Z]")
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
let temp = results.map { nsString.substring(with: $0.range)}
return temp.isEmpty
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return false
}
}
#IBAction func Send_Button(_ sender: AnyObject) {
let UserName = First_Name.text
if (isOneLetter(in: UserName!))
{
DisplayMyAlertMessage(userMessage: "First Name must contain only letter and spaces")
}
}
Your regex pattern checks for one alphanumeric character which matches both unwanted examples.
This regex checks for from beginning (^) to the end ($) of the string there must be one or more (+) alphanumeric characters ([a-zA-Z]). The benefit is that it treats an empty string also as bad.
^[a-zA-Z]+$
1st you have to inherit the UITextViewDelegate class with you own
class
class ViewController: UIViewController, UITextViewDelegate {
2nd add an IBOutlet
#IBOutlet weak var firstName: UITextField!
3rd you have to assure this object is using
override func viewDidLoad() {
super.viewDidLoad()
firstName.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == firstName {
let allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
let typedCharacterSet = CharacterSet(charactersIn: string)
let alphabet = allowedCharacterSet.isSuperset(of: typedCharacterSet)
return alphabet
}
}
Another way could be:
let userInput = ""
let set = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ")
if userInput.rangeOfCharacter(from: set.inverted) != nil {
print("ERROR: There are numbers included!")
}
Updated for swift 3:
if you want to validate name and allow only letters for User Name then used below simple lines of code :
// function definition:
func isValidName(_ nameString: String) -> Bool {
var returnValue = true
let mobileRegEx = "[A-Za-z]{3}" // {3} -> at least 3 alphabet are compulsory.
do {
let regex = try NSRegularExpression(pattern: mobileRegEx)
let nsString = nameString as NSString
let results = regex.matches(in: nameString, range: NSRange(location: 0, length: nsString.length))
if results.count == 0
{
returnValue = false
}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
returnValue = false
}
return returnValue
}
// function call :
var firstName = mEnterFirstNameTextField.text!
let isFirstNameValid = isValidName(firstName)
if isFirstNameValid{
// do user logic
}else{
// show error msg: -> "Enter name is not valid, please enter again..."
}