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

时间:2009-08-28 15:34:40

标签: ios objective-c iphone

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

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

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

我实现了textFieldShouldReturn操作,但现在Next和Done Buttons关闭了键盘。

34 个答案:

答案 0 :(得分:565)

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

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

  1. 所有“tabbable”UITextField都位于同一父视图中。
  2. 他们的“tab-order”由tag属性定义。
  3. 假设你可以覆盖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 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!
    

答案 1 :(得分:169)

有一个很多更优雅的解决方案,在我第一次看到它时就把我吹走了。优点:

  • 更接近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中,更改您的UITextField以使用SOTextField类。接下来,同样在IB中,将每个'SOTextFields'的委托设置为'文件所有者'(这是您放置委托方法的代码的位置 - textFieldShouldReturn)。这种设计的优点在于,现在您只需右键单击任何textField,并将nextField出口分配给您希望成为下一个响应者的下一个SOTextField对象。

Assigning nextField in IB

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

如果分配了nextField,可以很容易地将其扩展为自动将returnKeyType SOTextField分配给UIReturnKeyNext - 手动配置少一件事。

答案 2 :(得分: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的链接列表,每个人都知道该行的下一个是谁。

希望它会有所帮助。

答案 3 :(得分:78)

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

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(尽管它不像上面的程序化解决方案那样优雅,所以IMO使用上面代码viewDidLoad)。

答案 4 :(得分:45)

快速扩展,适用于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])

扩展程序会将返回按钮设置为&#34;下一步&#34;除了最后一个领域之外的其他所有领域和#34;完成&#34;对于最后一个字段,并在点击键盘时移动焦点/关闭键盘。

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

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 :(得分:25)

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

您需要做的就是

  1. UITextField的班级类型设为NextResponderTextField enter image description here
  2. 然后将nextResponderField的出口设置为指向下一个响应者,它可以是任何UITextField或任何UIResponder子类。它也可以是一个UIButton,并且该库足够聪明,只有在启用时才能触发按钮的TouchUpInside事件。 enter image description here enter image description here
  3. 这是正在运行的库:

    enter image description here

答案 6 :(得分: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;
}
  • 我总是返回NO,因为我不想插入换行符。我以为我会指出这一点,因为当我返回YES时它将自动退出后续字段或在我的TextView中插入换行符。我花了一点时间才弄明白。
  • activeField会跟踪活动字段,以防需要滚动才能从键盘中取消显示字段。如果您有类似的代码,请确保在更改第一个响应者之前分配activeField。立即更改第一响应者并立即触发KeyboardWasShown事件。

答案 7 :(得分:11)

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

来源和示例应用:GitHub repository for UIControlsWithTabbing

用法:

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

Assigning 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

答案 8 :(得分:10)

我尝试了很多代码,最后,这对我来说非常适合 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将使键盘返回键显示为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
 }

答案 9 :(得分:9)

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

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

答案 10 :(得分:9)

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

答案 11 :(得分:7)

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

在标题

中创建新的IBAction
- (IBAction)textFieldDoneEditing:(id)sender;

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

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

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

答案 12 :(得分: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];
}

可以预先显示用于滚动屏幕外响应者的其他逻辑。

这种方法的另一个优点是你不需要子类化你可能想要显示的各种控件(比如UITextField s),但可以改为在控制器级别管理逻辑,让我们诚实,正确的地方这样做。

答案 13 :(得分: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;
}

答案 14 :(得分:4)

我尝试使用更复杂的方法解决此问题,该方法基于在UITextField中为每个单元格(或UITableView)分配一个可以在以后检索的唯一标记值: activate-next-uitextfield-in-uitableview-ios

我希望这有帮助!

答案 15 :(得分: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
        }

    }

答案 16 :(得分: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

答案 17 :(得分:4)

大家好,请参阅this one

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

答案 18 :(得分:4)

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

COUNT(DISTINCT (CASE WHEN vote.vote_type = 'upvote' then vote_id ELSE NULL END)) AS 'upvote', 
COUNT(DISTINCT (CASE WHEN vote.vote_type = 'downvote' then vote_id ELSE NULL END)) AS 'downvote',

抓取按视图层次结构(或标签)显示的所有文本字段,或者指定自己的textFields数组。

答案 19 :(得分:3)

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

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标记,您希望下一个/ prev按钮响应。

答案 20 :(得分:3)

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

只需使用此扩展程序:

    <plugin>
        <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
        <artifactId>sonar-packaging-maven-plugin</artifactId>
        <version>1.15</version>
        <extensions>true</extensions>
        <configuration>
                <pluginClass>your.sonar.plugin</pluginClass>
                <pluginDescription>My SonarQube plugin</pluginDescription>
                <basePlugin>java</basePlugin>
            </configuration>
        </plugin>

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

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
    }

}

答案 21 :(得分:3)

这在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 内,您将拥有:

如果您的TextField尚未设置标签:

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.

答案 22 :(得分:3)

我的故事板中有大约10多个UITextField,我启用下一个功能的方式是创建一个UITextField数组,并使下一个UITextField成为第一个响应者。这是实施文件:

#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

答案 23 :(得分:3)

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

答案 24 :(得分:3)

我更愿意:

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

在NIB文件中,我将textField以所需顺序挂钩到此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;
}

答案 25 :(得分:2)

您可以使用IQKeyboardManager库来执行此操作。它处理所有事情,你不需要任何额外的setup.IQKeyboardManager可以通过CocoaPods,安装它只需将以下行添加到你的Podfile:

pod 'IQKeyboardManager'

或 只需将IQKeyBoardManager目录从演示项目拖放到项目中即可。而已。 你可以从https://github.com/hackiftekhar/IQKeyboardManager

找到IQKeyBoardManager目录

答案 26 :(得分:2)

我一直在使用Michael G. Emmons的答案大约一年,效果很好。我最近注意到,调用resignFirstResponder然后立即调用FirstFirstResponder会导致键盘“出现故障”,消失然后立即出现。如果nextField可用,我稍微更改了他的版本以跳过resignFirstResponder。

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

}

答案 27 :(得分:2)

这是一篇旧帖子,但页面排名很高,所以我会使用我的解决方案。

我遇到了类似的问题,最后创建了一个UIToolbar的子类来管理动态tableView中的下一个/上一个/完成的功能,其中包含以下部分:https://github.com/jday001/DataEntryToolbar

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

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

答案 28 :(得分:2)

在textFieldShouldReturn中,你应该检查你当前所在的文本字段是不是最后一个,当他们点击下一个,如果它不会解除键盘..

答案 29 :(得分:2)

这是 Anth0 的答案的Swift 3版本。我在这里张贴它是为了帮助任何快速的开发者想要利用他的好答案!我冒昧地添加了一个返回键类型&#34; Next&#34;当您设置关联对象时。

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

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

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.

答案 30 :(得分: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];
                                                    }];

答案 31 :(得分:1)

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

  • 文本字段委托设置为视图控制器
  • 所有文本字段都是同一视图的子视图
  • 文本字段按照您要进行的顺序包含标记(例如,textField2.tag = 2,textField3.tag = 3等)
  • 当您点击键盘上的 return 按钮时,将会移动到下一个文本字段(您可以将其更改为 next done 等等。)
  • 您希望键盘在最后一个文本字段后解除

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

    }
}

答案 32 :(得分:0)

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

答案 33 :(得分:-1)

swift 4 for mxcl answer:

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