UITextView becomeFirstResponder() adds new line - ios

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

Related

Move to next UITextField after entering exactly one numeric digit

I want to make a login screen for customer ID which accepts numeric input with a number pad and moves to next UITextField after one number is typed by the user.
I have five UITextField, The first UITextField should become the first responder with a number pad and should progress through the fields without pressing the return key. The four UITextField are,
#IBOutlet weak var customerIdOne: UITextField!
#IBOutlet weak var customerIdTwo: UITextField!
#IBOutlet weak var customerIDThree: UITextField!
#IBOutlet weak var customerIdFive: UITextField!
#IBOutlet weak var customerIdFour: UITextField!
and on pressing the login button, All the values in the UITextField should be concatenated.
#IBAction func loginButton(_ sender: Any) {
custID = "\(customerIdOne.text!)\(customerIdTwo.text!)\(customerIDThree.text!)\(customerIdFour.text!)\(customerIdFive.text!)"
print(custID)
}
I am beginner and i want to know if there are efficient ways to implement this.
currently, I used tags with textFieldShouldReturn Delegate method
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.tag == 1
{
customerIdOne.resignFirstResponder()
customerIdTwo.becomeFirstResponder()
}
if textField.tag == 2
{
customerIdTwo.resignFirstResponder()
customerIDThree.becomeFirstResponder()
}
if textField.tag == 3
{
customerIDThree.resignFirstResponder()
customerIdFour.becomeFirstResponder()
}
if textField.tag == 4
{
customerIdFour.resignFirstResponder()
customerIdFive.becomeFirstResponder()
}
if textField.tag == 5
{
customerIdFive.resignFirstResponder()
}
return false
}
Step-1
create the IBOutletCollections and set the tag for each textfield for identify which textfield user tapped.
#IBOutlet var customerIdButtons: [UITextField]!
Step-2
create the common extenson for textfield
extension yourViewController : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)
if newString.count == 1 {
textFieldShouldReturnSingle(textField, newString : newString)
return false
}
return true
}
func textFieldShouldReturnSingle(_ textField: UITextField, newString : String)
{
let nextTag: Int = textField.tag + 1
textField.text = newString
let nextResponder: UIResponder? = textField.superview?.viewWithTag(nextTag)
if let nextR = nextResponder
{
// Found next responder, so set it.
nextR.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
// call your method
}
}
}
finally get the all customer ID , then use
#IBAction func loginButton(_ sender: Any) {
var texts: [String] = []
customerIdButtons.forEach { texts.append($0.text!)}
custID = texts.reduce("", +)
print(custID)
}
You can use EditingChanged event to see when one text is entered.
Then you can use viewWithTag method to find the next textfield you want to make FirstResponder. Here is full code you'll need to write for your purpose.
override func viewDidLoad() {
super.viewDidLoad()
self.view.viewWithTag(1)?.becomeFirstResponder()
}
#IBAction func editingChanged(_ sender: UITextField) {
if let nextTextField = self.view.viewWithTag(sender.tag + 1) {
nextTextField.becomeFirstResponder()
}
}
Here is the result:
And after that in button action you can get the texts like below:
#IBOutlet var customerIDTextFields: [UITextField]! //IBOutletCollections of all textfields
#IBAction func loginButtonTapped(_ sender: UIButton) {
var customerIDString = ""
self.customerIDTextFields.forEach { (singleCustomerIDTextField) in
customerIDString.append(singleCustomerIDTextField.text!)
}
print(customerIDString)
}

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 Prompt a New Page After Inserting Name?

I created a simple page of my app today. And, now I want to expand it.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var myTextField: UITextField!
#IBOutlet weak var display: UILabel!
#IBAction func myButtonPressed(_ sender: Any) {
display.text = "Hi \(myTextField.text!)! What can I do for you
today?"
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
I want the app to prompt a new page after the user entered their name.
You can conform to the UITextViewDelegate and present a viewController in func textViewDidEndEditing(_ textView: UITextView) like:
extension ViewController: UITextFieldDelegate {
func textViewDidEndEditing(_ textView: UITextView) {
self.present(/*the targeted view controller*/)
}
}
You can add target like
// In viewDidLoad
myTextField.addTarget(self, action: #selector(onNameChange(sender:)), for: .editingChanged)
// on value change
#objc func onNameChange(sender:UITextField) {
// Do something
}
So far I got your query as:
You want to go to new ViewController when the user is done with filling his name in the textfield.
If I got you right then choose the "GO" (or any thing you wish from options) as the Return Key value inside 'Text Input Traits Section' of Attribute Inspector.
And now add this code in your view controller class with implementing UITextFieldDelegate:
func textFieldShouldReturn(_ textField: UITextField) -> Bool // called when 'return' key pressed. return false to ignore.
{
print(textField.tag)
if (textField.text?.isEmpty)! {
//show alert that text field is empty
return false
}
/* as per your case we have only one textfield,
So there no need of switch case and you can
directly present your next vc from here without having any button on UI */
return false
}

iOS: How to get the current visible keyboard type?

How do I find out if the keyboard is of type numeric, Twitter, email, etc...?
edit: Is there a way to detect keyboard type without using an outlet?
Consider that you have tow textFields in the ViewController, You will need to implement textFieldShouldBeginEditing method from UITextFieldDelegate protocol, as follows:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.keyboardType == .emailAddress {
// this is the tfEmail!
}
if textField.isSecureTextEntry {
// this is tfPassword!
}
}
}
Make sure their delegates are connected to the ViewController, programmatically:
tfEmail.delegate = self
tfPassword.delegate = self
or from the Interface Builder.
Note that you can recognize the keyboard type for the current textField by checking its keyboardType property, which is an instance of UIKeyboardType enum:
The type of keyboard to display for a given text-based view. Used with
the keyboardType property.
What about UITextView?
The same exact functionality should be applied when working with UITextViews, but you need to implement textViewDidBeginEditing(_:) method from UITextViewDelegate protocol instead of implementing textFieldShouldBeginEditing. Again, make sure the delegate of the textView is connected to the ViewController.
Also,
If your main purpose of checking the keyboard type is just for recognizing what is the current responded textField/textView, I suggest to do a direct check:
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
#IBOutlet weak var textViewDescription: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
tfEmail.delegate = self
tfPassword.delegate = self
textViewDescription.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === tfEmail {
// this is the tfEmail!
}
if textField === tfPassword {
// this is tfPassword!
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
if textView === textViewDescription {
// this is description textview
}
}
}
For more information about === operator you might want to check this question/answers.
Hope this helped.
In addition to Ahmad F 's great answer, this is my approach of getting the current keyboard type, at any time:
Step 1: Delegate UITextField
class File: UIViewController, UITextFieldDelegate{//...}
Update viewDidLoad() to this:
#IBOutlet weak var normalTextField: UITextField!
#IBOutlet weak var numberTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberTextField.keyboardType = .numberPad
normalTextField.keyboardType = .default
emailTextField.keyboardType = .emailAddress
numberTextField.delegate = self
normalTextField.delegate = self
emailTextField.delegate = self
}
Step 2: Working with UITextField's methods:
Add a variable called keyboardType, as below:
var keyboardType: UIKeyboardType? = nil
Then, change it whenever a new textField begins editing:
func textFieldDidBeginEditing(_ textField: UITextField) {
keyboardType = textField.keyboardType
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
keyboardType = nil
return true
}
Step 3: Create and call a function like below:
func getCurrentKeyboard() -> String{
if keyboardType == nil{
return "no current keyboard"
}
else if keyboardType == .numberPad{
return "number"
}
else if keyboardType == .emailAddress{
return "email"
}
else{
return "default"
}
}
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
print(self.getCurrentKeyboard())
}
And this outputs: email / number / no current keyboard / default, depending on the case.
If you want to check which type of keyboard it is with if-else statements, you can change your displayCurrentKeyboard() method to this:
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
let keyboardString = self.getCurrentKeyboard()
if keyboardString == "number"{
//...
}
else if keyboardString == "email"{
//...
}
else{
//...
}
}
And that's it! You can call this wherever you want in your code with this usage:
let keyboardString = self.getCurrentKeyboard()
NOTE: This method also handles the case of no keyboard visible on the screen, returning no current keyboard, in this case.
Let me know if this helps!

Function not running when return pressed

I have a textfield, when something is typed in the textfield and "return" on the keyboard is pressed, the keyboard should hide. But it doesn't..
Here is the code I am using:
import UIKit
class EditTableViewController: UITableViewController, UITextFieldDelegate {
var product: Product?
#IBOutlet weak var productImageView: UIImageView!
#IBOutlet weak var ProductDescriptionTextView: UITextView!
#IBOutlet weak var productTitleLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
println("loaded")
productImageView.image = product?.image
productTitleLabel.text = product?.title
ProductDescriptionTextView.text = product?.description
}
override func viewWillDisappear(animated: Bool) {
product?.title = productTitleLabel.text
product?.description = ProductDescriptionTextView.text
product?.image = productImageView.image!
}
func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
{
println("return")
return true
}
}
In the console I get "loaded", but when I press return in the textfield, I don't get "return"
how come?
You forgot to set the UITextField's delegate to your view controller (self)
productTitleLabel.delegate = self - also note that you should name your variables properly to avoid confusion (productTitleTextField instead of a 'Label' suffix)
Or, instead of doing it programmatically, you can do it in storyboard by Ctrl-dragging from your textView to the view controller in storyboard, and select delegate on the popup.
Then, let your view controller conform to UITextFieldDelegate protocol:
class EditTableViewController: UITableViewController, UITextFieldDelegate {
....
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == productTitleLabel {
textField.resignFirstResponder()
}
return true
}
}
In your viewDidLoad method you have to add:
productTitleLabel.delegate = self
And update your textFieldShouldReturn like this:
func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
{
productTitleLabel.resignFirstResponder()
return true
}
And It will hide your keyboard when return key pressed.

Resources