I have an app built with Swift and iOS8. In my ViewController I had two textfields.
I use this code:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == self.textfield_A {
self.textfield_B.becomeFirstResponder()
}
if textField == self.textfield_B {
self.textfield_B.resignFirstResponder()
}
return true
}
The effect:
I select textfield_A and Press the Next Button -> the cursor jump to textfield_B
On textfield_B I press the Done Button -> the keyboard will be hidden.
All works fine.
But now I have made a change and the code doesn't work like this anymore.
I changed the textfield_A to a textView.
Any idea how I have to modify my code?
You have to add an extension, this is the extension for swift 3.0
extension UITextField {
class func connectFields(fields:[UITextField]) -> Void {
guard let last = fields.last else {
return
}
for i in 0 ..< fields.count - 1 {
fields[i].returnKeyType = .next
fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
}
last.returnKeyType = .done
last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
}
}
and after you add this line of code:
UITextField.connectFields(fields: [field1, field2, field3])
textFieldShouldReturn won't be called anymore for textfield_A now because is is a textview now, not a textfield.
Try adding this function
func textView(textView: UITextView!, shouldChangeTextInRange: NSRange, replacementText: NSString!) -> Bool {
if(replacementText == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
If you are using both textfield then below code might be helpful to you my friend
Make sure your text fields have their delegate set and implement the textFieldShouldReturn method. This is the method that is called when the user taps the return key (no matter what it looks like).
The method might look something like this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == self.field1 {
self.field2.becomeFirstResponder()
}
return true
}
And do not forget to give Delegate
TextField.delegate = self
Using textField you can detect return key press by using textFieldShouldReturn: method. textView is a multi-line input so return key just adds a new line, so the easiest way to catch return key press is to implement UITextViewDelegate method:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
You can do it this way in your doneButton action:
#IBAction func donePressed(sender: AnyObject) {
if textV.resignFirstResponder() { //check if cursor is at textView.
textfield_B.becomeFirstResponder() //move it to your next textField.
} else {
textfield_B.resignFirstResponder() //else hide your keyboard.
}
}
And your result will be:
Hope it helps.
Swift 4.2
This is a More Generic Solution you can use this code with any amount of TextFields.
Just inherit UITextFieldDelegate and update the Textfield Tag according to the order
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let txtTag:Int = textField.tag
if let textFieldNxt = self.view.viewWithTag(txtTag+1) as? UITextField {
textFieldNxt.becomeFirstResponder()
}else{
textField.resignFirstResponder()
}
return true
}
Related
I am trying to create otp textfield using five textfield.All working fine if you add top, but issue is occurred when user try to add textfield empty and trying to backspace and it was not call any delegate method of UItextfiled which I already added.
I tried this :-
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
but it's called when textfield is not empty.
For example :-
In below screen shot add 1 and on two different textfield and third one is empty but when I try to backspace it's need to go in second textfield(third is field is empty) this is what I was facing issue from mine side.
Thanks
followed by #Marmik Shah and #Prashant Tukadiya answer here I add my answer , for quick answer I taken the some code from here
step 1 :
create the IBOutletCollection for your all textfields as well as don't forget to set the tag in all textfields in the sequence order, for e.g [1,2,3,4,5,6]
class ViewController: UIViewController{
#IBOutlet var OTPTxtFields: [MyTextField]! // as well as set the tag for textfield in the sequence order
override func viewDidLoad() {
super.viewDidLoad()
//change button color and other options
OTPTxtFields.forEach { $0.textColor = .red; $0.backspaceTextFieldDelegate = self }
OTPTxtFields.first.becomeFirstResponder()
}
step 2 :
in your current page UITextField delegate method
extension ViewController : UITextFieldDelegate, MyTextFieldDelegate {
func textFieldDidEnterBackspace(_ textField: MyTextField) {
guard let index = OTPTxtFields.index(of: textField) else {
return
}
if index > 0 {
OTPTxtFields[index - 1].becomeFirstResponder()
} else {
view.endEditing(true)
}
}
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 < 2 && !newString.isEmpty {
textFieldShouldReturnSingle(textField, newString : newString)
// return false
}
return newString.count < 2 || string == ""
//return true
}
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(paste(_:)) {
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()
callOTPValidate()
}
}
}
Step 3:
create the textfield class for access the backward function
class MyTextField: UITextField {
weak var myTextFieldDelegate: MyTextFieldDelegate?
override func deleteBackward() {
if text?.isEmpty ?? false {
myTextFieldDelegate?.textFieldDidEnterBackspace(self)
}
super.deleteBackward()
}
}
protocol MyTextFieldDelegate: class {
func textFieldDidEnterBackspace(_ textField: MyTextField)
}
step - 4
finally follow the #Marmik Shah answer for custom class for your UITextField
Step 5
get the values from each textfield use this
func callOTPValidate(){
var texts: [String] = []
OTPTxtFields.forEach { texts.append($0.text!)}
sentOTPOption(currentText: texts.reduce("", +))
}
func sentOTPOption(currentText: String) {
print("AllTextfieldValue == \(currentText)")
}
You can override the function deleteBackward()
Create a new Class that inherits UITextField and trigger and EditingEnd event.
class MyTextField: UITextField {
override func deleteBackward() {
super.deleteBackward()
print("Backspace");
self.endEditing(true);
}
}
Then, in your Storyboard, add a custom class for the UITextField
Next, in your view controller, in the editingEnd action, you can switch the textfield. For this to work, you will need to set a tag value for each of your textfield.
For example, you have two text fields, tfOne and tfTwo.
tfOne.tag = 1; tfTwo.tag = 2
Now, if currently you are editing tfTwo and backspace is clicked, then you set the currently editing text field to tfOne
class ViewController: UIViewController {
#IBOutlet weak var tfOne: MyTextField!
#IBOutlet weak var tfTwo: MyTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func editingEnded(_ sender: UITextField) {
// UITextField editing ended
if(sender.tag == 2) {
self.tfOne.becomeFirstResponder();
}
}
}
You can give tag to your textfield in sequence like 101,102,103,104,105.
when backspace tapped. check the length of string is equal to 0. then goto textfield.tag - 1 until you get first textfield.like if you are on textfield 102 then goto textfield 102-1 = 101.
Same as when enter any character goto next textfield until you reach to last textfield like if you are on textfield 102 then goto textfield 102+1 = 103.
You can use (self.view.viewWithTag(yourTag) as? UITextField)?.becomeFirstResponder()
I don't have system with me so couldn't able to post code
I want to hide keyboard before move to next controller. Also implement self.view.enditing(true) in viewWillDisapear . But keybard not hide how to hide this?
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == tagTxt{
self.view.endEditing(true)
textField.resignFirstResponder()
let interstControlle = self.storyboard?.instantiateViewController(withIdentifier: "InterestViewController") as! InterestViewController
interstControlle.PContId = "InterestViewController"
interstControlle.delegate = self
if tagsID.count > 0{
interstControlle.PTagsID = tagsID
}
self.navigationController?.pushViewController(interstControlle, animated: true)
}
}
Use this:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if textField == tagTxt{
return NO;
}
return YES;
}
Swift 3:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == tagTxt{
return false
}
return true
}
You have to return false before textfield get active. We can see difference by name textFieldShouldBeginEditing and textFieldDidBeginEditing.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == tagTxt {
//Your code to move in next view
return false
}
return true
}
Is there a way to automatically hide keyboard in swift after inputting four characters? I actually have a code that hides the keyboard but the user has to click anywhere on the screen. Here's the code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
Thanks in advance!
If I got your question correctly, Consider below example code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
txtF.delegate = self
txtF.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
func textFieldDidChange(textField: UITextField) {
if textField == txtF {
if textField.text?.characters.count == 4 {
self.txtF.resignFirstResponder()
}
}
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == txtF {
if textField.text?.characters.count > 3 {
self.txtF.resignFirstResponder()
return false
} else {
return true
}
} else {
return true
}
}
}
With above code keyboard will hide when textField have 4 characters and after that if user again tap on textField keyboard will pop up be user will not able to enter any text into textField and keyboard will hide again.
Result will be:
Hope this will help.
Here is the simplest way to hide the keyboard or a numberpad.
First you need a button and you need to make it the size of the screen.Send it to the back of the scene and connect it to an IBAction.Then you code should look like this:
#IBAction func HideKeyboard(sender: AnyObject) {
YourKeyboardHere.resignFirstResponder()
}
This should work for all types of keyboard.
This worked for me:
// Outlet to textfield, editing changed
#IBAction func textFieldEditingChanged(sender: UITextField) {
if sender.text?.characters.count == 4 {
view.endEditing(true)
}
// Optional if you don't want the user to paste in more than 4 characters
else if sender.text?.characters.count > 4 {
sender.text = nil
}
}
I also recommend you setting the
textField.clearsOnBeginEditing = true
so the textField gets cleared when the user clicks on it again.
You should use textfield delegate shouldChangeCharactersInRange something like,
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if (textField.text.length == 4) {
[textField resignFirstResponder];
}
return YES;
}
In Swift,
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField.text?.characters.count == 4 {
textField.resignFirstResponder()
}
return true
}
Hope this will help :)
func textFieldDidBeginEditing(textField: UITextField) {
scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70), animated: true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
scrlView .setContentOffset(CGPointMake(0, 0), animated: true)
return true
}
How can I move cursor from one textfield to another automatically after entering value in the first textfield?
first, you need add tags to your text fields orderly
func textFieldShouldReturn(textField: UITextField) -> Bool {
let nextTag = textField.tag + 1;
// Try to find next responder
var nextResponderTxt: UITextField!
nextResponderTxt = textField.superview?.viewWithTag(nextTag) as? UITextField
if ((nextResponderTxt) != nil) {
// Found next responder, so set it.
nextResponderTxt.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
return false; // We do not want UITextField to insert line-breaks.
}
Also you can change the return key to next
I use the "Next" value for the "Return Key" to get the Next button in place of the Done button, but (obviously) pressing it doesn't automatically move to the next UITextField in my view.
What's the right way to do this? I have seen many answers, but anyone have a swift solution?
Make sure your text fields have their delegate set and implement the textFieldShouldReturn method. This is the method that is called when the user taps the return key (no matter what it looks like).
The method might look something like this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == self.field1 {
self.field2.becomeFirstResponder()
}
return true
}
The actual logic in here might vary. There are numerous approaches, and I'd definitely advise against a massive if/else chain if you have lots of text fields, but the gist here is to determine what view is currently active in order to determine what view should become active. Once you've determined which view should become active, call that view's becomeFirstResponder method.
For some code cleanliness, you might consider a UITextField extension that looks something like this:
private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
}
}
}
And then change our textFieldShouldReturn method to look like this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.nextField?.becomeFirstResponder()
return true
}
Once you've done this, it should simply be a matter of setting each text field's new nextField property in viewDidLoad:
self.field1.nextField = self.field2
self.field2.nextField = self.field3
self.field3.nextField = self.field4
self.field4.nextField = self.field1
Although if we really wanted, we could prefix the property with #IBOutlet, and that would allow us to hook up our "nextField" property right in interface builder.
Change the extension to look like this:
private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
#IBOutlet var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
}
}
}
And now hook up the nextField property in interface builder:
(Set up your delegate while you're here too.)
And of course, if the nextField property returns nil, the keyboard just hides.
Here is an example in Swift:
I created a screen with 6 UITextFields. I assigned them the tags 1 through 6 in Interface Builder. I also changed the Return key to Next in IB. Then I implemented the following:
import UIKit
// Make your ViewController a UITextFieldDelegate
class ViewController: UIViewController, UITextFieldDelegate {
// Use a dictionary to define text field order 1 goes to 2, 2 goes to 3, etc.
let nextField = [1:2, 2:3, 3:4, 4:5, 5:6, 6:1]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Make ourselves the delegate of the text fields so that textFieldShouldReturn
// will be called when the user hits the Next/Return key
for i in 1...6 {
if let textField = self.view.viewWithTag(i) as? UITextField {
textField.delegate = self
}
}
}
// This is called when the user hits the Next/Return key
func textFieldShouldReturn(textField: UITextField) -> Bool {
// Consult our dictionary to find the next field
if let nextTag = nextField[textField.tag] {
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
// Have the next field become the first responder
nextResponder.becomeFirstResponder()
}
}
// Return false here to avoid Next/Return key doing anything
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
There is nothing wrong with the other answers, this is just a different approach with the benefit of being more focused on OOP - imho (although this is a bit more work up front, it can be reused). In the storyboard, I start off adding tags with a distinct range (e.g 800-810) that define the specific order of the fields I want to move between. This has the benefit of working across all subviews in the main view so that one can navigate between UITextField's and UITextView's (and any other control) as needed.
Generally - I typically try to have view controllers message between views and custom event handler objects. So I use a message (aka, NSNotification) passed back to the view controller from a custom delegate class.
(TextField Delegate Handler)
Note: In AppDelegate.swift: let defaultCenter = NSNotificationCenter.defaultCenter()
//Globally scoped
struct MNGTextFieldEvents {
static let NextButtonTappedForTextField = "MNGTextFieldHandler.NextButtonTappedForTextField"
}
class MNGTextFieldHandler: NSObject, UITextFieldDelegate {
var fields:[UITextField]? = []
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return true
}
func textFieldDidBeginEditing(textField: UITextField) {
textField.backgroundColor = UIColor.yellowColor()
}
func textFieldDidEndEditing(textField: UITextField) {
textField.backgroundColor = UIColor.whiteColor()
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return true
}
func textFieldShouldClear(textField: UITextField) -> Bool {
return false
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
return true
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
//passes the message and the textField (with tag) calling the method
defaultCenter.postNotification(NSNotification(name: MNGTextFieldEvents.NextButtonTappedForTextField, object: textField))
return false
}
}
This allows my view controller to remain focused on it's main job of handling the messaging between objects, model and view.
(View Controller receives a message from the delegate and passes instructions using the advanceToNextField function)
Note: In my storyboard my custom handler classes are defined using an NSObject and that object is linked into the storyboard as a delegate for the controls that I need monitored. Which causes the custom handler class to be initialized automatically.
class MyViewController: UIViewController {
#IBOutlet weak var tagsField: UITextField! { didSet {
(tagsField.delegate as? MNGTextFieldHandler)!.fields?.append(tagsField)
}
}
#IBOutlet weak var titleField: UITextField!{ didSet {
(titleField.delegate as? MNGTextFieldHandler)!.fields?.append(titleField)
}
}
#IBOutlet weak var textView: UITextView! { didSet {
(textView.delegate as? MNGTextViewHandler)!.fields?.append(textView)
}
}
private struct Constants {
static let SelectorAdvanceToNextField = Selector("advanceToNextField:")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerEventObservers()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
deRegisterEventObservers()
}
func advanceToNextField(notification:NSNotification) {
let currentTag = (notification.object as! UIView).tag
for aView in self.view.subviews {
if aView.tag == currentTag + 1 {
aView.becomeFirstResponder()
}
}
}
func registerEventObservers () {
defaultCenter.addObserver(self, selector: Constants.SelectorAdvanceToNextField, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil)
}
func deRegisterEventObservers() {
defaultCenter.removeObserver(self, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil)
}
....
}
Just another way to achieve the result that I found helpful. My app had 11 text fields followed by a text view. I needed to be able to cycle through all fields using the next key and then resign the keyboard following the textview (i.e. other notes).
In the storyboard, I set the tag on all of the fields (both text and textview) starting with 1 through 12, 12 being the textview.
I'm sure there are other ways to do it and this method isn't perfect, but hopefully it helps someone.
In code, I wrote the following:
func textFieldShouldReturn(textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
//Handle Textview transition, Textfield programmatically
if textField.tag == 11 {
//Current tag is 11, next field is a textview
self.OtherNotes.becomeFirstResponder()
} else if nextTag > 11 {
//12 is the end, close keyboard
textField.resignFirstResponder()
} else {
//Between 1 and 11 cycle through using next button
let nextResponder = self.view.viewWithTag(nextTag) as? UITextField
nextResponder?.becomeFirstResponder()
}
return false
}
func textFieldDidEndEditing(textField: UITextField) {
textField.resignFirstResponder()
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
//Remove keyboard when clicking Done on keyboard
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
Another approach, if you're using storyboards, you can change the textfield's attribute for Return Key.
Currently you have the following options: Default (Return), Go, Google, Join, Next, Route, Search, Send, Yahoo, Done, Emergency Call, Continue