如何浏览文本字段(下一个/完成按钮)

phx*_*phx 480 iphone objective-c ios

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

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

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

我实现了textFieldShouldReturn操作,但现在Next和Done Buttons关闭了Keyboard.

Pey*_*loW 573

在Cocoa for Mac OS X中,您有下一个响应者链,您可以在其中询问文本字段接下来应该关注哪个控件.这就是使文本字段之间的标签工作的原因.但由于iOS设备没有键盘,只有触摸,这个概念还没有幸免于过渡到Cocoa Touch.

无论如何,这可以轻松完成,有两个假设:

  1. 所有"tabbable" UITextField都在同一个父视图中.
  2. 他们的"tab-order"由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.
}
Run Code Online (Sandbox Code Playgroud)

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

Swift 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
}
Run Code Online (Sandbox Code Playgroud)

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

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
Run Code Online (Sandbox Code Playgroud)

  • 只是为了迂腐,我想在这篇文章中澄清一些错误的信息.在OS X上,Tab键顺序是通过NSView的setNextKeyView :(而不是setNextResponder :)方法设置的.响应者链与此分开:它是处理"非目标"动作的响应者对象的层次结构,例如发送到第一响应者而不是直接发送到控制器对象的动作.响应者链通常遵循视图层次结构,直到窗口及其控制器,然后是应用程序委托.谷歌"响应链"提供了大量信息. (30认同)
  • 不要忘记添加`UITextFieldDelegate`并设置textfields委托. (13认同)
  • 有时键盘顶部有"下一个"和"上一个"按钮.至少在浏览器中. (6认同)
  • 友好提醒从故事板设置标签选项(在我的例子中)。您必须按升序手动设置 TextFields 标记。(第一个是 0,第二个是 1 等)才能使该解决方案发挥作用。 (2认同)

mem*_*ons 170

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

  • 更接近OSX文本字段实现,其中文本字段知道下一个焦点应该去哪里
  • 不依赖于设置或使用标签 - 这对于此用例来说是IMO脆弱的
  • 可以扩展为使用UITextFieldUITextView控件 - 或任何键盘输入UI控件
  • 不会使用样板文件UITextField委托代码来混淆视图控制器
  • 与IB完美集成,可以通过熟悉的选项 - 拖放来配置连接插座.

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

@interface SOTextField : UITextField

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

@end
Run Code Online (Sandbox Code Playgroud)

这是实施:

@implementation SOTextField

@end
Run Code Online (Sandbox Code Playgroud)

在视图控制器中,您将创建-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;
}
Run Code Online (Sandbox Code Playgroud)

在IB中,更改您的UITextField以使用SOTextField该类.其次,也是在IB,设置每个"SOTextFields'to'文件的所有者’(你把代码的委托方法是正确的- textFieldShouldReturn)的代表.这种设计的优点在于,现在您只需右键单击任何textField,并将nextField出口分配给SOTextField您希望成为下一个响应者的下一个对象.

在IB中分配nextField

此外,你可以做很酷的事情,比如循环textFields,这样在最后一个失去焦点后,第一个会再次获得焦点.

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

  • 标签更容易,但这种方式更好,更适合良好的OO设计 (4认同)
  • 我非常喜欢使用标签进行IB集成 - 尤其是Xcode的新版本,它可以很好地将IB与IDE的其余部分集成在一起 - 而且不需要使用样板文本填充我的控制器textField委托代码是一个奖励. (3认同)
  • 对于较新版本的xcode和ios,是否有机会更新此答案? (2认同)

mxc*_*xcl 80

这是一个没有代表团的人:

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
Run Code Online (Sandbox Code Playgroud)

ObjC:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
Run Code Online (Sandbox Code Playgroud)

使用(主要是未知的)UIControlEventEditingDidEndOnExit UITextField动作.

您还可以轻松地将其连接到故事板中,因此不需要委托代码.

编辑:实际上我无法弄清楚如何在故事板中将其连接起来.becomeFirstResponder似乎不是这个控制事件的提议行动,这是一个遗憾.仍然,您可以将所有文本字段挂钩到ViewController中的单个操作,然后确定哪个textField becomeFirstResponder基于发件人(尽管它不像上面的编程解决方案那样优雅,因此IMO使用上面的代码执行viewDidLoad).

  • 非常简单有效的解决方案.链中的最后一个甚至可以触发例如`[tfN addTarget:self action:@selector(loginButtonTapped :) forControlEvents:UIControlEventEditingDidEndOnExit];`.谢谢@mxcl. (18认同)
  • 斯威夫特4:txtFirstname.addTarget(txtLastname,action:#selector(becomeFirstResponder),for:UIControlEvents.editingDidEndOnExit) (2认同)

Ant*_*th0 78

这是我解决这个问题的方法.

为了解决这个问题(因为我讨厌依靠标签做的东西),我决定自定义属性添加到UITextField对象.换句话说,我在UITextField上创建了一个类别,如下所示:

的UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end
Run Code Online (Sandbox Code Playgroud)

的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
Run Code Online (Sandbox Code Playgroud)

现在,这是我如何使用它:

UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield

textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;
Run Code Online (Sandbox Code Playgroud)

并实现textFieldShouldReturn方法:

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

    UITextField *next = theTextField.nextTextField;
    if (next) {
        [next becomeFirstResponder];
    } else {
        [theTextField resignFirstResponder];
    }

    return NO; 
}
Run Code Online (Sandbox Code Playgroud)

我现在有一个UITextField的链接列表,每个人都知道谁在下一行.

希望它会有所帮助.

  • 这是最好的解决方案,应该选择作为答案. (11认同)
  • 这个解决方案对我有用,只有我改变的是制作nextTextField和IBOutlet所以我可以在我的Storyboard中设置链接. (4认同)

Amo*_*hua 46

一个快速扩展,应用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

它易于使用:

UITextField.connectFields([field1, field2, field3])
Run Code Online (Sandbox Code Playgroud)

对于除最后一个字段之外的所有字段,扩展名将返回按钮设置为"下一个",对于最后一个字段,将"返回"设置为"完成",并在点击这些字段时切换焦点/关闭键盘.

斯威夫特<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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,如果你想让Done执行一个动作,你可以让你的View Controller符合`UITextFieldDelegate`,并实现`func textFieldDidEndEditing(textField:UITextField)`.如果`textField!= field3`,请尽早退出. (5认同)

moh*_*945 25

更一致和更健壮的方法是使用NextResponderTextField 您可以完全从界面构建器配置它,而无需设置委托或使用view.tag.

你需要做的就是

  1. 设置类类型的的UITextFieldNextResponderTextField 在此输入图像描述
  2. 然后设置nextResponderField指向下一个响应者的出口,它可以是UITextField任何子UIResponder类或任何子类.它也可以是一个UIButton,并且该库足够智能,TouchUpInside只有在启用时才触发按钮事件. 在此输入图像描述 在此输入图像描述

这是图书馆的行动:

在此输入图像描述


Mar*_*uee 14

我喜欢Anth0和Answerbot已经提出过的OO解决方案.但是,我正在开发一个快速小巧的POC,所以我不想让子类和类别混乱.

另一个简单的解决方案是创建一个NSArray字段,并在按下下一个字段时查找下一个字段.不是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;
}
Run Code Online (Sandbox Code Playgroud)
  • 我总是返回NO,因为我不想插入换行符.我以为我会指出这一点,因为当我返回YES时它将自动退出后续字段或在我的TextView中插入换行符.我花了一点时间才弄明白.
  • activeField会跟踪活动字段,以防需要滚动来从键盘中取消显示字段.如果您有类似的代码,请确保在更改第一个响应者之前分配activeField.立即更改第一响应者并立即触发KeyboardWasShown事件.


pic*_*ano 11

这是使用UIControl上的类别进行Tab键的实现.这个解决方案具有Michael和Anth0方法的所有优点,但适用于所有UIControl,而不仅仅是UITextFields.它还可以与Interface Builder和故事板无缝协作.

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

用法:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField transferFirstResponderToNextControl];
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

在Interface Builder中分配nextControl

标题:

//
// UIControl+NextControl.h
// UIControlsWithTabbing
//

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

@property (nonatomic, weak) IBOutlet UIControl *nextControl;

- (BOOL)transferFirstResponderToNextControl;

@end
Run Code Online (Sandbox Code Playgroud)

执行:

#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
Run Code Online (Sandbox Code Playgroud)


BHU*_*MAR 10

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

ViewController班应该继承了UITextFieldDelegate制作该代码工作.

class ViewController: UIViewController,UITextFieldDelegate  
Run Code Online (Sandbox Code Playgroud)

添加带有正确标记号的文本字段,此标记号用于根据分配给它的增量标记号将控件转移到适当的文本字段.

override func viewDidLoad() {
    userNameTextField.delegate = self
    userNameTextField.tag = 0
    userNameTextField.returnKeyType = UIReturnKeyType.next
    passwordTextField.delegate = self
    passwordTextField.tag = 1
    passwordTextField.returnKeyType = UIReturnKeyType.go
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,returnKeyType = UIReturnKeyType.nextwhere将使键盘返回键显示,因为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
 }
Run Code Online (Sandbox Code Playgroud)


Ken*_*ner 9

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

这实际上可能是一个棘手的问题,因为您通常还需要滚动屏幕或以其他方式调整文本字段的位置,以便在编辑时很容易看到.只需确保以不同的方式进出文本字段并进行大量测试,并提前离开(总是让用户选择关闭键盘而不是去下一个字段,通常使用"完成"导航栏)


iKu*_*hal 9

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


jcr*_*son 7

按下"完成"按钮时,解除键盘的一种非常简单的方法是:

在标题中创建一个新的IBAction

- (IBAction)textFieldDoneEditing:(id)sender;
Run Code Online (Sandbox Code Playgroud)

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

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
  [sender resignFirstResponder];
}
Run Code Online (Sandbox Code Playgroud)

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


Chr*_*orr 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];
}
Run Code Online (Sandbox Code Playgroud)

可以应用用于预先滚动屏幕外响应者的附加逻辑.

这种方法的另一个好处是,你并不需要继承各种控件,你可能想要显示(如UITextFieldS),但在控制器级别,其中,说实话可以代替管理逻辑,是在正确的地方这样做.


小智 6

首先在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;
}
Run Code Online (Sandbox Code Playgroud)


Kri*_*kur 5

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
        }

    }
Run Code Online (Sandbox Code Playgroud)


Moh*_*diq 5

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

在此处输入图片说明

这是我如何实施这个

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

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
}
Run Code Online (Sandbox Code Playgroud)

使用以下函数来处理水龙头

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
}
Run Code Online (Sandbox Code Playgroud)

您需要按顺序提供 textFields 标签,您希望下一个/上一个按钮响应。