UITextView shouldChangeCharactersInRange in Swift - ios

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

Related

Why my label left 1 character when updating UILabel from UITextField?

so I am making UILabel live update from UiTextfield (user input). I am using the code from this thread Swift3: Live UiLabel update on user input
but somehow, my UILabel always left one character when I fully erase the text in my UITextField. like the .gif in here http://g.recordit.co/SPQWnYtHJg.gif
and it seems one character is always missing like the picture below
here is the code I use
import UIKit
class CreateEventVC: UIViewController {
#IBOutlet weak var eventNameTextField: UITextField!
#IBOutlet weak var eventNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//initial value
eventNameLabel.text = " "
// delegate declaration
eventNameTextField.delegate = self
}
}
extension CreateEventVC : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
eventNameLabel.text = eventNameTextField.text
return true
}
}
I initially suspect because I add this line in viewDidload
eventNameLabel.text = " "
but if i delete this one, the problem is still there
what should I do ?
textField:shouldChangeCharactersIn:range:replacementString is called before the change is applied to the text field, this allows your app to veto the request and filter out unwanted content.
The problem is, you're relying on the text field's text. Instead, you need build the resulting value from the information passed to the delegate and apply that
Maybe something more like...
extension CreateEventVC: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = textField.text ?? ""
eventNameLabel.text = (text as NSString).replacingCharacters(in: range, with: string)
return true;
}
}
class CreateEventVC: UIViewController {
#IBOutlet weak var eventNameTextField: UITextField!
#IBOutlet weak var eventNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//initial value
eventNameLabel.text = " "
eventNameTextField.addTarget(self, action: #selector(onTextFieldTextDidChange), for: .editingChanged)
}
#objc func onTextFieldTextDidChange() {
eventNameLabel.text = eventNameTextField.text
}
}
Explanations:
We add target to the eventNameTextField which will call the onTextFieldTextDidChange func each time the textField text is changed.

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

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

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

UITextView becomeFirstResponder() adds new line

I'm quite new to iOS/Swift and I have a strange behaviour when setting a UITextView as a first responder (when touching next from the previous UITextField). It automatically inserts a new line in the UITextView. The code:
class MyViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var descriptionTextView: UITextView!
override func viewDidLoad() {
nameTextField.delegate = self
descriptionTextView.delegate = self
}
func textFieldShouldReturn(nameTextField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
descriptionTextView.becomeFirstResponder()
return true
}
func textView(descriptionTextView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
descriptionTextView.resignFirstResponder()
return false
}
return true
}
}
A new line is always added and i press next, even if there's already text or a new line.
On the first next here is what happens (the cursor is automatically on the 2nd line).
If I go back to the text field and press next again, the cursor is on the 3rd line).
Return false in textFieldShouldReturn
func textFieldShouldReturn(nameTextField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
descriptionTextView.becomeFirstResponder()
return false
}
More details here

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

Resources