Best way to dismiss keyboard when tapping outside of UITextField - IOS - ios

I've found a few threads here about this, and some videos online about it as well, but every solution seems to have problems reported by others. The simplest solution I've found is the one below.
import UIKit
class SignupController: UIViewController, UITextFieldDelegate {
// Outlets
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var nameTF: CustomTextField!
#IBOutlet weak var emailTF: CustomTextField!
#IBOutlet weak var passwordTF: CustomTextField!
#IBOutlet weak var confirmPassTF: CustomTextField!
// Actions
#IBAction func signupButton(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
logoImage.image = UIImage(named: "logo2")
nameTF.delegate = self
emailTF.delegate = self
passwordTF.delegate = self
confirmPassTF.delegate = self
}
// Moves to next text field each time return key is pressed
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == nameTF {
textField.resignFirstResponder()
emailTF.becomeFirstResponder()
} else if textField == emailTF {
textField.resignFirstResponder()
passwordTF.becomeFirstResponder()
} else if textField == passwordTF {
textField.resignFirstResponder()
confirmPassTF.becomeFirstResponder()
}else if textField == confirmPassTF {
textField.resignFirstResponder()
}
return true
}
// Dismisses keyboard when tapped
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
It works, is very simple, but my project and coding experience are in their infancy, so I'm not sure if this is the best method simply because it's short, or if there's something I'm missing due to lack of experience/knowledge?
Anybody know of a better solution, or is this one just fine?

just do this:
class viewController: UIViewController, UITextFieldDelegate {
// Outlets
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var nameTF: CustomTextField!
#IBOutlet weak var emailTF: CustomTextField!
#IBOutlet weak var passwordTF: CustomTextField!
#IBOutlet weak var confirmPassTF: CustomTextField!
// Actions
#IBAction func signupButton(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
logoImage.image = UIImage(named: "logo2")
nameTF.delegate = self
emailTF.delegate = self
passwordTF.delegate = self
confirmPassTF.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dissMissKeyboard))
view.addGestureRecognizer(tap)
}
func dissMissKeyboard() {
view.endEditing(true)
}

I prefer to use UITextField delegate method:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
view.endEditing(true)
return true
}
or setup inputAccessoryView which have 'done' or 'exit' button.

Then you need to implement the gesture recognition for this . Or you can do like this :
override func viewDidLoad() {
super.viewDidLoad()
//Looks for single or multiple taps.
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dissMissKeyboard))
//Uncomment the line below if you want the tap not not interfere and cancel other interactions.
//tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
//Calls this function when the tap is recognized.
func dissMissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}

Related

How do you exclude a specific UIButton from an extension in Swift?

I am a beginner so bear with me.
I created a basic timer app and rounded the corners of two buttons. The button was still clickable outside of the circle, I needed to fix that so I found a solution online that said to insert the code into the UIButton's subclass or extension. I found solutions for both ways and chose extension option because the code was easier to read and I could understand it better.
The issue now is that I have a third UIButton that is being affected by the extension and I would like to exclude it. I'm not sure if this is even practical (or possible) so please correct me if there is a better way to approach this. The button I need to exclude from the extension is resetButton.
import UIKit
extension UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
//exclude resetButton???
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var stopButton: UIButton!
#IBOutlet weak var resetButton: UIButton!
var timeRemaining: Int = 10
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//// Moved to an extension in order to
//// remove the clickable areas outside
//// of the circle
//startButton.layer.cornerRadius = 50.0
//stopButton.layer.cornerRadius = 50.0
}
#IBAction func start(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: Any) {
timer?.invalidate()
}
#IBAction func reset(_ sender: Any) {
timer?.invalidate()
timeRemaining = 10
label.text = "\(timeRemaining)"
}
#objc func step() {
if timeRemaining > 0 {
timeRemaining -= 1
} else {
timer?.invalidate()
}
label.text = "\(timeRemaining)"
}
}
For your case, extension is not the right option as the methods invoked from extension will apply to the type itself (all UIButton objects in this case).
One option for you is to make a subclass of UIButton instead of extension. Something like this:
class RoundedButton: UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
And hence you can select the buttons that should inherit the custom layout from the class above:
#IBOutlet weak var startButton: RoundedButton!
#IBOutlet weak var stopButton: RoundedButton!
#IBOutlet weak var resetButton: UIButton! // Will not get the style applied for startButton and stopButton

How to remove popView if we tap anywhere in the background except tapping in popView in swift

I have created one popView with textfield and button in ViewController. if i click button then popView is appearing, and i am able to enter text in textfield and submit is working, and if i tap anywhere in view also i am able to remove popView, but here i want if i tap on anywhere in popView i don't want to dismiss popView, Please help me in the code.
here is my code:
import UIKit
class PopUPViewController: UIViewController {
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
popView.isHidden = true
// Do any additional setup after loading the view.
}
#IBAction func butnAct(_ sender: Any) {
view?.backgroundColor = UIColor(white: 1, alpha: 0.9)
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView))
view.addGestureRecognizer(tap)
}
#objc func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
}
In my code if i tap anywhere in the view popView is removing even if i tap on popView also its removing, i don't need that, if i tap on popView then popView need not to be remove.
Please help me in the code
You can override the touchesBegan method which is triggered when a new touch is detected in a view or window. By using this method you can check a specific view is touched or not.
Try like this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view != self.popView {
dismissView()
}
}
func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
It's not the way I would have architected this, but to get around the problem you face you need to adapt your dismissView method so that it only dismisses the view if the tap is outside the popView.
To do this modify your selector to include the sender (the UITapGestureRecogniser )as a parameter:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView(_:)))
and then in your function accept that parameter and test whether the tap is inside your view, and if so don't dismiss the view:
#objc func dismissView(_ sender: UITapGestureRegognizer) {
let tapPoint = sender.location(in: self.popView)
if self.popView.point(inside: tapPoint, with: nil)) == false {
self.popView.isHidden = true
view?.backgroundColor = .white
}
}
Your Popup view is inside the parent view of viewcontroller that's why on tap of popview also your popview is getting hidden.
So to avoid just add a view in background and name it bgView or anything what you want and replace it with view. And it will work fine .
Code:
#IBOutlet weak var bgView: UIView!//Add this new outlet
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
popView.isHidden = true
}
#IBAction func butnAct(_ sender: Any) {
bgView.backgroundColor = UIColor(white: 1, alpha: 0.9)//change view to bgView[![enter image description here][1]][1]
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissView))
bgView.addGestureRecognizer(tap)//change view to bgView
}
#objc func dismissView() {
self.popView.isHidden = true
bgView.backgroundColor = .white//change view to bgView
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}

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!

How can I change UIStepper value when changed from UITextField?

I'm very new to all of this and I found some code that got me understanding some of this syntax. I'm trying to create a textfield that lets me type in a value that updates the stepper's value. The stepper currently works (updates the uitextfield) but when I change the value in the textfield it doesn't update the stepper's value, so when I click on the stepper, it reverts back to whatever value it was before I typed in a value... Can anyone tell me why the two functions STracksValueDidChange and CTrackValueDidChange have errors?
Here's my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var STracks: UITextField!
#IBOutlet weak var STracksStepper: UIStepper!
#IBOutlet weak var CTracks: UITextField!
#IBOutlet weak var CTrackStepper: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
STracksStepper.autorepeat = true
STracksStepper.maximumValue = 100.0
STracksStepper.minimumValue = 2.0
STracksStepper.stepValue = 2.0
print(STracksStepper.value)
STracks.text = "\(Int(STracksStepper.value))"
STracksStepper.addTarget(self, action: "SstepperValueDidChange:", forControlEvents: .ValueChanged)
STracks.addTarget(self, action: "STextValueDidChange:", forControlEvents: .ValueChanged)
CTrackStepper.autorepeat = true
CTrackStepper.maximumValue = 100.0
CTrackStepper.minimumValue = 2.0
CTrackStepper.stepValue = 2.0
print(CTrackStepper.value)
CTracks.text = "\(Int(CTrackStepper.value))"
CTrackStepper.addTarget(self, action: "CstepperValueDidChange:", forControlEvents: .ValueChanged)
CTracks.addTarget(self, action: "CTextValueDidChange:", forControlEvents: .ValueChanged)
}
//Steppers will update UITextFields
func SstepperValueDidChange(stepper: UIStepper) {
let stepperMapping: [UIStepper: UITextField] = [STracksStepper: STracks]
stepperMapping[stepper]!.text = "\(Int(stepper.value))"
}
func STracksValueDidChange(SText: UITextField) {
let STextMapping: [UITextField: UIStepper] = [STracks: STracksStepper]
STextMapping[SText]!.value = "(SText.text)"
}
func CstepperValueDidChange(stepper: UIStepper) {
let stepperMapping: [UIStepper: UITextField] = [CTrackStepper: CTracks]
stepperMapping[stepper]!.text = "\(Int(stepper.value))"
}
func CTrackValueDidChange(CText: UITextField) {
let CTextMapping: [UITextField: UIStepper] = [CTracks: CTrackStepper]
CTextMapping[CText]!.value = "(CText.text)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try something like this.
CTrackStepper.value = Double(Textfield.text)
I am not so sure what the mapping is in your code.
But i don't think you need it for changing the value.
Update, made a project my self:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textfield: UITextField!
#IBOutlet weak var stepper: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func stepperValueChanged(sender: UIStepper) {
textfield.text = String(sender.value)
}
#IBAction func valueChanged(sender: UITextField) {
if Double(sender.text!) != nil {
stepper.value = Double(sender.text!)!
}
}
}
For steppervaluechanged and valuechanged just drag from uistepper and textfield and choose action and change the Anyobject to Uistepper of Uitextfield.
Good luck :)

Swift: Value Changing Control Events Not Calling?

So I have added targets to my IBActions I have created that occur when the value of a text field changes. When these actions occur, the system should check if the two text fields are both integers. I have set two variables set to false, and they are set to true when both of them are an int. In the IBActions, I have if statements that tell a button to be enabled if both of the variables contain integers. When I run the simulator, this button doesn't enable when both of the text fields contain an integer.
I am new to swift, so if possible, please write all of the code out and where it should be in my code. Here is what I have so far:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var calculatorButton: UIButton!
#IBOutlet weak var inspirationLabel: UILabel!
#IBOutlet weak var beginningLabel: UILabel!
#IBOutlet weak var calculatorContainer: UIView!
#IBOutlet weak var answer1Label: UILabel!
#IBOutlet weak var doneButton: UIButton!
#IBOutlet weak var yourWeightTextField: UITextField!
#IBOutlet weak var calorieNumberTextField: UITextField!
#IBOutlet weak var menuExampleButton: UIButton!
#IBOutlet weak var aboutButton: UIButton!
#IBOutlet weak var calculateButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib
yourWeightTextField.delegate = self
calorieNumberTextField.delegate = self
calculateButton.enabled = false
// Calling the textfield valueChanged Methods
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.ValueChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.ValueChanged);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func calculatorButtonTapped(sender: AnyObject) {
calculatorContainer.hidden = false
inspirationLabel.hidden = true
beginningLabel.hidden = true
menuExampleButton.hidden = true
aboutButton.hidden = true
}
var yourWeightFilled = false
var calorieNumberFilled = false
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
// If the textfields have the properties of the function
if textField == yourWeightTextField {
yourWeightFilled = text.toInt() != nil
} else if textField == calorieNumberTextField {
calorieNumberFilled = text.toInt() != nil
}
return true
}
func textFieldShouldReturn(textField: UITextField) -> Bool
{
textField.resignFirstResponder();
return true;
}
// The methods to close the keyboard when editing is finished
#IBAction func yourWeightEditingDidEnd(sender: AnyObject) {
yourWeightTextField.resignFirstResponder()
}
#IBAction func calorieNumberEditingDidEnd(sender: AnyObject) {
calorieNumberTextField.resignFirstResponder()
}
#IBAction func yourWeightValueChanged(sender: AnyObject) {
// If both variables are true and the text fields contain integers, enable button
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
#IBAction func calorieNumberValueChanged(sender: AnyObject) {
// If both variables are true and the text fields contain integers, enable button
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
}
You should look for EditingChaged event, not ValueChanged
EDIT:
What I mean is to change from:
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.ValueChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.ValueChanged);
to :
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.EditingChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.EditingChanged);
You simply are looking for wrong event.
If you are looking for a text changed event , then Right Click on the text field select Editing Did End from the Sent Events . You can see a circle on the right end click the circle Hold Down Ctrl and Drag it to your ViewController file. Name the Action you want and . I have provided some screen shots for this.
Here i name the Action TextChanged
I am Using Xcode 7 Swift 2 here
Right Click on the Text Box and You can see Something Like this
Finally You can see the TextChanged event Created. when you type something on a text box and click return this event fires.

Resources