checking validity of decimal input in UITextfield - swift

One of the UITextfield input should accept decimals. But I want to restrict the user to type only one ".", otherwise the floating point conversion gets messed up if it has more than one "." in the number. How to ensure user does not key in more than one "." as a part of decimal input?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if ( textField == areaTextField) {
guard CharacterSet(charactersIn: "0123456789.").isSuperset(of: CharacterSet(charactersIn: string)) else {
return false
}
}
return true
}

Your approach is wrong to start with; you are way overthinking this. The computer already knows whether a string is a valid number, so just ask it. Don't examine any characters. Don't examine any keys. Simply perform the replacement described by the parameters and ask the computer if the result would be a number:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let rep = (textField.text! as NSString).replacingCharacters(in: range, with: string)
return rep.count == 0 || Double(rep) != nil
}

Related

how to remove character when backspace is pressed swift

i want to remove one last character when user presses the backspace
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {
print("backspace pressed")
if let itemToRemove = textField.text?.dropLast(){
let text = textField.text?.replacingOccurrences(of: itemToRemove, with: "")
textField.text = text
return true
}
}
return true
}
this function clears all the elements present in the textfield
You're using this method wrong. The delegate method is not for you to implement the change to the text, it's for you to approve the change (hence returning a bool).
From the documentation (which is always a good first point of call if something isn't working how you expect):
Return Value
true if the specified text range should be replaced;
otherwise, false to keep the old text.
Discussion
The text field calls this method whenever user actions cause its text to change. Use this
method to validate text as it is typed by the user. For example, you
could use this method to prevent the user from entering anything but
numerical values.
EDIT: (as pointed out by Duncan C in the comments, and as should have been in the original answer)
A good starting point is just to return true from this method, as then all the user input will be reflected in the text field. If you need to be more specific about what edits you allow you can introduce that logic later.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Backspace handled
guard !string.isEmpty else {
return true
}
return true
}

Creating own character set causes crash

I'm using this piece of code to limit user input regarding the keyboard.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.init(charactersIn: ".0123456789")
let characterSet = NSCharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet as CharacterSet)
}
but my program crashes when I enter any character.
Update 2:
So 99.9% of this solution works great, unfortunately the period/decimal point does not register. Unsure why this is happening?
This appears to be a bug in Swift, there are multiple issues on this matter on the Swift issue tracker: https://bugs.swift.org/browse/SR-3311, https://bugs.swift.org/browse/SR-3667
Until this has been fixed, you can workaround this problem by using the following extension:
extension CharacterSet {
func isSupersetOf(other: CharacterSet) -> Bool {
return CFCharacterSetIsSupersetOfSet(self as CFCharacterSet, (other as NSCharacterSet).copy() as! CFCharacterSet)
}
}
Keep in mind that you need to change your characterSet variable from type NSCharacterSet to CharacterSet to be able to use that extension in your example.

How to properly solve the EXC_BAD_ACCESS (code=EXC_i386_GPFLT) error in my function

Ok, so total Swift noob here. Going through some beginner tutorials.
In order to achieve some goal,
I need a UITextField to accept only numbers
I have managed to get the UITextField to accept only letters via this code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.TextField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.letters
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
}
However as soon as I turn allowedCharacters into something like:
let allowedCharacters = CharacterSet(charactersIn: "0123456789")
The Simulator still launches successfully (and I get a Build Succeeded message)
However, as soon as I start typing it crashes. Xcode then highlights my return statement and shows the error message:
Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
When I google for it I get the sense that it might have something to do with the fact that I might need to an if-statement somewhere in my function to catch any unexpected values that I guess are being returned as soon as I start typing in the textfield, but I can not see the logic yet as to where and how.
Any thoughts to enlighten me would be great!
PS: I know I could also just configure my textfield to only show a numpad, but I'd still like to know why the current solution is causing problems for I might need other, more advanced, restrictions sometime in the future.
EDIT
Another solution that works (which I discovered after posting the question) is to change my declaration of allowedCharacters as such:
let allowedCharacters = CharacterSet.decimalDigits
However, the accepted answer to this question allows for more finetuning when necessary.
If you want to handle it that way, instead of making a character set out of your string and checking if it's a subset you should use string's method rangeOfCharacter(from: CharacterSet) -> Range<String.Index>?. This method returns a position of a character from given set or nil if there was no character from the set present.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print(string)
let allowedCharacters = CharacterSet(charactersIn: "0123456789")
return string.rangeOfCharacter(from: allowedCharacters) != nil
}
However it is not a good approach to specify allowed characters as here you'll prevent user from removing characters with backspace. You should rather specify disallowed characters then this function will change to:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print(string)
let disallowedCharacters = CharacterSet(charactersIn: "0123456789")
return string.rangeOfCharacter(from: disallowedCharacters) == nil
}

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;
}

User should not be able to delete given symbol in textfield which is a text

func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString string: String) -> Bool
{
return true
}
I have to restrict user to delete this symbol from textfield in swift. User can delete any thing from textfield without deleting this symbol.
As the most probable scenario is that the Euro symbol will always be in the textfield and will be the first character, I would check if the range of characters to change is longer than 0 and starts at 0. (This will work regardless of the symbol in the first position)
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if range.length>0 && range.location == 0 {
return false
}
return true
}
If however, the euro symbol is not always in the text field, you could check whether the user is deleting this symbol or not by getting a reference to the string the user is changing and checking whether this contains a euro symbol:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if range.length>0 && range.location == 0 {
let changedText = NSString(string: textField.text!).substringWithRange(range)
if changedText.containsString("€") {
return false
}
}
return true
}