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

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

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

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


当前回答

我已经实现了在视图层次结构中通过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];
    }
}

其他回答

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

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

UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

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

希望能有所帮助。

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

我希望这能有所帮助!

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

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

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

一个快速扩展,应用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])

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

Swift < 2.3

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for var i = 0; i < fields.count - 1; i += 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
    }
}

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

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