UITextField Subclass not calling shouldChangeText(in range:replacementText:) - ios

I have a subclass of UITextField that works in all respects except that the overridden method shouldChangeText(in range:replacementText:) is not being called. I am trying to make a field that permits the user to enter only valid decimal numbers, and got that working in the equivalent delegate method, but since I will be using this in many places I want to just subclass it once.
My entire class is
class FWNumberField: FWTextField {
override func shouldChangeText(in range: UITextRange, replacementText text: String) -> Bool {
if text == ".", self.text == nil || self.text!.isEmpty || self.text!.contains(".") {
return false
} else {
return true
}
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) { return false }
return super.canPerformAction(action, withSender: sender)
}
}
Where FWTextField is a subclass of UITextField that adds a simple inputAccessoryView that has a "Done" button, and the keyboard is restricted to a decimal keyboard in my Storyboard.
My storyboard and IBOutlet both have their types set to FWNumberField. Any thoughts on why the method isn't being called? The docs say it should be.

Related

How to create OTP verification screen and detect delete backward on multiple uitextfield is Swift

so i make this otp screen but i have some catch,
i make this otp screen with bunch of uitextfield and i make the logic of it, but i just cant delete on of the num in the textfield that i make
the textfield wont delete when i fill like the first 2 of my num, even i pressess backButton it wont work.....but it will work when i fill the whole num of textfield, in my case is six.
so i have to fill all six of the number and i can delete the number from the textfield, it wont work if only half fill in the textfield.
heres my code :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if ((textField.text?.count)! < 1) && (string.count > 0) {
if textField == txtOTP1 {
txtOTP2.becomeFirstResponder()
}
if textField == txtOTP2 {
txtOTP3.becomeFirstResponder()
}
if textField == txtOTP3 {
txtOTP4.becomeFirstResponder()
}
if textField == txtOTP4 {
txtOTP5.becomeFirstResponder()
}
if textField == txtOTP5{
txtOTP6.becomeFirstResponder()
}
if textField == txtOTP6{
txtOTP6.resignFirstResponder()
}
textField.text = string
return false
}else if ((textField.text?.count)! >= 1) && (string.count == 0) {
if textField == txtOTP2{
txtOTP1.becomeFirstResponder()
}
if textField == txtOTP3{
txtOTP2.becomeFirstResponder()
}
if textField == txtOTP4{
txtOTP3.becomeFirstResponder()
}
if textField == txtOTP5{
txtOTP4.becomeFirstResponder()
}
if textField == txtOTP6{
txtOTP5.becomeFirstResponder()
}
if textField == txtOTP1{
txtOTP1.resignFirstResponder()
}
textField.text = ""
return false
}
else if (textField.text?.count)! >= 1 {
textField.text = string
return false
}
return true
}
thats the code i use to make the otp uitextField logic......please tell me i know theres something wrong with my logic, thanks.
i watch a tutorial to make this otp screen in this vid
https://www.youtube.com/watch?v=gZnBXh0TRO8
and according to the maker, he said that to fix this issue i just need to "set user interactions for textfield false and make first textfield first responder", i think i just did that but i maybe i did it wrong....
i really need to fix this guys, thanks.
Instead of fixing that code I prefer to create a custom text field that would inform when the deleteBackward key is pressed. So first subclass a UITextField:
import UIKit
class SingleDigitField: UITextField {
// create a boolean property to hold the deleteBackward info
var pressedDelete = false
// customize the text field as you wish
override func willMove(toSuperview newSuperview: UIView?) {
keyboardType = .numberPad
textAlignment = .center
backgroundColor = .blue
isSecureTextEntry = true
isUserInteractionEnabled = false
}
// hide cursor
override func caretRect(for position: UITextPosition) -> CGRect { .zero }
// hide selection
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { [] }
// disable copy paste
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { false }
// override deleteBackward method, set the property value to true and send an action for editingChanged
override func deleteBackward() {
pressedDelete = true
sendActions(for: .editingChanged)
}
}
Now in your ViewCOntroller:
import UIKit
class ViewController: UIViewController {
// connect the textfields outlets
#IBOutlet weak var firstDigitField: SingleDigitField!
#IBOutlet weak var secondDigitField: SingleDigitField!
#IBOutlet weak var thirdDigitField: SingleDigitField!
#IBOutlet weak var fourthDigitField: SingleDigitField!
override func viewDidLoad() {
super.viewDidLoad()
// add a target for editing changed for each field
[firstDigitField,secondDigitField,thirdDigitField,fourthDigitField].forEach {
$0?.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
// make the firsDigitField the first responder
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
}
// here you control what happens to each change that occurs to the fields
#objc func editingChanged(_ textField: SingleDigitField) {
// check if the deleteBackwards key was pressed
if textField.pressedDelete {
// reset its state
textField.pressedDelete = false
// if the field has text empty its content
if textField.hasText {
textField.text = ""
} else {
// otherwise switch the field, resign the first responder and activate the previous field and empty its contents
switch textField {
case secondDigitField, thirdDigitField, fourthDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case secondDigitField:
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
firstDigitField.text = ""
case thirdDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
secondDigitField.text = ""
case fourthDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
thirdDigitField.text = ""
default:
break
}
default: break
}
}
}
// make sure there is only one character and it is a number otherwise delete its contents
guard textField.text?.count == 1, textField.text?.last?.isWholeNumber == true else {
textField.text = ""
return
}
// switch the textField, resign the first responder and make the next field active
switch textField {
case firstDigitField, secondDigitField, thirdDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case firstDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
case secondDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
case thirdDigitField:
fourthDigitField.isUserInteractionEnabled = true
fourthDigitField.becomeFirstResponder()
default: break
}
case fourthDigitField:
fourthDigitField.resignFirstResponder()
default: break
}
}
}
Xcode 12 sample project

Disable all UIResponderStandardEditActions when UITextfield is empty

I am currently trying to prevent all UIResponderStandardEditActions like copy, paste, delete from showing up when the UITextfield is empty. I would only like to show them if the user has types a message. I have tried 2 solutions and currently don't work, I'm not sure if its to do with iOS 12 or. I have tried overriding the canPerformAction method both in a UITextfield extension and using a custom class later assigned to the UITextfield in the Storyboard but no luck. Is there another way to do this. Here is what I have tried.
extension UITextField {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if self.text!.isEmpty {
return false
}
return action == #selector(UIResponderStandardEditActions.paste(_:))
}
}
class CustomTextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) || action == #selector(UIResponderStandardEditActions.copy(_:)) || action == #selector(UIResponderStandardEditActions.delete(_:)) {
return false
}
return true
}
}
Just override with your subclass and use this class instead of UITextField. By the way, this will disable copy paste,cut for whole conditions so that you need to add some hasText: Bool or related condition to the switch case.
#IBDesignable
class ActionsDisabledUITextField: UITextField {
#IBInspectable var isPasteEnabled: Bool = false
#IBInspectable var isSelectEnabled: Bool = false
#IBInspectable var isSelectAllEnabled: Bool = false
#IBInspectable var isCopyEnabled: Bool = false
#IBInspectable var isCutEnabled: Bool = false
#IBInspectable var isDeleteEnabled: Bool = false
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(UIResponderStandardEditActions.paste(_:)) where !isPasteEnabled,
#selector(UIResponderStandardEditActions.select(_:)) where !isSelectEnabled,
#selector(UIResponderStandardEditActions.selectAll(_:)) where !isSelectAllEnabled,
#selector(UIResponderStandardEditActions.copy(_:)) where !isCopyEnabled,
#selector(UIResponderStandardEditActions.cut(_:)) where !isCutEnabled,
#selector(UIResponderStandardEditActions.delete(_:)) where !isDeleteEnabled:
return false
default:
//return true : this is not correct
return super.canPerformAction(action, withSender: sender)
}
}
}

How to jump to next UITextField when editing has finished?

I want to inspect my current UITextField and jump to the next one in case that editing has finished.
So far I have this code implemented, but for some reason is not working.
IMPORTANT: The code that actually work as expected is Robert approach, see it below. I couldn't vote for him since my reputation is low.
NEW ADDED: I am using this to limit to ONE character
//Limit the length of the UITextfields
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.characters.count + string.characters.count - range.length
print("textField...shouldChangeCharactersInRange method called")
switch textField {
case rateTvA:
return newLength <= limitLength
case rateTvB:
return newLength <= limitLength
case rateTvC:
return newLength <= limitLength
// Do not put constraints on any other text field in this view
// that uses this class as its delegate.
default:
return true
}
}
NOTE: I am expecting to receive just ONE single character for each UITextField
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
let charCount = textField.text!.characters.count
print("textFieldShouldEndEditing method called")
print("How many characters has been typed in? \(charCount)")
switch textField {
case rateTvA:
if(charCount > 0) {
rateTvB.becomeFirstResponder()
return true
}
case rateTvB:
if(charCount > 0) {
rateTvC.becomeFirstResponder()
return true
}
case rateTvC:
if(charCount > 0) {
rateTVD.becomeFirstResponder()
return true
}
default:
return false
}
return false
}
What you need depends upon what you want the user's experience to be.
1) You want to automatically jump to the next text field after the user has entered a single character. In this case you should use the Interface Builder to connect the text field's "Editing Changed" event to an IBAction method in your controller. That IBAction method should perform the first responder switching logic.
2) You want the user to take some action to indicate that editing has completed. That action's handler should then perform the first responder switching logic. I think that the return key is a good way to go. In this case you would use the TextField delegate's textFieldShouldReturn method.
Additionally, you should think about what you want to have happen if the user ends the editing of text field A by clicking in one of text fields B or C (or upon some other UI element that your are presenting); in which case your text field delegate's textFieldXXXXEndEditing methods will be called. The question is: should your textFieldXXXXEndEditing method switch the focus away from the element that the user has chosen?
Finally, you said that your code is not working. What is not working? Is the textFieldShouldEndEditing method not being called? If no then you might not be assigning to the text field's delegate property. Or, you might be confused about what causes the textFieldShouldEndEditing method to be called (my suggestions above should give you some clues).
Here is some code. The outlets should be connected using IB. Also, use IB to connect the Editing Changed event of each of the three text fields to the IBAction method.
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textFieldA: UITextField!
#IBOutlet weak var textFieldB: UITextField!
#IBOutlet weak var textFieldC: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textFieldA.delegate = self
textFieldB.delegate = self
textFieldC.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
print("Should begin editing")
return true;
}
func textFieldDidBeginEditing(textField: UITextField) {
print("Did begin editing")
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
print("Should end editing")
return true;
}
func textFieldDidEndEditing(textField: UITextField) {
print("Did end editing")
}
func textFieldShouldClear(textField: UITextField) -> Bool {
print("Should clear")
return true;
}
func textFieldShouldChangeCharactersInRange(textField: UITextField, range: NSRange, replacement: String) -> Bool {
print("Should change")
return true;
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
print("User pressed return: switching to next text field")
switchFields(textField)
return true;
}
#IBAction func textChanged(textField: UITextField) {
print("User entered a character; switching to next text field")
switchFields(textField)
}
private func switchFields(textField: UITextField) {
switch textField {
case textFieldA:
textFieldB.becomeFirstResponder()
case textFieldB:
textFieldC.becomeFirstResponder()
case textFieldC:
textFieldA.becomeFirstResponder()
default:
break
}
}
}
You should probably change the responder in the "textFieldDidEndEditing" delegate call. So take out the xxx.becomeFirstResponder() calls in "textFieldShouldEndEditing" and implement roughly:
optional func textFieldDidEndEditing(_ textField: UITextField) {
switch textField {
case rateTvA:
rateTvB.becomeFirstResponder()
case rateTvB:
rateTvC.becomeFirstResponder()
case rateTvC:
rateTVD.becomeFirstResponder()
default:
return
}
return
I haven't compiled this so beware.
If you don't want to depend on a Third-party or any library you can follow below
Use textfield delegate (Swift, Objective-c)
In Objective c you can do this:
-(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if (_verifyOTP.text.length >= 1 && range.length == 0)
{
[self.verifyOTP2 becomeFirstResponder];
}
if(_verifyOTP2.text.length >= 1 && range.length == 0) {
[_verifyOTP2 resignFirstResponder];
[self.verifyOTP3 becomeFirstResponder];
}
if (_verifyOTP3.text.length >= 1 && range.length == 0)
{
[_verifyOTP3 resignFirstResponder];
[self.verifyOTP4 becomeFirstResponder];
}
if (_verifyOTP4.text.length >= 1 && range.length == 0)
{
[self.verifyOTP4 becomeFirstResponder];
}
return YES;
}
In swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (_verifyOTP.text.count >= 1 && range.length == 0)
{
self.verifyOTP2.becomeFirstResponder()
}
if(_verifyOTP2.text.count >= 1 && range.length == 0) {
_verifyOTP2.resignFirstResponder()
self.verifyOTP3.becomeFirstResponder()
}
if (_verifyOTP3.text.count >= 1 && range.length == 0)
{
_verifyOTP3.resignFirstResponder()
self.verifyOTP4.becomeFirstResponder()
}
if (_verifyOTP4.text.count >= 1 && range.length == 0)
{
self.verifyOTP4 becomeFirstResponder()
}
return true;
}
Else you can use PinView third party Library
Pin view Github link

Swift next button change textfield

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
}

Using "Next" as a Return Key

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

Resources