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
}
Related
I have an onboarding user flow:
Name -> Age -> Gender
Each of the screens shares the same structure:
Question (top)
Input (middle)
Continue (bottom)
I have a class OnboardingHelper.swift that creates a class to set the question box and continue button:
class UserOnboardingHelper{
var text: String
var questionbox: UIView
var viewController: UIViewController
var continueButton: UIButton
init(text: String, questionbox: UIView, viewController: UIViewController, continueButton: UIButton){
self.text = text
self.questionbox = questionbox
self.viewController = viewController
self.continueButton = continueButton
}
func setQuestionBox(){
//sets question box
}
func setContinueButton(){
//sets continue button
enableContinueButton()
addContinueButtonPath()
}
func enableContinueButton(){
//enables continue button
}
func disableContinueButton(){
//disables continue button
}
func addContinueButtonPath(){
//sets path of continue button based on which view
}
}
In each of the onboarding ViewControllers I am setting the class in ViewDidLoad():
class NamePageViewController: UIViewController, UITextFieldDelagate {
#IBOutlet weak var questionbox: UIView!
#IBOutlet weak var continueButton: UIButton!
#IBOutlet weak var inputLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let namePageSettings = UserOnboardingHelper(text: "What is your name", questionbox: questionbox, viewController: self, continueButton: continueButton)
namePageSettings.setQuestionBox()
namePageSettings.setContinueButton()
inputLabel.delegate = self
if nameIsFilled {
namePageSettings.enableContinueButton()
} else{
namePageSettings.disableContinueButton()
}
}
}
The issue is that in the ViewController I textFieldDidEndEditing() function which needs to call the namePageSettings class from viewDidLoad()
func textFieldDidEndEditing(_ textField: UITextField){
if (textField.text?.empty)!{
//I want to call disableContinueButton() from UserOnboardingHelper
} else {
//I want to enable enableContinueButton() from UserOnboardingHelper
}
}
Trying to understand if:
The overall approach is correct and if not, what's the best way
If the above approach is in the right direction, how should disableContinueButton() and enableContinueButton() be called?
Thanks in advance! Sorry if the approach is really dumb - I'm still trying to wrap my head around classes.
You can have the view controller have a weak reference to the onboarding helper, so you can still call helper methods without creating a retain cycle.
In NamePageViewController, add a property:
weak var userOnboardingHelper: UserOnboardingHelper?
Then, in UserOnboardingHelper's initializer, add:
self.viewController.userOnboardingHelper = self
You can now call the onboarding helper's methods in the view controller:
userOnboardingHelper.disableContinueButton()
userOnboardingHelper.enableContinueButton()
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
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!
In XCode 6.3.2, I have a UITextField:
#IBOutlet weak var uiswitchControlledTextField: UITextField!
I am now using a UISwitch (named mySwitch) to control its enabled or disabled state in the following way:
#IBOutlet weak var mySwitch: UISwitch!
mySwitch.addTarget(self, action: Selector("stateChanged:"), forControlEvents: UIControlEvents.ValueChanged)
//callback below:
func stateChanged(switchState: UISwitch) {
uiswitchControlledTextField.enabled = switchState.on
}
The above works well, however, I am looking to try if it would be possible to create a UITextFieldDelegate to control the above UITextField in the same way. So far, I have the following by implementing textFieldShouldBeginEditing, in which I wish to return false to disable the UITextField, but I don't know how to let the UISwitch dynamically return true or false from textFieldShouldBeginEditing
import Foundation
import UIKit
class SwitchControlledTextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return false; //do not show keyboard or cursor
}
}
In ViewController, I try to set
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
but it does not work as I wished. Any help would be appreciated.
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
The problem is that that line merely creates an instance of your SwitchControlledTextFieldDelegate class, which then immediately goes right back out of existence.
You need to use, as your text field delegate, some instance which already exists and which will persist - like, perhaps, your view controller!
(Xcode 7)
Use this:
override func viewDidLoad() {
super.viewDidLoad()
// Setting the delegate
self.textField3.delegate = self
self.editingSwitch.setOn(false, animated: false)
}
// Text Field Delegate Methods
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return self.editingSwitch.on
}
#IBAction func toggleTheTextEditor(sender: AnyObject) {
if !(sender as! UISwitch).on {
self.textField3.resignFirstResponder()
}
}
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.