如何在文本字段中导航(“下一步” /“完成”按钮)


487

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

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

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

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


请在下面查看我的答案。以我的经验,这是比通常给出的答案更好,更完整的解决方案。我不知道为什么有人会给它负面的评价。
2011年

我也有类似的问题,但是对于Cordova,Phonegap,有什么方法可以解决?

Answers:


578

在Mac OS X的Cocoa中,您具有下一个响应者链,您可以在其中询问文本字段接下来应关注的控件。这就是使文本字段之间的制表符起作用的原因。但是由于iOS设备没有键盘,只有触摸,所以这个概念在过渡到Cocoa Touch时还没有幸免。

无论如何,可以通过两个假设轻松完成此操作:

  1. 全部“可租用” UITextField都在同一父视图上。
  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
}

如果文本字段的超级视图为UITableViewCell,则下一个响应者为

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

6
有时,键盘上方有“下一个”和“上一个”按钮。至少在浏览器中。
TimBüthe10年

31
只是为了学究,我想清除这篇文章中的一些错误信息。在OS X上,通过NSView的setNextKeyView :(不是setNextResponder :)方法设置选项卡顺序。响应者链与此不同:它是响应者对象的层次结构,用于处理“非目标”操作,例如,发送给第一个响应者而不是直接发送给控制器对象的操作。响应者链通常遵循视图层次结构,直至窗口及其控制器,然后是应用程序委托。Google的“响应者链”提供了大量信息。
davehayden 2012年

文本字段的超级视图将为UITableViewCell。您将需要访问关联的内容UITableView并获取包含您要查找的文本字段的单元格。
Michael Mior 2012年

13
不要忘记添加UITextFieldDelegate和设置文本字段委托。
2014年

1
友好提醒,请从情节提要中设置TAG选项(在我的情况下)。您必须按升序手动设置TextFields标记。(第一个是0,第二个是1,依此类推),此解决方案才能起作用。
约阿基姆(Joakim)

170

还有一个很多更优雅的解决方案,它把我吓倒了,我第一次看到它。优点:

  • 更接近于OSX文本字段实现,其中文本字段知道下一步应该将焦点移到哪里
  • 不依赖于设置或使用标签-在这种情况下IMO脆弱
  • 可以扩展为同时使用UITextFieldUITextView控件-或任何键盘输入UI控件
  • 使用样板UITextField委托代码不会使视图控制器混乱
  • 与IB很好地集成在一起,可以通过熟悉的option-drag-drop进行配置以连接插座。

创建一个UITextField子类,该子类具有IBOutlet名为nextField 的属性。这是标题:

@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'的委托设置为'File's Owner'(在放置委托方法的代码-textFieldShouldReturn的正确位置)。这种设计的优点在于,现在您只需右键单击任何textField,并将nextField出口分配给SOTextField您想要成为下一个响应者的下一个对象。

在IB中分配nextField

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

这可以很容易地扩展到自动分配returnKeyTypeSOTextField一个UIReturnKeyNext,如果有一个nextField分配-少了一个东西手动配置。


3
我真的很喜欢IB集成而不是使用标签-尤其是与IB与其他IDE很好地集成了IB的较新版本的Xcode-并且不必用样板textField委托代码来使我的控制器变得混乱。
记忆

4
标签更容易,但是这种方法更好,并且更适合于良好的OO设计
Brain2000 2011年

1
我真的很喜欢这种解决方案,但是我在的单独的单元格视图中使用每个文本字段UITableView。尽管我IBOutlet在视图控制器上具有每个文本字段的nextField属性,但不能让我将属性链接到“文件所有者”上的那些属性。有什么建议可以使它在这种情况下工作吗?
杰伊·伊默曼

1
@JayImerman IB不允许您连接单元之间的插座。因此,在这种情况下,IB将一事无成。但是,您可以仅使用代码来连接nextField属性。textField1.nextField = textField2; textField2.nextField = textField3;
记忆卡

2
有机会针对新版本的xcode和ios更新此答案吗?
lostintranslation 2014年

82

这是一个没有授权的:

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)

对象:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];

使用(几乎未知)UIControlEventEditingDidEndOnExit UITextField操作进行工作。

您也可以轻松地将其连接到情节提要中,因此不需要委派代码。

编辑:实际上我不知道如何将其挂接到情节提要中。becomeFirstResponder对于此控制事件,似乎不是提供的操作,这很可惜。不过,您仍然可以将所有文本字段都挂接到ViewController中的单个动作上,然后再becomeFirstResponder根据发送者确定要发送的文本字段(尽管它不如上面的编程解决方案那么优雅,因此IMO使用中的上述代码进行了处理viewDidLoad)。


18
非常简单有效的解决方案。链中的最后一个甚至可以触发例如[tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];。谢谢@mxcl。
msrdjan 2014年

您将如何在情节提要中执行此操作?
Pedro Borges 2014年

这可能是最好的答案。
daspianist,2015年

即使在Swift 2中,这也对我非常有效。

2
迅捷4:txtFirstname.addTarget(txtLastname,操作:#selector(becomeFirstResponder),用于:UIControlEvents.editingDidEndOnExit)
realtimez

78

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

为了解决这个问题(并且因为我讨厌依靠标签来完成工作),我决定向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的链接列表,每个列表都知道该行中的下一个。

希望能对您有所帮助。


11
这是最好的解决方案,应选择答案。
乔瓦尼(Giovanni)

4
该解决方案对我有效,唯一改变的就是制作nextTextField和IBOutlet,以便可以在Storyboard中设置链接。
zachzurn

如果我可以保存喜欢的答案,那将是其中之一。感谢您的干净解决方案!
马特·贝克尔

1
请注意,如果您是IB,则还可以将这些属性设置为IBOutlets。。注意名称冲突-苹果最终可能会将其添加到UIResponder中。
贾斯珀·布鲁斯

该解决方案与界面生成器兼容吗?我的意思是,它可以使用IBOutlets和情节提要引用而不是通过编程设置nextTextField吗?IB似乎不允许您将UITextField类设置为类别。
Pedro Borges 2014年

46

一个快速扩展,使用mxcl的答案使其变得特别容易(适用于Traveler的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])

该扩展程序会将除最后一个字段以外的所有字段的返回按钮设置为“下一步”,对于最后一个字段将返回按钮设置为“完成”,并在点按这些键时移动焦点/关闭键盘。

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

5
顺便说一句,如果您想让Done执行某项操作,则只需使View Controller符合UITextFieldDelegate并实现即可func textFieldDidEndEditing(textField: UITextField)。如果早点回来textField != field3
瑞安

25

一种更一致,更健壮的方法是使用NextResponderTextField。 您可以完全从接口构建器对其进行配置,而无需设置委托或使用view.tag

您需要做的就是

  1. 将您的班级类型设置UITextFieldNextResponderTextField 在此处输入图片说明
  2. 然后将的出口设置nextResponderField为指向下一个响应者,它可以是UITextField任何子UIResponder类或任何子类。它也可以是UIButton,并且该库足够聪明,TouchUpInside只有在启用按钮后才能触发按钮事件。 在此处输入图片说明 在此处输入图片说明

这是运行中的库:

在此处输入图片说明


是。使用Swift 3.1(非常优雅的解决方案)可以很好地工作,这就是IB应该如何开箱即用地工作。
理查德

抱歉,我没收到问题。
mohamede1945

1
NextResponderTextField为我工作。它既快速又易于使用,只需一个快速文件,并且完全满足了我的需要。
Pat McG

这很棒!注意:我必须手动更改Return键,以使其像示例中一样显示“ Next”和“ Done”。
Mike Miller

14

我喜欢Anth0和Answerbot已经建议的OO解决方案。但是,我正在研究一个快速而小型的POC,所以我不想将子类和类别弄得一团糟。

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

这是我的代码(基于该线程的其他答案):

@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。更改第一响应者是立即的,并将立即触发KeyboardWasShown事件。

真好!简单,代码全部集中在一个模型中。AnthO的也很好。
Seamus

请记住,使用此代码会存在保留周期的风险。您的fieldArray会将所有IBOutlet中使用的弱引用转换为数组本身中的强引用。
Michael Long

11

这是在UIControl上使用类别进行制表的实现。此解决方案具有Michael和Anth0中方法的所有优点,但适用于所有UIControl,而不仅限于UITextFields。它还可以与Interface Builder和情节提要无缝地配合使用。

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

用法:

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

在Interface Builder中分配nextControl

标头:

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

1
有时,下一个窗口小部件不是控件,即UITextView,并且您可能还希望在其中进行制表。考虑将类型从UITextField更改为UIResponder。
Pedro Borges 2014年

1
效果很好,文档也很棒。我希望我几个小时前向下滚动到这个问题。:)
哈维尔·加的斯

10

我尝试了许多代码,最后,这在Swift 3.0 Latest [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键盘返回键在何处显示,因为Next您还有其他选择,例如Join/Go根据您的应用程序更改值。

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

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
 }

此方法有效,除非输入字段为UITextView。知道如何结合使用吗?
T9b

@ T9b您到底需要什么?您在应用中使用哪种类型的控件?
BHUVANESH MOHANKUMAR '17

有两种类型的文本输入,第一种是UITextField(输入单行文本),另一种是UITextView(输入多行文本)。UITextView显然不响应此代码,但可以形式使用。
T9b

是的,这部分代码仅适用于UITextField,您是否尝试过修改UITextView的事件?
BHUVANESH MOHANKUMAR '17

9

从一个文本字段退出后,调用[otherTextField成为FirstResponder],下一个字段将获得焦点。

实际上,这可能是一个棘手的问题,因为通常您还希望滚动屏幕或以其他方式调整文本字段的位置,以便在编辑时易于查看。只要确保对进入和离开文本字段的方式进行大量测试,并尽早离开(总是让用户可以选择关闭键盘,而不是转到下一个字段,通常使用“完成”导航栏)


9
 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}

您不应为此目的使用标签。
Christian Schnorr 2015年

7

按下“完成”按钮时,关闭键盘的一种非常简单的方法是:

在标题中创建一个新的IBAction

- (IBAction)textFieldDoneEditing:(id)sender;

在实现文件(.m文件)中,添加以下方法:

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

然后,当您将IBAction链接到文本字段时-链接到“退出时结束结束”事件。


1
不用客气,它不会在文本字段中导航,但是会关闭键盘并允许您选择其他字段。
jcrowson 2012年

7

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

7

我对这里有多少答案无法理解一个简单的概念感到惊讶:浏览应用程序中的控件并不是视图本身应该做的事情。是控制器决定要做出下一个第一响应者的控制权的工作。

同样,大多数答案仅适用于向前导航,但用户可能还想向后导航。

所以这就是我想出的。您的表单应由视图控制器管理,并且视图控制器是响应者链的一部分。因此,您可以完全自由地实现以下方法:

#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),但在控制器级别,其中,说实话可以代替管理逻辑,是在正确的地方这样做。


我找到了与您最相关的答案,但是我注意到一些有趣的事情,也许您可​​以解决。当使用不受UIViewController管理的UITextField时,按Tab键将默认使下一个UITextField成为第一响应者。但是,当每个都由VC管理时,情况并非如此。我的VC没有执行任何操作,但是内置的制表功能已消失。我注意到VC的keyCommands最终被添加到视图的响应者链中。这使我相信,如果您使用的是VC,则必须实施keyCommands。
乔伊·卡森

4

如果您要实现上一个/下一个按钮功能,我已经在PeyloW的答案中添加了:

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

4

大家好,请看这一个

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

代替UIView * responder,使用UIRespoder * responder更为准确
Pedro Borges


4

在处理GNTextFieldsCollectionManager时,我刚刚创建了新的Pod 。它会自动处理下一个/最后一个textField问题,并且非常易于使用:

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

获取所有出现在视图层次结构(或按标签)中的文本字段,或者您可以指定自己的textFields数组。


4

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
        }

    }

4

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

在此处输入图片说明

这是我实现此方法的方式

使用以下命令为要设置的每个文本字段添加附件视图

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标签,以使next / prev按钮响应。


3

我宁愿:

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

3
如果(cell == nil)
{
    单元格= [[UITableViewCell分配] initWithStyle:UITableViewCellStyleDefault重用标识符: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标签=(int)textField.tag;
    UITextField * txt = [self.array_Textfields objectAtIndex:tag];
    [txt成为FirstResponder];
    返回YES;
}

3

我的故事板上有大约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

3

这在Xamarin.iOS / Monotouch中为我工作。将键盘按钮更改为“下一步”,将控件传递给下一个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.

3

这是快速的简单解决方案,无需使用标签,也没有情节提要技巧...

只需使用此扩展名:

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
    }

}

并使用您的文本字段委托这样调用(例如):

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

关于此的一种评论是,您的方法假定所有字段都垂直堆叠在单个Superview中。它不允许分组字段,并且在文本字段可能并排时不起作用。如今,当人们使用大小类重新排列用于水平视图的表单以及将表单显示在平板电脑上时,后者尤为重要。
Michael Long

3

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

  • 文本字段委托设置为您的视图控制器
  • 所有文本字段都是同一视图的子视图
  • 文本字段具有要按顺序排列的标签(例如,textField2.tag = 2,textField3.tag = 3,依此类推)
  • 当您点击键盘上的返回按钮时,将移动到下一个文本字段(您可以将其更改为nextdone等)。
  • 您希望键盘在最后一个文本字段后消失

Swift 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

    }
}

2

在textFieldShouldReturn中,当他们单击下一步时,如果没有关闭键盘,则应检查当前使用的文本字段是否不是最后一个。


嘿,好的,这行得通,但是如何使用“下一步”按钮跳转到下一个文本字段?
phx

2

这是旧文章,但是页面排名很高,因此我将尽我所能。

我有一个类似的问题,最终创建了一个子类UIToolbar来管理带有部分的动态tableView中的next / previous / done功能:https : //github.com/jday001/DataEntryToolbar

您将工具栏设置为文本字段的inputAccessoryView并将其添加到其字典中。这样,即使是动态内容,您也可以前后循环浏览它们。如果您想在textField导航发生时触发自己的功能,则可以使用委托方法,但不必处理任何标签或第一响应者状态。

GitHub链接上有代码片段和示例应用程序,可帮助您实现详细信息。您将需要自己的数据模型来跟踪字段中的值。


2

在不使用标签且不为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];
                                                    }];

2

我一直在使用Michael G. Emmons的答案大约一年了,效果很好。最近我确实注意到,调用resignFirstResponder然后立即成为FirstResponder会导致键盘“毛刺”,消失并立即出现。如果nextField可用,我略微更改了其版本以跳过resignFirstResponder。

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

    如果([textField isKindOfClass:[NRTextField class]])
    {
        NRTextField * nText =(NRTextField *)textField;
        如果([nText nextField]!= nil){
            dispatch_async(dispatch_get_main_queue(),
                           ^ {[[nText nextField]成为FirstResponder]; });

        }
        其他{
            [textField resignFirstResponder];
        }
    }
    其他{
        [textField resignFirstResponder];
    }

    返回true;

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.