SWIFT: Extension for UITextField that doesn't accept whitespace as input - ios

I know shouldChangeTextInRange method on UITextFieldDelegate can filter input on UITextField, it's ok if i only need to filter one UITextField. Now my problem is i have lot of UITextField that need to filter whitespace. And i don't want to implement shouldChangeTextInRange on every UIViewController that have UITextField in it. Is there anyway to make extension of the UITextField or other?

Actually this is quite simple, just subclass UITextField, add delegate to it, and implement the shouldChangeTextInRange there.
class CustomTextField: UITextField, UITextFieldDelegate {
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if (string.rangeOfCharacterFromSet(.whitespaceCharacterSet()) != nil) {
return false
} else {
return true
}
}
}

Swift allows to extend the protocols as well. You can make an extension UITextFieldDelegate and implement your custom code in that method. But there are some limitations, check this answer, it will help you swift 2.0 - UITextFieldDelegate protocol extension not working

Related

UITextView delegate shouldChangeTextInRange isn't called when keyboard is selected to Chinese (Simplified) - Handwriting in Swift 4.2

Here is my Sample Code:
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView_editingView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView_editingView.text = ""
textView_editingView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print("Not Called")
return true
}
func textViewDidChange(_ textView: UITextView) {
print("Called")
}
}
When I change keyboard type to "Chinese (Simplified) - Handwriting", "shouldChangeTextIn range" (UITextViewDelegate) isn't called while entering a text.
There are certain check in this delegate which needs to be called like mention, hashtags, urls. Since this delegate is not called, so my logic for these validations fails when keyboard is changed. Otherwise, everything works fine for a normal English keyboard.
Please Note: I am using the default keyboard which are available in XCode Simulator's Keyboard setting, and this is also reproducible on iPhone 7 Plus.
Honestly, I have the same problem, and it seems like HandWriting whatever in any language has the same problem. My workaround is to observe "func textFieldDidEndEditing(UITextField)", instead, and to do whatever I need to in "func textFieldShouldEndEditing(UITextField) -> Bool", presumably.

textField.shouldChangeCharactersIn not called with Braille Screen Input

I’m working on an app that uses accessibility. When the user clicks in a UITextField and writes a letter, the textField.shouldChangeCharactersIn method gets called. This happens without problems when voiceOver is switched on or off. But if the users turns on the Braille Screen Input, the method textField.shouldChangeCharactersIn doesn’t get called.
Instead an error gets logged out:
[User Defaults] Couldn't write value for key
VoiceOverHandwritingWasNativeAutocorrectEnabled in
CFPrefsPlistSource<0x1d4107860> (Domain: com.apple.Accessibility,
User: kCFPreferencesCurrentUser, ByHost: No, Container: (null),
Contents Need Refresh: Yes): setting preferences outside an
application's container requires user-preference-write or
file-write-data sandbox access, switching to read-only
Can somebody explain what this means?
It can be reproduced easily with:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let text = UITextField(frame: CGRect(x: 20.0, y: 150.0, width: 200.0, height: 30.0))
text.delegate = self;
text.backgroundColor = .gray
self.view.addSubview(text)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print("replacementString : \(string)")
return true
}
}
I think it's a bug on apple Api. This solution might not fit your use case but you may try something like this. Here is the alternate temporary solution to keep track of text using custom class inheriting Textfield.
class AccessibilityTextField: UITextField {
override func accessibilityElementDidLoseFocus() {
_ = delegate?.textFieldDidEndEditing?(self)
}
}
Then in your textfield delegate implementation method, validate the text to enable or disable some other button.
extension MyView: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
//Do you check here
}
}
Remember this is not live tracking of Text(like char in range) as didLoseFocus is called only once.
Make you textField Object global because it gets deallocated right after the execution of the method viewDidLoad
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var textField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
textField = UITextField(frame: CGRect(x: 20.0, y: 150.0, width: 200.0, height: 30.0))
textField?.delegate = self;
textField?.backgroundColor = .gray
self.view.addSubview(textField!)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print("replacementString : \(string)")
return true
}
}

How to show entered data from UITextField to Label permanently?

I'm trying to make a function where when user enter a text in UITextField in the same moment label shows entered text.
How could I make it?
Like:
textField.text = "10"
Label.text = "\(textField.text) smthg" //. (10 smthg)
textField.text = "10.56"
Label.text = "\(textField.text) smthg" //. (10.56 smthg)
Implement UITextFieldDelegate and set it to textField.delegate property. From UITextFieldDelegate implement shouldChangeCharactersIn callback method that gets called everytime the user tries to change input in the textfield:
class MyViewController: UIViewController {
...
func viewDidLoad() {
super.viewDidLoad()
// set the textField's delegate to self
textField.delegate = self
}
}
extension MyViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// to be always updated, you cannot use textField.text directly, because after this method gets called, the textField.text will be changed
let newStringInTextField = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
yourLabel.text = "\(newStringInTextField) smthg"
return true
}
}
Using arguments of the function you can get a string that will appear in textField and you can set it as text in yourLabel.
You need to implement textfield's delegate method shouldChangeCharactersIn, it will be called when user start typing, delete a character from textfield, click on clear button appear at right side of textfield when there is text in textfield.
You can use Editing Changed Action for that textField
#IBAction func changeText(_ sender: UITextField) {
Label.text = "\(textField.text) smthg"
}

How to customize numeric input for a UITextField?

I have a UITextField (that represents a tip value) in my Storyboard that starts out as $0.00. If the user types an 8, I want the textField to read $0.08. If the user then types a 3, I want the textField to read $0.83. If the user then types 5, I want the textField to read $8.35. How would I go about changing the input to a UITextField in this manner?
You can do this with the following four steps:
Make your viewController a UITextFieldDelegate by adding that to the class definition.
Add an IBOutlet to your textField by Control-dragging from the UITextField in your Storyboard to your code. Call it myTextField.
In viewDidLoad(), set your viewController as the textField’s delegate.
Implement textField:shouldChangeCharactersInRange:replacementString:.
Take the incoming character and add it to the tip, and then use the String(format:) constructor to format your string.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var myTextField: UITextField!
// Tip value in cents
var tip: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
myTextField.text = "$0.00"
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let digit = Int(string) {
tip = tip * 10 + digit
textField.text = String(format:"$%d.%02d", tip/100, tip%100)
}
return false
}
}

UITextView shouldChangeCharactersInRange in Swift

Trying to disable/enable the next button (which begins disabled on the storyboard) based on how many characters are in the textView field. It isn't working, which makes me think that only the textField has this feature and not the textView (they were really lazy with developing the textView field).
I have an outlet connected for the textView and next button:
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var nextButton: UIBarButtonItem!
func textView(textView: UITextView, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldText: NSString = textView.text
let newText: NSString = oldText.stringByReplacingCharactersInRange(range, withString: string)
if newText.length > 0 {
nextButton.enabled = true
} else {
nextButton.enabled = false
}
return true
}
How do I get this to work, because it just completely ignores it even if it compiles without errors.
The used delegates method was wrong.
shouldChangeCharactersInRange is for UITextField not UITextView
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
Method for UITextView
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
Requires UITextViewDelegate reference in class line:
class MyViewController: UIViewController, UITextViewDelegate {
}
AND it also requires a referencing outlet from the view controller to the textView delegate. Select the view controller, click on outlets, drag from referencing outlet to the textView, click delegate.
Probably you have not set the viewController as the delegate of the textField.
There are 2 ways to do that ...
1) Override viewDidLoad and mention textField.delegate = self
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
2) open the corresponding storyBoard, control drag the textField to the viewController and select delegate from the pop-up.
You have to define the function name with the correct name, otherwise it will not be recognised and not triggered
func textView(textView: UITextView, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
You'll have to include the UITextViewDelegate reference in the class definition, so that the deligate it assigned to the class:
class ViewController: UIViewController, UITextViewDelegate {
And you have to link the textView delegate (eg. in the viewDidLoad):
textView.delegate = self

Resources