如何浏览文本框(下一步/完成button)
如何通过iPhone键盘上的“下一步”button浏览所有文本字段?
最后一个文本字段应该closures键盘。
我已经设置了IB的button(下一步/完成),但现在我卡住了。
我实现了textFieldShouldReturn操作,但现在下一步button和完成buttonclosures键盘。
在Cocoa for Mac OS X中,你有下一个响应者链,在那里你可以问文本字段下一个控件应该有什么焦点。 这是什么让文本字段之间工作。 但是由于iOS设备没有键盘,只能触摸,这个概念还没有幸存下来,转向cocoa触摸。
无论如何,这可以轻松完成,有两个假设:
- 所有的“tabbable”
UITextField
都在同一个父视图上。 - 他们的“标签顺序”是由标签属性定义的。
假设你可以覆盖textFieldShouldReturn:如下所示:
-(BOOL)textFieldShouldReturn:(UITextField*)textField { NSInteger nextTag = textField.tag + 1; // Try to find next responder UIResponder* nextResponder = [textField.superview viewWithTag:nextTag]; if (nextResponder) { // Found next responder, so set it. [nextResponder becomeFirstResponder]; } else { // Not found, so remove keyboard. [textField resignFirstResponder]; } return NO; // We do not want UITextField to insert line-breaks. }
添加一些更多的代码,这些假设也可以被忽略。
更新: – SWIFT 2.0
func textFieldShouldReturn(textField: UITextField) -> Bool { let nextTage=textField.tag+1; // Try to find next responder let nextResponder=textField.superview?.viewWithTag(nextTage) as UIResponder! if (nextResponder != nil){ // Found next responder, so set it. nextResponder?.becomeFirstResponder() } else { // Not found, so remove keyboard textField.resignFirstResponder() } return false // We do not want UITextField to insert line-breaks. }
如果文本字段的超级视图将是一个UITableViewCell,那么下一个响应者将是
let nextResponder=textField.superview?.superview?.superview?.viewWithTag(nextTage) as UIResponder!
有一个更优雅的解决scheme,第一次看到它的时候就把我吹走了。 优点:
- 更接近OSX文本字段实现,其中文本字段知道焦点应该放在哪里
- 不依赖于设置或使用标签 – 这是脆弱的IMO用例
- 可以扩展到使用
UITextField
和UITextView
控件 – 或者任何键盘inputUI控件 - 不要使用样板UITextField委托代码来混乱你的视图控制器
- 与IB很好地集成,可以通过熟悉的选项 – 拖放来连接sockets。
创build一个名为nextField的IBOutlet
属性的UITextField子类。 这是标题:
@interface SOTextField : UITextField @property (weak, nonatomic) IBOutlet UITextField *nextField; @end
这是实现:
@implementation SOTextField @end
在您的视图控制器中,您将创build-textFieldShouldReturn:
委托方法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField { if ([textField isKindOfClass:[SOTextField class]]) { UITextField *nextField = [(SOTextField *)textField nextField]; if (nextField) { dispatch_async(dispatch_get_current_queue(), ^{ [nextField becomeFirstResponder]; }); } else { [textField resignFirstResponder]; } } return YES; }
在IB中,将您的UITextFields更改为使用SOTextField
类。 接下来,也在IB中,将每个“SOTextFields”的委托设置为“文件所有者”(这就是您为代理方法放置代码的地方 – textFieldShouldReturn)。 这个devise的优点是,现在你可以简单地右键单击任何文本字段,并将nextField插口分配给下一个要作为下一个响应者的SOTextField
对象。
此外,你可以做一些很酷的事情,像循环textFields,以便在最后一个失去焦点后,第一个将再次获得焦点。
这可以很容易地扩展到自动分配的returnKeyType
的SOTextField
到一个UIReturnKeyNext
如果有一个nextField分配 – less一件事情手动configuration。
这是我解决这个问题的方法。
为了解决这个问题(因为我讨厌依赖标签做东西),我决定添加一个自定义属性到UITextField对象。 换句话说,我在UITextField上创build了一个类,如下所示:
的UITextField + Extended.h
@interface UITextField (Extended) @property(retain, nonatomic)UITextField* nextTextField; @end
的UITextField + Extended.m
#import "UITextField+Extended.h" #import <objc/runtime.h> static char defaultHashKey; @implementation UITextField (Extended) - (UITextField*) nextTextField { return objc_getAssociatedObject(self, &defaultHashKey); } - (void) setNextTextField:(UITextField *)nextTextField{ objc_setAssociatedObject(self, &defaultHashKey, nextTextField, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
现在,我是这样使用它的:
UITextField *textField1 = ...init your textfield UITextField *textField2 = ...init your textfield UITextField *textField3 = ...init your textfield textField1.nextTextField = textField2; textField2.nextTextField = textField3; textField3.nextTextField = nil;
并执行textFieldShouldReturn方法:
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField { UITextField *next = theTextField.nextTextField; if (next) { [next becomeFirstResponder]; } else { [theTextField resignFirstResponder]; } return NO; }
我现在有一个UITextField的链表,每一个知道谁在下一行。
希望它会有所帮助。
这里没有一个代表团:
tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit) tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
ObjC:
[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; [tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
使用(大部分是未知的) UIControlEventEditingDidEndOnExit
UITextField
操作。
你也可以很容易地把它挂在故事板上,所以不需要授权或代码。
编辑:其实我无法弄清楚如何把这个在故事板上。 becomeFirstResponder
似乎不是这个控制事件提供的行动,这是可惜的。 尽pipe如此,你可以在你的ViewController中把所有的textfields都绑定到一个单独的action中,然后根据发送者确定哪个textField成为becomeFirstResponder
(尽pipe如此,它不像上面的编程解决scheme那样优雅,所以IMO在viewDidLoad
使用上面的代码)。
一个迅速的扩展应用mxcl的答案,使得这个特别容易(适应快速2.3旅行者):
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: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit) } last.returnKeyType = .Done last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit) } }
它很容易使用:
UITextField.connectFields([field1, field2, field3])
除了最后一个字段外,扩展名会将返回button设置为“下一步”,对于最后一个字段则为“完成”,并在点击时移动焦点/closures键盘。
Swift <2.3
extension UITextField { class func connectFields(fields:[UITextField]) -> Void { guard let last = fields.last else { return } for var i = 0; i < fields.count - 1; i += 1 { fields[i].returnKeyType = .Next fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit) } last.returnKeyType = .Done last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit) } }
SWIFT 3:像这样使用 –
UITextField.connectFields(fields: [field1, field2]) Extension: 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 = .go last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit) } }
更一致和可靠的方法是使用NextResponderTextField您可以完全从界面构build器进行configuration,无需设置委托或使用view.tag
。
所有你需要做的是
- 将您的
UITextField
的类types设置为NextResponderTextField
- 然后设置
nextResponderField
的出口指向下一个响应者,它可以是任何UITextField
或任何UIResponder
子类。 它也可以是一个UIButton,并且该库足够聪明,只有在它启用时才触发button的TouchUpInside
事件。
这里是图书馆在行动:
我喜欢Anth0和Answerbot已经提出的OO解决scheme。 但是,我正在做一个快速而小巧的POC,所以我不想用子类和类别混淆东西。
另一个简单的解决scheme是创build一个NSArray的字段,然后按next来查找下一个字段。 不是面向对象解决scheme,而是快速,简单,易于实现。 另外,您可以一目了然地查看和修改订单。
这是我的代码(build立在此线程中的其他答案):
@property (nonatomic) NSArray *fieldArray; - (void)viewDidLoad { [super viewDidLoad]; fieldArray = [NSArray arrayWithObjects: firstField, secondField, thirdField, nil]; } - (BOOL) textFieldShouldReturn:(UITextField *) textField { BOOL didResign = [textField resignFirstResponder]; if (!didResign) return NO; NSUInteger index = [self.fieldArray indexOfObject:textField]; if (index == NSNotFound || index + 1 == fieldArray.count) return NO; id nextField = [fieldArray objectAtIndex:index + 1]; activeField = nextField; [nextField becomeFirstResponder]; return NO; }
- 我总是返回NO,因为我不想插入换行符。 只是以为我会指出,因为当我返回YES它会自动退出后续字段或在我的TextView中插入换行符。 我花了一些时间来弄清楚。
- activeField跟踪活动字段的情况下滚动是必要unobscure领域的键盘。 如果您有类似的代码,请确保在更改第一个响应者之前分配activeField。 立即更改第一响应者,并立即触发KeyboardWasShown事件。
这里是使用UIControl上的一个类别的Tab键实现。 这个解决scheme具有Michael和Anth0方法的所有优点,但是适用于所有UIControls,而不仅仅是UITextField
。 它也可以与界面生成器和故事板无缝工作。
源代码和示例应用程序: UITontrolsWithTabbing的GitHub存储库
用法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField transferFirstResponderToNextControl]; return NO; }
标题:
// // UIControl+NextControl.h // UIControlsWithTabbing // #import <UIKit/UIKit.h> @interface UIControl (NextControl) @property (nonatomic, weak) IBOutlet UIControl *nextControl; - (BOOL)transferFirstResponderToNextControl; @end
执行:
#import "UIControl+NextControl.h" #import <objc/runtime.h> static char defaultHashKey; @implementation UIControl (NextControl) - (UIControl *)nextControl { return objc_getAssociatedObject(self, &defaultHashKey); } - (void)setNextControl:(UIControl *)nextControl { objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)transferFirstResponderToNextControl { if (self.nextControl) { [self.nextControl becomeFirstResponder]; return YES; } [self resignFirstResponder]; return NO; } @end
从一个文本字段退出后,您可以调用[otherTextField becomeFirstResponder],下一个字段获得焦点。
这实际上可能是一个棘手的问题,因为经常你也想滚动屏幕或者调整文本字段的位置,所以编辑时很容易看到。 只要确保进行大量的testing,以不同的方式出入文本字段,并提前离开(总是给用户一个选项,而不是进入下一个字段,通常用“完成”导航栏)
-(BOOL)textFieldShouldReturn:(UITextField *)textField { [[self.view viewWithTag:textField.tag+1] becomeFirstResponder]; return YES; }
按下“完成”button时,解除键盘的一种非常简单的方法是:
在标题中创build一个新的IBAction
- (IBAction)textFieldDoneEditing:(id)sender;
在实现文件(.m文件)中添加以下方法:
- (IBAction)textFieldDoneEditing:(id)sender { [sender resignFirstResponder]; }
然后,当您将IBAction链接到文本框时 – 链接到“退出结束”事件。
首先在xib中设置键盘返回键,否则你可以在viewdidload
编写代码:
passWord.returnKeyType = UIReturnKeyNext; -(BOOL)textFieldShouldReturn:(UITextField *)textField { if(textField == eMail) { [textField resignFirstResponder]; [userName becomeFirstResponder]; } if (textField==userName) { [textField resignFirstResponder]; [passWord becomeFirstResponder]; } if (textField==passWord) { [textField resignFirstResponder]; [country becomeFirstResponder]; } if (textField==country) { [textField resignFirstResponder]; } return YES; }
我惊讶于这里有多less个答案不能理解一个简单的概念:在你的应用程序中通过控件导航不是视图本身应该做的事情。 控制器的工作是确定哪个控制器能够成为下一个响应者。
另外,大多数答案只适用于向前导航,但用户也可能想要倒退。
所以这就是我所想的。 您的表单应由视图控制器pipe理,视图控制器是响应器链的一部分。 所以你完全可以自由地实现以下方法:
#pragma mark - Key Commands - (NSArray *)keyCommands { static NSArray *commands; static dispatch_once_t once; dispatch_once(&once, ^{ UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(tabForward:)]; UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(tabBackward:)]; commands = @[forward, backward]; }); return commands; } - (void)tabForward:(UIKeyCommand *)command { NSArray *const controls = self.controls; UIResponder *firstResponder = nil; for (UIResponder *const responder in controls) { if (firstResponder != nil && responder.canBecomeFirstResponder) { [responder becomeFirstResponder]; return; } else if (responder.isFirstResponder) { firstResponder = responder; } } [controls.firstObject becomeFirstResponder]; } - (void)tabBackward:(UIKeyCommand *)command { NSArray *const controls = self.controls; UIResponder *firstResponder = nil; for (UIResponder *const responder in controls.reverseObjectEnumerator) { if (firstResponder != nil && responder.canBecomeFirstResponder) { [responder becomeFirstResponder]; return; } else if (responder.isFirstResponder) { firstResponder = responder; } } [controls.lastObject becomeFirstResponder]; }
用于滚动离屏响应者的其他逻辑可能适用。
这种方法的另一个优点是你不需要inheritance你想要显示的所有types的控件(比如UITextField
),而是可以在控制器级别pipe理逻辑,在那里,老实说,是正确的地方所以。
我已经尝试了许多代码,最后,这在Swift 3.0中为我工作最新[2017年3月]
ViewController
类应该inheritanceUITextFieldDelegate
以使这个代码工作。
class ViewController: UIViewController,UITextFieldDelegate
添加带有正确标签号的文本字段,该标签号用于根据分配给它的增量标签号将控制权转移到适当的文本字段。
override func viewDidLoad() { userNameTextField.delegate = self userNameTextField.tag = 0 userNameTextField.returnKeyType = UIReturnKeyType.next passwordTextField.delegate = self passwordTextField.tag = 1 passwordTextField.returnKeyType = UIReturnKeyType.go }
在上面的代码中, returnKeyType = UIReturnKeyType.next
在哪里将使键盘返回键显示为Next
你也有其他的select,如Join/Go
等,根据您的应用程序更改值。
这个textFieldShouldReturn
是一个UITextFieldDelegate控制的方法,在这里我们有下一个字段select基于标签值增量
func textFieldShouldReturn(_ textField: UITextField) -> Bool { if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField { nextField.becomeFirstResponder() } else { textField.resignFirstResponder() return true; } return false }
我已经添加到PeyloW的答案,以防您想要实现上一个/下一个buttonfunction:
- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender { NSInteger nextTag; UITextView *currentTextField = [self.view findFirstResponderAndReturn]; if (currentTextField != nil) { // I assigned tags to the buttons. 0 represent prev & 1 represents next if (sender.tag == 0) { nextTag = currentTextField.tag - 1; } else if (sender.tag == 1) { nextTag = currentTextField.tag + 1; } } // Try to find next responder UIResponder* nextResponder = [self.view viewWithTag:nextTag]; if (nextResponder) { // Found next responder, so set it. // I added the resign here in case there's different keyboards in place. [currentTextField resignFirstResponder]; [nextResponder becomeFirstResponder]; } else { // Not found, so remove keyboard. [currentTextField resignFirstResponder]; } }
你在这里inheritanceUIView:
@implementation UIView (FindAndReturnFirstResponder) - (UITextView *)findFirstResponderAndReturn { for (UITextView *subView in self.subviews) { if (subView.isFirstResponder){ return subView; } } return nil; } @end
大家好,请看这个
- (void)nextPrevious:(id)sender { UIView *responder = [self.view findFirstResponder]; if (nil == responder || ![responder isKindOfClass:[GroupTextField class]]) { return; } switch([(UISegmentedControl *)sender selectedSegmentIndex]) { case 0: // previous if (nil != ((GroupTextField *)responder).previousControl) { [((GroupTextField *)responder).previousControl becomeFirstResponder]; DebugLog(@"currentControl: %i previousControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).previousControl.tag); } break; case 1: // next if (nil != ((GroupTextField *)responder).nextControl) { [((GroupTextField *)responder).nextControl becomeFirstResponder]; DebugLog(@"currentControl: %i nextControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).nextControl.tag); } break; } }
我尝试使用更复杂的方法来解决这个问题,这种方法基于在UITableView
为每个单元格(或UITextField
)分配一个可以稍后检索的唯一标记值: activate-next-uitextfield-in-uitableview-ios
我希望这有帮助!
处理这个东西GNTextFieldsCollectionManager时,我刚刚创build了新的Pod。 它会自动处理下一个/最后一个textField问题,非常容易使用:
[[GNTextFieldsCollectionManager alloc] initWithView:self.view];
抓取按视图层次结构(或标签)排列的所有文本字段,也可以指定自己的textField数组。
这在Xamarin.iOS / Monotouch中适用于我。 将键盘button更改为下一步,将控件传递到下一个UITextField并在最后一个UITextField后隐藏键盘。
private void SetShouldReturnDelegates(IEnumerable<UIView> subViewsToScout ) { foreach (var item in subViewsToScout.Where(item => item.GetType() == typeof (UITextField))) { (item as UITextField).ReturnKeyType = UIReturnKeyType.Next; (item as UITextField).ShouldReturn += (textField) => { nint nextTag = textField.Tag + 1; var nextResponder = textField.Superview.ViewWithTag(nextTag); if (null != nextResponder) nextResponder.BecomeFirstResponder(); else textField.Superview.EndEditing(true); //You could also use textField.ResignFirstResponder(); return false; // We do not want UITextField to insert line-breaks. }; } }
在ViewDidLoad中,你将拥有:
如果你的TextFields没有标签现在设置:
txtField1.Tag = 0; txtField2.Tag = 1; txtField3.Tag = 2; //...
和电话
SetShouldReturnDelegates(yourViewWithTxtFields.Subviews.ToList()); //If you are not sure of which view contains your fields you can also call it in a safer way: SetShouldReturnDelegates(txtField1.Superview.Subviews.ToList()); //You can also reuse the same method with different containerViews in case your UITextField are under different views.
我宁愿:
@interface MyViewController : UIViewController @property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields; @end
在NIB文件中,我将textField以所需顺序挂接到这个inputFields数组中。 之后,我对UITextField的索引做一个简单的testing,报告用户点击返回:
// for UITextField -(BOOL)textFieldShouldReturn:(UITextField*)textField { NSUInteger index = [_inputFields indexOfObject:textField]; index++; if (index < _inputFields.count) { UIView *v = [_inputFields objectAtIndex:index]; [v becomeFirstResponder]; } return NO; } // for UITextView -(BOOL)textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text { if ([@"\n" isEqualToString:text]) { NSUInteger index = [_inputFields indexOfObject:textView]; index++; if (index < _inputFields.count) { UIView *v = [_inputFields objectAtIndex:index]; [v becomeFirstResponder]; } else { [self.view endEditing:YES]; } return NO; } return YES; }
如果(cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; txt_Input = [[UITextField alloc] initWithFrame:CGRectMake(0,10,150,30)]; txt_Input.tag = indexPath.row + 1; [self.array_Textfields addObject:txt_Input]; //在ViewDidLoad中初始化可变数组 } - (BOOL)textFieldShouldReturn:(UITextField *)textField { int tag =(int)textField.tag; UITextField * txt = [self.array_Textfields objectAtIndex:tag]; [txt becomeFirstResponder]; 返回YES; }
我的故事板中有大约10多个UITextField,启用下一个function的方式是创build一个UITextField数组,并使下一个UITextField成为firstResponder。 这里是实现文件:
#import "RegistrationTableViewController.h" @interface RegistrationTableViewController () @property (weak, nonatomic) IBOutlet UITextField *fullNameTextField; @property (weak, nonatomic) IBOutlet UITextField *addressTextField; @property (weak, nonatomic) IBOutlet UITextField *address2TextField; @property (weak, nonatomic) IBOutlet UITextField *cityTextField; @property (weak, nonatomic) IBOutlet UITextField *zipCodeTextField; @property (weak, nonatomic) IBOutlet UITextField *urlTextField; @property (weak, nonatomic) IBOutlet UITextField *usernameTextField; @property (weak, nonatomic) IBOutlet UITextField *emailTextField; @property (weak, nonatomic) IBOutlet UITextField *passwordTextField; @property (weak, nonatomic) IBOutlet UITextField *confirmPWTextField; @end NSArray *uiTextFieldArray; @implementation RegistrationTableViewController - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"view did load"); uiTextFieldArray = @[self.fullNameTextField,self.addressTextField,self.address2TextField,self.cityTextField,self.zipCodeTextField,self.urlTextField,self.usernameTextField,self.emailTextField,self.passwordTextField,self.confirmPWTextField]; for(UITextField *myField in uiTextFieldArray){ myField.delegate = self; } } -(BOOL)textFieldShouldReturn:(UITextField *)textField{ long index = [uiTextFieldArray indexOfObject:textField]; NSLog(@"%ld",index); if(index < (uiTextFieldArray.count - 1)){ [uiTextFieldArray[++index] becomeFirstResponder]; }else{ [uiTextFieldArray[index] resignFirstResponder]; } return YES; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
这是一个简单的解决scheme,没有标签使用,没有故事板技巧…
只要使用这个扩展:
extension UITextField{ func nextTextFieldField() -> UITextField?{ //field to return var returnField : UITextField? if self.superview != nil{ //for each view in superview for (_, view) in self.superview!.subviews.enumerate(){ //if subview is a text's field if view.isKindOfClass(UITextField){ //cast curent view as text field let currentTextField = view as! UITextField //if text field is after the current one if currentTextField.frame.origin.y > self.frame.origin.y{ //if there is no text field to return already if returnField == nil { //set as default return returnField = currentTextField } //else if this this less far than the other else if currentTextField.frame.origin.y < returnField!.frame.origin.y{ //this is the field to return returnField = currentTextField } } } } } //end of the mdethod return returnField } }
And call it like this (for example) with your textfield delegate:
func textFieldShouldReturn(textField: UITextField) -> Bool { textField.resignFirstResponder() textField.nextTextFieldField()?.becomeFirstResponder() return true }
in textFieldShouldReturn you should check that the textfield you are currently on is not the last one when they click next and if its n ot dont dismiss the keyboard..
This is an old post, but has a high page rank so I'll chime in with my solution.
I had a similar issue and ended up creating a subclass of UIToolbar
to manage the next/previous/done functionality in a dynamic tableView with sections: https://github.com/jday001/DataEntryToolbar
You set the toolbar as inputAccessoryView of your text fields and add them to its dictionary. This allows you to cycle through them forwards and backwards, even with dynamic content. There are delegate methods if you want to trigger your own functionality when textField navigation happens, but you don't have to deal with managing any tags or first responder status.
There are code snippets & an example app at the GitHub link to help with the implementation details. You will need your own data model to keep track of the values inside the fields.
Without usings tags and without adding a property for nextField/nextTextField, you can try this to emulate TAB, where "testInput" is your current active field:
if ([textInput isFirstResponder]) [textInput.superview.subviews enumerateObjectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([textInput.superview.subviews indexOfObject:textInput]+1, [textInput.superview.subviews count]-[textInput.superview.subviews indexOfObject:textInput]-1)] options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) { *stop = !obj.hidden && [obj becomeFirstResponder]; }]; if ([textInput isFirstResponder]) [textInput.superview.subviews enumerateObjectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [textInput.superview.subviews indexOfObject:textInput])] options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) { *stop = !obj.hidden && [obj becomeFirstResponder]; }];
I've been using Michael G. Emmons' answer for about a year now, works great. I did notice recently that calling resignFirstResponder and then becomeFirstResponder immediately can cause the keyboard to "glitch", disappearing and then appearing immediately. I changed his version slightly to skip the resignFirstResponder if the nextField is available.
- (BOOL)textFieldShouldReturn:(UITextField *)textField { if ([textField isKindOfClass:[NRTextField class]]) { NRTextField *nText = (NRTextField*)textField; if ([nText nextField] != nil){ dispatch_async(dispatch_get_main_queue(), ^ { [[nText nextField] becomeFirstResponder]; }); } else{ [textField resignFirstResponder]; } } else{ [textField resignFirstResponder]; } 返回true; }
you can use IQKeyboardManager library to do this. it handle every thing, you don't need any additional setup.IQKeyboardManager is available through CocoaPods, to install it simply add the following line to your Podfile:
pod 'IQKeyboardManager'
or Just drag and drop IQKeyBoardManager directory from demo project to your project. 而已。 you can find IQKeyBoardManager directory from https://github.com/hackiftekhar/IQKeyboardManager
Here is a Swift 3 version of Anth0 's answer. I'm posting it here to help any swift developers in wanting to take advantage of his great answer! I took the liberty of adding a return key type of "Next" when you set the associated object.
extension UITextField { @nonobjc static var NextHashKey: UniChar = 0 var nextTextField: UITextField? { get { return objc_getAssociatedObject(self, &UITextField.NextHashKey) as? UITextField } set(next) { self.returnKeyType = UIReturnKeyType.next objc_setAssociatedObject(self, &UITextField.NextHashKey,next,.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
Here is another extension that shows a possibility of using the above code to cycle through a list of UITextFields.
extension UIViewController: UITextFieldDelegate { public func textFieldShouldReturn(_ textField: UITextField) -> Bool { guard let next = textField.nextTextField else { textField.resignFirstResponder() return true } next.becomeFirstResponder() return false } }
And then in your ViewController or wherever, you can setup your textfields like so…
@IBOutlet fileprivate weak var textfield1: UITextField! @IBOutlet fileprivate weak var textfield2: UITextField! @IBOutlet fileprivate weak var textfield3: UITextField! ... [textfield1, textfield2, textfield3].forEach{ $0?.delegate = self } textfield1.nextTextField = textfield2 textfield2.nextTextField = textfield3 // We don't assign a nextTextField to textfield3 because we want // textfield3 to be the last one and resignFirstResponder when // the return button on the soft keyboard is tapped.
Solution in Swift 3.1, After connecting your textfields IBOutlets set your textfields delegate in viewDidLoad, And then navigate your action in textFieldShouldReturn
class YourViewController: UIViewController,UITextFieldDelegate { @IBOutlet weak var passwordTextField: UITextField! @IBOutlet weak var phoneTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() self.passwordTextField.delegate = self self.phoneTextField.delegate = self // Set your return type self.phoneTextField.returnKeyType = .next self.passwordTextField.returnKeyType = .done } func textFieldShouldReturn(_ textField: UITextField) -> Bool{ if textField == self.phoneTextField { self.passwordTextField.becomeFirstResponder() }else if textField == self.passwordTextField{ // Call login api self.login() } return true } }