Swift checking textfield input live - swift

I've been struggling to find how to check the input of a textfield as a user is typing.
if a user types a word, it should change a label and an image according to some defined rules.
my code is working, but I'm always a step behind. (as it reads the content always before the next character is entered.
If it just to check the length I could use countElements(textfield) + 1, but I want it to also show that a user cannot use certain characters as they are typing, therefore that would not work for checking undesired characters.
I am assuming the function I am using is not the right one "shouldChangeCharacters". So I am a bit lost as to what to use. Is there a way to read a println or NSLog command to return to an outlet?
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let passwordcheck = UserPasswordTextField.text
if UserPasswordTextField.isFirstResponder() {
if isValidPassword(passwordcheck) {
PWimg.image = UIImage(named: "passwordapprovedicon")
} else if passwordcheck.isEmpty {
PWimg.image = UIImage(named: "passwordiconwrong")
} else {
PWimg.image = UIImage(named: "passwordiconwrong")
}
}
func isValidPassword(testStr2:String) -> Bool {
println("validate password: \(testStr2)")
let passwordRegEx = "[A-Z0-9a-z._%+-:/><#]{6,30}"
if let passwordTest = NSPredicate(format: "SELF MATCHES %#", passwordRegEx) {
return passwordTest.evaluateWithObject(testStr2)
}
return false

Listen for UIControlEventEditingChanged events from the text field. Register for them either with
the Interface Builder: drag from the text field to the file, and select the action type "Editing Changed"
the following code:
override func viewDidLoad() {
super.viewDidLoad()
// ...
textField.addTarget(self, action:"edited", forControlEvents:UIControlEvents.EditingChanged)
}
func edited() {
println("Edited \(textField.text)")
}

Updated for swift 3:
override func viewDidLoad() {
super.viewDidLoad()
//...
textField.addTarget(self, action: #selector(textFieldDidChange), for:.editingChanged)
}
func textFieldDidChange(){
print(textField.text)
}

Updated for swift 4.2: just add #objc to func textFieldDidChange()
override func viewDidLoad()
{
super.viewDidLoad()
textField.addTarget(self,
action : #selector(textFieldDidChange),
for : .editingChanged)
}
#objc func textFieldDidChange()
{ print(textField.text ?? "Doh!") }

Related

Custom keyboard and shouldChangeCharactersIn

I have custom keyboard which I added as textfield.inputView. But my shouldChangeCharactersIn at UITextFieldDelegate doesn’t work? Any idea?
You have to implement the call to the delegate method yourself prior to inserting the text into the control.
For example, let’s assume you have some function for handling tap of a button, which calls UIKeyInput method insertText. Just check that the delegate implements the method and that it did not return false:
#objc func didTapButton(_ sender: ...) {
guard let range = target?.selectedRange else { return } // assumes `target` was defined to conform to `UITextInput`, using extension shared below
let string = ...
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string) // assumes `target` was defined to conform to `UIKeyInput`
}
Where:
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
Clearly, the details are dependent upon your custom keyboard implementation (the above is based upon https://stackoverflow.com/a/57275689/1271826), but hopefully it illustrates the basic idea.

Eureka in swift List Selection Selected Value Automatically Deselect when navigating away

I am implementing Eureka list selection. On the language selections, there are 7 values to choose from. Value 1 to 6 is good. But when the last one selected and user navigates away from the VC, it's deselected automatically. I wonder if there is something wrong with my code. I have tried to "hardcode" the last selection on viewWillAppear just so the app goes smoothly and yes it is. But it's a little weird because none of the selection selected. I am a beginner to programming and greatly appreciated any help. Thank you
import Eureka
class ListSectionsController: FormViewController {
let defaults = UserDefaults.standard
var languageSelected = "English (default)"
override func viewDidAppear(_ animated: Bool) {
defaults.set(languageSelected, forKey: "LanguageSelection")
}
override func viewWillAppear(_ animated: Bool) {
if let language = defaults.string(forKey: "LanguageSelection") {
if language == "Korean 한국어" {
languageSelected = language
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let languageSelections = ["English (default)", "Spanish", "Japanese 日本語", "Chinese Mandarin 普通话", "Indonesian", "Dutch - het Nederlands", "Korean 한국어"]
form +++ SelectableSection<ImageCheckRow<String>>() { section in
section.header = HeaderFooterView(title: "Language Selection")
}
for option in languageSelections {
if let language = self.defaults.string(forKey: "LanguageSelection") {
self.form.setValues([language : language])
} else {
self.form.setValues([languageSelected : languageSelected])
}
form.last! <<< ImageCheckRow<String>(option){ lrow in
lrow.title = option
lrow.selectableValue = option
lrow.value = nil
}
}
}
override func valueHasBeenChanged(for row: BaseRow, oldValue: Any?, newValue: Any?) {
if row.section === form[0] {
if let selected = (row.section as! SelectableSection<ImageCheckRow<String>>).selectedRow()?.baseValue {
print("Selected: \(selected)")
languageSelected = selected as! String
defaults.set(languageSelected, forKey: "LanguageSelection")
}
} else if row.section === form[1] {
print("Mutiple Selection:\((row.section as! SelectableSection<ImageCheckRow<String>>).selectedRows().map({$0.baseValue}))")
}
}
}

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
}