i am trying to handle the scrolling of UITextField to avoid the keyboard to overlap it. Click here to view current OUTPUT i am getting ]StoryBoard Screenshot. I tried to scroll the view when i click on the last textfield ..ie password field. When i click on the password field, only the email field ie the 2nd one is scrolling up. No scroll occur ion the first and last field. How can i solve this issue?
`
//
// ViewController.swift
import UIKit
class ViewController: UIViewController,UITextFieldDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var name: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func textFieldDidBeginEditing(_ textField: UITextField) {
// Begin
switch textField.tag {
case 0...1:
print("DO NOTHING")
default:
print("Do Scroll")
scrollView.setContentOffset(CGPoint(x:0,y:100) , animated: true)
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
// End
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Return
if textField.tag == 0
{
email.becomeFirstResponder()
}
else if textField.tag == 1
{
password.becomeFirstResponder()
}
else if textField.tag == 2
{
textField.resignFirstResponder()
}
return true
}
}
`
Unless you really want to handle the logic yourself, I recommend you give IQKeyboardManager a try!
It's a simple CocoaPod library that you import and enable in your appDelegate and well... that's all.
It's a codeless library so you really don't have to think about it ever again as it takes over and handles the repositioning of every textfields in the entire application.
You need to move all textfield to scrollview as below.
There are multiple solutions of the keyboard-overlap problem both here on StackOverflow and on YouTube. They all are variations of editing event handling. For lazy and time-restrained folks (like me), I would recommend just to use a fixed-height footer (a View with or without content) on the bottom of a scroll view. I usually place there different kinds of disclaimers and help information. This footer will always "push" your textview above the keyboard.
Related
I am creating a signup screen.
There are four UITextField, which is the ID, password, password check, name,
I am #Iboutlet var signupTextFields: [UITextField]! I connected it. After that, I want to make an ID, password validation.
First, I divided ViewController and UItextFieldDelegate.
class SignUpViewController: UIViewController {
#IBOutlet var signUpTextFields: [UITextField]! {
didSet {
signUpTextFields.forEach { textField in
textField.delegate = textFieldDelegate
textField.returnKeyType = .next
}
}
}
#IBOutlet weak var nextButton: UIButton!
private lazy var textFieldDelegate = TextFieldDelegate(self)
override func viewDidLoad() {
super.viewDidLoad()
signUpTextFields.first?.becomeFirstResponder()
}
#IBAction func nextButtonTapped(_ sender: UIButton) {
}
}
class TextFieldDelegate: NSObject, UITextFieldDelegate {
private weak var signUpViewController: SignUpViewController?
init(_ signUpViewController: SignUpViewController) {
self.signUpViewController = signUpViewController
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.layer.borderWidth = 1
textField.layer.borderColor = UIColor.systemBlue.cgColor
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
textField.layer.borderColor = UIColor.black.cgColor
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
The problem when using the functions provided by Delegate is that the example code is to identify UITextField using if-else, such as
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == idTextField {
//code
} else if textField == passwordTextField {
//code
}
}
I was not this way, but I thought I wanted to abstract the uitextfield a little more to use the Factory method or polymorphism.
Is there a way to identify a UITextField Collection without using if-Else?
I'd recommend - Tag + Enum. (tag field is the norm to identify a view instance from Storyboard / Xib in the cases where you don't have individual IBOutlets)
Set "tag" in the Storyboard - ensure they are unique. (simple index oughta be enough -- 0, 1, 2 .. etc.)
Create an enum inside the view controller.
enum TextField {
case name = 0
case password = 1
}
& so on (explicitly declare the tag values in the enum)
Use Switch instead of if-else for identification.
switch textField.tag {
case .name:
case .password:
}
This should suffice for your case (since you are just going through an example code). But it's good that you are interested in clean code -- I'd recommend creating the TextFields programmatically & using the enum to set the .tag field.
Using the same delegate for all of the text fields was bad design if your intention was to behave differently in the delegate methods. If it’s too late to change that, I would recommend an array of closures corresponding to the array of text fields. That way, calling the right closure is a one-liner based on firstIndex(of:).
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
}
Environment: Xcode Version 8.2 (8C38)/Swift 3.0
A textFiled object in the View is wire up to a method named textFieldReturn in the controller via IBAction. The related codes are presented as follow
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func textFieldReturn(_ sender: Any) {
_ = (sender as AnyObject).resignFirstResponder()
}
}
What I expect:
When I hit the Return key of the virtual keyboard the function textFieldReturn(_:) will be called and the keyboard will be hidden
Issue Observer:
The function is not called after I tapped the return key, the keyboard is still there
Resource:
This code spinet come from the example of the Chapter 16 of the book iOS 10 App Development Essentials by Neil Symth (pp-114)
The only difference between this code and original code is the type of the function argument (Sender). It is AnyObject in the original book while I've got Any by default, therefore I've cast to AnyObject inside the function body
Question:
Its seems to be a decent book, but the sample code doesn't work for me. How can I call the resignFirstResponder() method when I hit the return key
Alternative try out:
Instead of using IBAction, I turn to the idea of delegate, I've set the VeiwController as the delegate of the textField
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tempText: UITextField! //reference the TextField as the variable **tempText** in the controller
override func viewDidLoad() {
super.viewDidLoad()
self.tempText.delegate = self //set up the delegation
}
func textFieldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return(true)
}
}
Problem
The alternative solution still not working.
Thanks for your time and help
Why are you not using the original delegate function of UITextField?
I think the default function will work as you want:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tempText: UITextField! //reference the TextField as the variable **tempText** in the controller
override func viewDidLoad() {
super.viewDidLoad()
self.tempText.delegate = self //set up the delegation
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool{
textField.resignFirstResponder()
return false
}
}
Replace your textFieldReturn method with this and it should work just fine.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Make sure to keep the following in your viewDidLoad()
self.tempText.delegate = self
I am creating a simple calculator app - everything works fine but I'd like to clear the result label when the user enters a new number in a textfield (or just touches inside a textfield).
Right now I am using a touchesBegan function to clear the label (which also dismisses the keyboard:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
resultLabel.text = ""
}
Problem is of course that it doesn't matter where the user touches on the screen (for example accidentally doesn't hit the calc button), the label will always be cleared.
Is there a way to clear the "resultLabel" when the user touches the "numberField" textfield?
Thanks!
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var numberField: UITextField!
override func viewDidLoad(){
super.viewDidLoad()
numberField.delegate = self
}
extension viewcontroller: UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
resultLabel.text = ""
}
}
For clear resultlable first thing you need to give delegate to your text filed
numberField.delegate = self
Setup your viewcontroller to implement UITextFieldDelegate
Add below method to your viewcontroller
class viewcontroller: UIViewController,UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
resultLabel.text = ""
}
}
You can use text field Delegate methods on your textField where the user is entering the numbers.
textField.delegate = self
textField.tag = 0
Add a tag to your textField and check it in didBeginEditing.
func textFieldDidBeginEditing(textField: UITextField) {
if textField.tag == 0 //Or whatever tag you attach{
//Do the required task
}
}
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!