Swift: Limit Characters # in textField without disabling/interfering with other functions - swift

I want to limit the number of characters a user can use in a textField. I took a function from this link: Max length UITextField (Imanou Petit)
However, in my viewDidLoad() I have several textFields that I'm already referencing the delegate because I want the keyboard to "Return" when the user presses the Return key on the keyboard. This I'm doing through the textFieldShouldReturn like this (I also have a touchesBegan method but I want the user to also have the option of the Return key):
func textFieldShouldReturn(textField: UITextField!) -> Bool {
self.stuffOneTextField.resignFirstResponder()
self.linkTextField.resignFirstResponder()
self.descriptionTextField.resignFirstResponder()
self.ogTextField.resignFirstResponder()
self.priceTextField.resignFirstResponder()
return true
}
If I add in this function below to the viewDidLoad, then the 'Return' key on the keyboard doesn't work and it limits ALL of the textFields (I have 5).
override func viewDidLoad() {
super.viewDidLoad()
self.stuffOneTextField.delegate = self
self.linkTextField.delegate = self
self.descriptionTextField.delegate = self
self.ogTextField.delegate = self
self.priceTextField.delegate = self
}
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
return newLength <= limitLength
}
I only need to limit 2 textFields. I've tried just putting in the specific textField name instead of all the textFields as textFields and then it limits 1 textField and doesn't let me type in the others... Very strange...
How do I go around this?
Any help means a lot.

You're being given the textField that is changing characters in the delegate function. Here, you can compare it to the specific fields that you want to limit:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
if !(textField == stuffOneTextField || textField == descriptionTextField) {
return true
}
let newLength = text.characters.count + string.characters.count - range.length
return newLength <= limitLength
}
Also, func textFieldShouldReturn(textField: UITextField!) -> Bool is giving you a text field, and you can call resignFirstResponder() on that, if that is what you want to do there.

Related

Swift add target for paste action UITextField

I have a UITextField that has a target added which performs checks on the field as the user is typing. I currently have an issue however when my code adds text to the textfield in that the text doesn't get checked. Is there a way I can solve this through .editingChanged or is there another UIControlEvent to hook into?
Code is:
NumberOutlet.addTarget(self, action: #selector(handleNumberImage), for: .editingChanged)
The way you can handle this is by implementing the UITextViewDelegate protocol in your viewcontroller. In your viewDidLoad you would want to set the delegate of your UITextField to self.
Then, simply implement the following method, like demonstrated here:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.length > 1 {
// Text was pasted into the text field
// do something with pasted text
} else {
//typed string
}
return true
}
You will want to conform to the UITextFieldDelegate protocol.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let isAddingCharacter: Bool = range.location >= 0 && range.length == 0
let isDeletingCharacter: Bool = range.location >= 0 && range.length == 1
var newCount: Int = textView.text.characters.count
if isAddingCharacter {
newCount += 1
} else if isDeletingCharacter {
newCount -= 1
}
// If the newCount is > 0, the user is entering text
return true
}
Side note, your outlet should be named numberOutlet, not NumberOutlet. It is convention to use camel case syntax for variable names in swift.
The only way I know would be just to call the method you used as selector after you add text via code.
For example you have a method which is executed after you press a button and there you add text to your textfield and the UIControlEvent doesn't get fired here. So just call the method after adding text via code in this example after pressing a button:
#IBAction func buttonPressed(_ sender: UIButton) {
// Note: camel case convention
numberOutlet.text?.append("added text via code")
// perform your check method
handleNumberImage()
}

How to prevent username textfield for not using whitespace or special characters swift 3.0 [duplicate]

I am creating a trivia application that asks for a username on start up. I'd like to make it impossible to use characters such as #$#!^& etc (also including "space"). I took a look at this post here but it is written entirely in Objective-C. Thanks in advance.
Swift 4 iOS 11.2.x based on using an extension, tests to see if a string is a valid hex number in this example.
extension String {
var containsValidCharacter: Bool {
guard self != "" else { return true }
let hexSet = CharacterSet(charactersIn: "1234567890ABCDEFabcdef")
let newSet = CharacterSet(charactersIn: self)
return hexSet.isSuperset(of: newSet)
}
}
You use it like with the UITextFieldDelegate.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return (string.containsValidCharacter)
}
Although I read in an earlier post that CharacterSets do not support characters that are composed of more than one Unicode.Scalar; so use with caution I guess.
Since you're explicitly asking for Swift, I've translated the top asnwer in the linked question.
let notAllowedCharacters = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";
func textField(
textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool
{
let set = NSCharacterSet(charactersInString: notAllowedCharacters);
let inverted = set.invertedSet;
let filtered = string
.componentsSeparatedByCharactersInSet(inverted)
.joinWithSeparator("");
return filtered != string;
}
internal func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{
if let text = string{
if text == "#" || text == "$" || text == "!"{ \\and so on
return false
}
}
return true
}
Swift 2.3
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let characters = ["#", "$", "!", "&","#"]
for character in characters{
if string == character{
print("This characters are not allowed")
return false
}
}
}
So this is probably the most robust way to restrict Spaces. Using this user won't be able to Paste/Type Whitespaces
This is how you can Implement using Swift 3.
Add below mentioned extension snippet to a Swift file;
extension String {
var containsWhitespace: Bool {
for scalar in unicodeScalars {
switch scalar.value {
case 0x20:
return true
default:
continue
}
}
return false
}
}
In your ViewController Swift file drag out your Editing Changed Instance and a Referencing Outlet of UITextField from Storyboard, the one mentioned in picture below:
Use the dragged Instances as mentioned below:
Referencing Outlet as:
#IBOutlet weak var textField: UITextField!
and Editing Changed as:
#IBAction func textChanged(_ sender: UITextField) {
if (textField.text!.containsWhitespace) == true {
print("Restrict/Delete Whitespace")
emailField.deleteBackward()
} else {
print("If Its not Whitespace, Its allowed.")
}
}
This will detect and remove whitespace as soon as user tries to type/paste it.
Swift 4 iOS 11.2.x based on using an extension, tests to see if a string is a valid hex number in this example.
extension String {
var containsValidCharacter: Bool {
guard self != "" else { return true }
let hexSet = CharacterSet(charactersIn: "1234567890ABCDEFabcdef")
let newSet = CharacterSet(charactersIn: self)
return hexSet.isSuperset(of: newSet)
}
}
You use it like with the UITextFieldDelegate.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return (string.containsValidCharacter)
}
Swift : 3 and a different approach:
Add a target function for the text field change in your viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
}
in the target function, simply detect the entered char and replace it with blank. I have tested it and it prevents the user from entering any non desirable characters in the text field.
func textFieldDidChange(textField: UITextField) {
if let textInField = textField.text{
if let lastChar = textInField.characters.last{
//here include more characters which you don't want user to put in the text field
if(lastChar == "*")
{
textField.text = textInField.substring(to: textInField.index(before: textInField.endIndex))
}
}
}
}
Adding on to what #Evdzhan Mustafa said. You want to add a return statement in case the string is empty. Without it you won't be able to delete your text. Modified Code Below:
Swift 3 Version
let notAllowedCharacters = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty{
return true
}
print("String: \(string)")
let set = NSCharacterSet(charactersIn: notAllowedCharacters);
let inverted = set.inverted;
let filtered = string.components(separatedBy: inverted).joined(separator: "")
print("String Filtered: \(filtered)")
return filtered != string;
}

How to limit text field entry to a certain range of numbers in Swift

enter image description hereI have managed to prevent the user from entering more than 2 digits in the 'month' field, using a text delegate function:
Swift code
However, I also want to prevent the user entering a number greater than 12. Can anyone point me in the right direction?
Thanks!
Add Int(myString) < 13 in your return condition with && operator.
in didload
txt_field.delegate=self
txt_field.addTarget(self, action:"submit:", forControlEvents: UIControlEvents.EditingChanged)
then define "submit" method as
#IBAction func submit(sender: AnyObject) {
let a:Int? = txt_field.text.toInt()
if a > 12{
print("number is greater than 12")
}
else{
print("number is less than 12")
}
}
"submit" method is called each time user stops editing the textfield. Hence you can check what user is entering and prevent him from entering value greater than 12.
Hope it helps.
Happy Coding.
textEdit.delegate = self from your view controllar
extension UserProfileViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let numberFiltered = string.components(separatedBy: NSCharacterSet(charactersIn: "0123456789").inverted).joined(separator: "")
guard string == numberFiltered, range.location < 2 else {
return false
}
if let newValue = textField.text?.intValue, let currentValue = string.intValue {
let totalValue = newValue*10 + currentValue
switch totalValue {
case 16..<80:
return true
default:
textField.text = ""
return false
}
}
return true
} }

Uneditable prefix inside a UITextField using Swift

I'm having a problem regarding the creation of a prefix inside a UITextField using the new Swift language. Currently I have created the UITextField using the Interface Builder and I have assigned an IBOutlet to it, named usernameField, then using the textFieldDidBeginEditing function I write a NSMutableAttributedString inside it, named usernamePrefix, containing only the word "C-TAD-" and finally I limited the UITextField max characters number to 13, like so:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var usernameField : UITextField!
private var usernamePrefix = NSMutableAttributedString(string: "C-TAD-")
func textFieldDidBeginEditing(textField: UITextField) {
if textField == usernameField {
if usernameField.text == "" {
usernameField.attributedText = usernamePrefix
}
}
usernameField.addTarget(self, action: "textFieldDidChangeText:", forControlEvents:UIControlEvents.EditingChanged)
}
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let maxUsernameLength = countElements(usernameField.text!) + countElements(string!) - range.length
return maxUsernameLength <= 13
}
override func viewDidLoad() {
super.viewDidLoad()
usernameField.delegate = self
passwordField.delegate = self
}
}
Now, how can I assign new parameters to the usernamePrefix in order to have to give 2 different colors to the text written in the UITextField? I would like to have the prefix in .lightGreyColor() and the rest in .blackColor(). Also how can I make the usernamePrefix un-editable and un-deletable by the user?
Thanks for the help
Simpler option would be to set leftView of the UITextField and customise it how you like it:
let prefix = UILabel()
prefix.text = "C-TAD-"
// set font, color etc.
prefix.sizeToFit()
usernameField.leftView = prefix
usernameField.leftViewMode = .whileEditing // or .always
It is un-editable and un-deletable and you don't need to do any calculations to check the length of the input.
For the first part, you can refactor your delegate method as follow.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
//This makes the new text black.
textField.typingAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
let protectedRange = NSMakeRange(0, 6)
let intersection = NSIntersectionRange(protectedRange, range)
if intersection.length > 0 {
return false
}
if range.location == 12 {
return true
}
if range.location + range.length > 12 {
return false
}
return true
}
This will lock down both the length at 13 and the prefix can not be deleted. Everything typed will be UIColor.blackColor()
Then you can a method like the following in your viewDidLoad, to set the prefix.
func makePrefix() {
let attributedString = NSMutableAttributedString(string: "C-TAD-")
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGrayColor(), range: NSMakeRange(0,6))
textField.attributedText = attributedString
}
I've adopted the solution from Jeremy and make a little bit improvement to make it a bit more swifty, and also handle the case when user pastes multiple characters into the text field.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let protectedRange = NSRange(location: 0, length: usernamePrefix.length)
let intersection = protectedRange.intersection(range)
// prevent deleting prefix
if intersection != nil {
return false
}
// limit max character count
if (textField.text ?? "").count + string.count > 13 {
return false
}
return true
}

UITextView length issue

I've a UITextView with scroll enabled, I can change font and size of the text inside. So, How can I set a text limit? I've already tried to set max row and max number of character but it doesn't working because I need to set a size limit of the text, How can I do this?
You have to implement the UITextViewDelegate:
class ViewController: UIViewController, UITextViewDelegate {
and then set the delegate of the textView in the viewDidLoad-method:
yourTextView.delegate = self
After that you can use the shouldChangeTextInRange method to check the letters:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
let maxtext: Int = 140
//If the text is larger than the maxtext, the return is false
// Swift 2.0
return textView.text.characters.count + (text.characters.count - range.length) <= maxtext
// Swift 1.1
// return countElements(textView.text) + (countElements(text.length) - range.length) <= maxtext
}
The nice part in this solution is, that you can even control cut/copy and paste. So the user can't trick the field to accept more letters by copying.
1) class ViewController: UIViewController, UITextViewDelegate {
2) textView.delegate = self
3)
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
let maxCharacter: Int = 1000
return (textView.text?.utf16.count ?? 0) + text.utf16.count - range.length <= maxCharacter
}
Usually you'd conform to the UITextFieldDelegate protocol, specify your view controller as the delegate for the UITextField (specifying this either in IB or in code) and then implement a shouldChangeCharactersInRange that considers the length of the string:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldString = textField.text ?? ""
let startIndex = oldString.startIndex.advancedBy(range.location)
let endIndex = startIndex.advancedBy(range.length)
let newString = oldString.stringByReplacingCharactersInRange(startIndex ..< endIndex, withString: string)
return newString.characters.count <= kMaxLength
}
If, on the other hand, you want to control the amount of text entered to conform to the size of the control, you could use the aforementioned shouldChangeTextInRange, but use sizeWithAttributes to dictate whether that method returns true or false rather than the number of characters.
This isn't quite right, but it illustrates the basic idea:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldString = textField.text ?? ""
let startIndex = oldString.startIndex.advancedBy(range.location)
let endIndex = startIndex.advancedBy(range.length)
let newString = oldString.stringByReplacingCharactersInRange(startIndex ..< endIndex, withString: string)
let stringSize = newString.sizeWithAttributes([NSFontAttributeName : textField.font ?? UIFont.systemFontSize()])
return stringSize.width < textField.editingRectForBounds(textField.bounds).size.width
}