如何在文本框中导航(下一步/完成按钮)

我如何通过iPhone键盘上的“下一步”按钮浏览所有的文本字段?

最后一个文本字段应该关闭键盘。

我已经设置了IB按钮(下一步/完成),但现在我卡住了。

我实现了textFieldShouldReturn动作,但现在下一步和完成按钮关闭键盘。

219854 次浏览

在textFieldShouldReturn你应该检查你目前的文本域不是最后一个,当他们点击下一步,如果不是不要解散键盘..

退出一个文本字段后,调用[otherTextField becomeFirstResponder],下一个字段获得焦点。

这实际上是一个棘手的问题,因为通常你还想滚动屏幕或以其他方式调整文本字段的位置,以便在编辑时容易看到。只要确保做大量的测试,以不同的方式进入和退出文本字段,并提前离开(总是给用户一个选项,取消键盘,而不是进入下一个字段,通常在导航栏中有“完成”)

在Mac OS X的Cocoa中,你有下一个响应器链,在那里你可以询问文本字段下一个控件应该有焦点。这就是在文本字段之间进行标签操作的原因。但由于iOS设备没有键盘,只有触摸,所以这一概念没有在Cocoa touch的过渡中幸存下来。

这很容易做到,只要有两个假设:

  1. 所有“tabbable”__abc0都在同一个父视图上。
  2. 它们的“制表符顺序”由tag属性定义。

假设你可以重写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.
}

添加更多的代码,也可以忽略这些假设。

斯威夫特4.0

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
// Try to find next responder
let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!


if nextResponder != nil {
// Found next responder, so set it
nextResponder?.becomeFirstResponder()
} else {
// Not found, so remove keyboard
textField.resignFirstResponder()
}


return false
}

如果文本字段的superview是一个UITableViewCell那么下一个responder将是

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!

大家好,请看这一个

- (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;
}
}

有一个更优雅的解决方案,让我第一次看到它的时候就大吃一惊。好处:

  • 更接近OSX文本框的实现,其中一个文本框知道下一步的重点应该去哪里
  • 不依赖于设置或使用标签-这是,IMO脆弱的用例
  • 可以扩展到工作与UITextFieldUITextView控件-或任何键盘输入UI控件
  • 不会让你的视图控制器与样板UITextField委托代码混淆
  • 很好地与IB集成,并可以通过熟悉的选项-拖放连接插座进行配置。

创建一个UITextField子类,它有一个名为nextField的IBOutlet属性。下面是标题:

@interface SOTextField : UITextField


@property (weak, nonatomic) IBOutlet UITextField *nextField;


@end

这是它的实现:

@implementation SOTextField


@end

在你的视图控制器中,你将创建-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)。这种设计的美妙之处在于,现在你可以简单地右键单击任何textField并将nextField出口分配给你想要作为下一个响应器的下一个SOTextField对象。

在IB中分配nextField

此外,您还可以做一些很酷的事情,如循环textFields,以便在最后一个失去焦点后,第一个将再次获得焦点。

这可以很容易地扩展为自动将SOTextFieldreturnKeyType分配给UIReturnKeyNext(如果分配了nextField)——手动配置的事情少了一件。

这是我对这个问题的解决方案。

为了解决这个问题(因为我讨厌依赖标签来做事情),我决定向UITextField对象添加一个自定义属性。换句话说,我在UITextField上创建了一个这样的类别:

< >强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的链表,每一个都知道下一个是谁。

希望能有所帮助。

没有usings标签,也没有为nextField/nextTextField添加属性,你可以尝试模拟TAB,其中"testInput"是你当前的活动字段:

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];
}];

当按下“Done”按钮时,一个非常简单的方法是:

在头文件中创建一个新的IBAction

- (IBAction)textFieldDoneEditing:(id)sender;

在实现文件(。M文件)添加如下方法:

- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}

然后,当你来链接IBAction到文本框-链接到'Did End On Exit'事件。

我喜欢Anth0和Answerbot已经提出的面向对象解决方案。然而,我正在开发一个快速而小型的POC,所以我不想让子类和类别使事情变得混乱。

另一个简单的解决方案是创建一个字段的NSArray,并在按下next时查找下一个字段。不是面向对象的解决方案,而是快速、简单且易于实现。此外,您可以一目了然地查看和修改排序。

下面是我的代码(基于这个线程中的其他答案):

@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跟踪活动字段,以防需要滚动来从键盘上显示字段。如果您有类似的代码,请确保在更改第一个响应器之前分配了activeField。更改第一响应器是立即的,并将立即触发keyboardwasshows事件。

我已经添加到PeyloW的答案,以防你想实现一个previous/next按钮功能:

- (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];


}
}

你像这样子类化UIView:

@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
for (UITextView *subView in self.subviews) {
if (subView.isFirstResponder){
return subView;
}
}
return nil;
}
@end
 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}
我尝试使用一种更复杂的方法来解决这个问题,该方法基于为UITableView中的每个单元格(或UITextField)分配一个稍后可以检索的唯一标记值: activate-next-uitextfield-in-uitableview-ios < / p >

我希望这能有所帮助!

我更喜欢:

@interface MyViewController : UIViewController
@property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields;
@end

在NIB文件中,我以所需的顺序将textFields钩子到这个inputFields数组中。之后,我做了一个简单的UITextField的索引测试,报告用户点击返回:

// 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;
}

首先在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;
}

下面是一个没有授权的例子:

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似乎不是这个控制事件的提供动作,这是一个遗憾。尽管如此,你可以将所有的文本字段连接到你的ViewController中的一个动作,然后根据发送方确定哪个textField到becomeFirstResponder(尽管它不像上面的编程解决方案那么优雅,所以我用viewDidLoad中的上述代码来做)。

下面是一个在UIControl上使用类别的选项卡实现。这个解决方案具有Michael和Anth0方法的所有优点,但适用于所有uicontrol,而不仅仅是__abc0。它还可以与接口生成器和故事板无缝工作。

源代码和示例应用程序:UIControlsWithTabbing的GitHub存储库

用法:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField transferFirstResponderToNextControl];
return NO;
}

 assign nextControl in Interface Builder

标题:

//
// 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
if (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]; // Initialize mutable array in ViewDidLoad
}


-(BOOL)textFieldShouldReturn:(UITextField *)textField
{


int tag = ( int) textField.tag ;
UITextField * txt = [  self.array_Textfields objectAtIndex:tag ] ;
[ txt becomeFirstResponder] ;
return YES ;
}

我在我的故事板中有大约10+ UITextField,我启用下一个功能的方式是通过创建一个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

这是一个老帖子,但有一个很高的页面排名,所以我将附和我的解决方案。

我有一个类似的问题,最终创建了UIToolbar的子类来管理动态表视图中的下一个/前一个/完成功能:https://github.com/jday001/DataEntryToolbar

您将工具栏设置为文本字段的inputAccessoryView,并将它们添加到它的字典中。这允许您向前和向后循环浏览它们,即使是动态内容。有委托方法,如果你想触发你自己的功能时textField导航发生,但你不必处理管理任何标签或第一个响应器状态。

这里有代码片段&在GitHub链接上提供了一个示例应用程序,以帮助了解实现细节。您需要自己的数据模型来跟踪字段中的值。

我用Michael G. Emmons的答案已经有一年了,效果很好。我最近注意到,立即调用resignFirstResponder,然后立即调用becomeFirstResponder会导致键盘“故障”,消失,然后立即出现。我稍微改变了他的版本,跳过resignFirstResponder如果nextField是可用的。

- (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];
}


return true;


}

我很惊讶,这里有这么多答案没有理解一个简单的概念:在应用程序中的控件中导航不是视图本身应该做的事情。控制器的工作是决定将哪个控件作为下一个第一响应器。

此外,大多数答案只适用于前进导航,但用户也可能想后退。

这就是我想到的。表单应该由视图控制器管理,视图控制器是响应器链的一部分。所以你可以完全自由地实现以下方法:

#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];
}

额外的逻辑滚动屏幕外的响应可见之前可能适用。

这种方法的另一个优点是,你不需要子类化你可能想要显示的所有类型的控件(比如UITextFields),而是可以在控制器级别管理逻辑,让我们诚实地说,是正确的地方来做到这一点。

你可以使用IQKeyboardManager库来做到这一点。它处理所有的事情,你不需要任何额外的设置。IQKeyboardManager可以通过CocoaPods获得,要安装它,只需在Podfile中添加以下行:

pod 'IQKeyboardManager'
< p >或 只需拖放IQKeyBoardManager目录从演示项目到您的项目。就是这样。 你可以从https://github.com/hackiftekhar/IQKeyboardManager

找到IQKeyBoardManager目录

一个更一致和健壮的方法是使用< A href="https://github.com/mohamede1945/NextResponderTextField" rel="noreferrer" title="NextResponderTextField">NextResponderTextField 你可以完全从接口构建器中配置它,而不需要设置委托或使用view.tag

你所需要做的就是

  1. 设置你的UITextField的类类型为NextResponderTextField enter image description here
  2. 然后将nextResponderField的出口设置为指向下一个响应器,它可以是任何UITextField或任何UIResponder子类。它也可以是一个UIButton,并且库足够智能,只有当它被启用时才会触发按钮的TouchUpInside事件。 enter image description here enter image description here

下面是运行中的库:

enter image description here

我刚刚创建了新的Pod处理这个东西GNTextFieldsCollectionManager。它自动处理下一个/最后一个textField问题,非常容易使用:

[[GNTextFieldsCollectionManager alloc] initWithView:self.view];

抓取所有的文本字段排序出现在视图层次结构(或标签),或者你可以指定自己的文本字段数组。

这对我在Xamarin很有效。iOS / Monotouch。 将键盘按钮更改为Next,将控件传递给下一个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.

一个快速扩展,应用mxcl的答案,使这特别容易(适应swift 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])

扩展将设置返回按钮为“下一步”为所有但最后一个字段和“完成”为最后一个字段,并转移焦点/解散键盘时,这些被轻敲。

斯威夫特& lt;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)
}
}

< >强劲迅速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)
}
}

这是一个简单的解决方案在swift,没有标签使用,没有故事板技巧…

只需使用这个扩展:

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
}


}

并调用它像这样(例如)与你的textfield委托:

func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
textField.nextTextFieldField()?.becomeFirstResponder()
return true
}

下面是Anth0的答案的Swift 3版本。我把它贴在这里,以帮助任何想要利用他的伟大答案的敏捷开发人员!当您设置关联对象时,我擅自添加了一个返回键类型“Next”。

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)
}
}
}

下面是另一个扩展,它显示了使用上述代码循环遍历UITextFields列表的可能性。

extension UIViewController: UITextFieldDelegate {
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let next = textField.nextTextField else {
textField.resignFirstResponder()
return true
}


next.becomeFirstResponder()
return false
}
}

然后在你的ViewController或者其他地方,你可以像这样设置你的文本框。

@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.

我已经尝试了许多代码,最后,这在Swift 3.0最新版本[2017年3月]中为我工作

ViewController类应该继承UITextFieldDelegate以使此代码正常工作。

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将使Key pad的返回键显示为Next,您也有其他选项如Join/Go等,根据您的应用程序更改值。

这个textFieldShouldReturn是一个UITextFieldDelegate控制的方法,这里我们有基于标签值增量的下一个字段选择

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
}

解决方案在Swift 3.1,连接你的文本字段IBOutlets设置你的文本字段委托在viewDidLoad,然后在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
}


}

如果有人想这样。我认为这是最接近问题所要求的要求

enter image description here

下面是我如何实现它

为需要设置的每个文本字段添加附件视图,使用

func setAccessoryViewFor(textField : UITextField)    {
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.sizeToFit()


// Adds the buttons


// Add previousButton
let prevButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(previousPressed(sender:)))
prevButton.tag = textField.tag
if getPreviousResponderFor(tag: textField.tag) == nil {
prevButton.isEnabled = false
}


// Add nextButton
let nextButton = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(nextPressed(sender:)))
nextButton.tag = textField.tag
if getNextResponderFor(tag: textField.tag) == nil {
nextButton.title = "Done"
}


let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.setItems([prevButton,spaceButton,nextButton], animated: false)
toolBar.isUserInteractionEnabled = true
textField.inputAccessoryView = toolBar
}

使用以下函数来处理轻拍

func nextPressed(sender : UIBarButtonItem) {
if let nextResponder = getNextResponderFor(tag: sender.tag) {
nextResponder.becomeFirstResponder()
} else {
self.view.endEditing(true)
}


}


func previousPressed(sender : UIBarButtonItem) {
if let previousResponder = getPreviousResponderFor(tag : sender.tag)  {
previousResponder.becomeFirstResponder()
}
}


func getNextResponderFor(tag : Int) -> UITextField? {
return self.view.viewWithTag(tag + 1) as? UITextField
}


func getPreviousResponderFor(tag : Int) -> UITextField? {
return self.view.viewWithTag(tag - 1) as? UITextField
}

您需要按您希望下一个/前一按钮响应的顺序给出textFields标记。

Swift 3解决方案,使用UITextField's的有序数组

func nextTextField() {
let textFields = // Your textfields array


for i in 0 ..< textFields.count{
if let textfield = textFields[i], textfield.isFirstResponder{
textfield.resignFirstResponder()
if i+1 < textFields.count, let nextextfield = textFields[i+1]{
nextextfield.becomeFirstResponder()
return
}
}
}
}

Swift 4 for mxcl答案:

txtFirstname.addTarget(txtLastname, action:
#selector(becomeFirstResponder), for: UIControlEvents.editingDidEndOnExit)

一种更安全、更直接的方式,假设:

  • 文本字段委托被设置为你的视图控制器
  • 所有文本字段都是同一视图的子视图
  • 文本字段的标签按照你想要进行的顺序(例如,textField2. txt)。标签= 2,textField3。Tag = 3,等等)
  • 移动到下一个文本字段将发生在你点击键盘上的返回按钮时(你可以将此更改为下一个完成等)。
  • 您希望键盘在最后一个文本字段之后被取消

斯威夫特4.1:

extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
guard let nextTextField = textField.superview?.viewWithTag(nextTag) else {
textField.resignFirstResponder()
return false
}


nextTextField.becomeFirstResponder()


return false


}
}

我已经实现了在视图层次结构中通过Responders导航的动态解决方案。其思想是,在整个层次结构中搜索下一个可能的响应器,根据需要向前或向后搜索。

UIView搜索扩展:

UIView + Cat.h

- (UIResponder *)getNextFirstResponder;
- (UIResponder *)getPreviousFirstResponder;

UIView + Cat.m

- (UIResponder *)getPreviousFirstResponder {
if (!self.superview) {
return nil;
}
    

BOOL hasreachedself = NO;
for (NSInteger i = self.superview.subviews.count-1; i >= 0; i--) {
UIView *v = [self.superview.subviews objectAtIndex:i];
if (v == self) {
hasreachedself = YES;
continue;
}
if (!hasreachedself) continue;
        

if ([v canBecomeFirstResponder] && !v.hidden) {
return v;
}
UIResponder *subResponder = [self getNextFirstResponderInView:v];
if (subResponder) {
return subResponder;
}
}
    

//search hierachicaly in superviews
return [self.superview getPreviousFirstResponder];
}


- (UIResponder *)getNextFirstResponder {
if (!self.superview) {
return nil;
}
    

BOOL hasreachedself = NO;
for (UIView *v in self.superview.subviews) {
if (v == self) {
hasreachedself = YES;
continue;
}
if (!hasreachedself) continue;
        

if ([v canBecomeFirstResponder] && !v.hidden) {
return v;
}
UIResponder *subResponder = [self getNextFirstResponderInView:v];
if (subResponder) {
return subResponder;
}
}
    

//search hierachicaly in superviews
return [self.superview getNextFirstResponder];
}


- (UIResponder *)getNextFirstResponderInView:(UIView *)view {
if ([view canBecomeFirstResponder] && !view.hidden) {
return view;
}
for (UIView *v in view.subviews) {
UIResponder *subResponder = [self getNextFirstResponderInView:v];
if (subResponder) {
return subResponder;
}
}
return nil;
}

用法:添加UIToolbar到UITextField / UITextView(在类别或子类中):

- (void)addToolbarInputAccessoryView {
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectZero];
UIBarButtonItem *prev = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.up"] style:UIBarButtonItemStylePlain target:self action:@selector(moveToPreviousFirstResponder)];
UIBarButtonItem *next = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.down"] style:UIBarButtonItemStylePlain target:self action:@selector(moveToNextFirstResponder)];
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(hideKeyboard)];
toolbar.items = @[prev, next, space, done];
    

if ([[UITextField appearance] keyboardAppearance] == UIKeyboardAppearanceDark) {
toolbar.barStyle = UIBarStyleBlack;
toolbar.translucent = YES;
[toolbar setBarTintColor:[UIColor blackColor]];
[toolbar setTintColor:[UIColor whiteColor]];
}
    

[toolbar sizeToFit];
    

self.inputAccessoryView = toolbar;
}


- (void)hideKeyboard {
[self resignFirstResponder];
}


- (void)moveToNextFirstResponder {
UIResponder *next = [self getNextFirstResponder];
if (next) {
[next becomeFirstResponder];
}
else {
[self resignFirstResponder];
}
}


- (void)moveToPreviousFirstResponder {
UIResponder *prev = [self getPreviousFirstResponder];
if (prev) {
[prev becomeFirstResponder];
}
else {
[self resignFirstResponder];
}
}