如何使用iPhone键盘上的"下一步"按钮浏览所有文本字段?
最后一个文本字段应该关闭键盘.
我已经设置了IB按钮(下一个/完成),但现在我被卡住了.
我实现了textFieldShouldReturn操作,但现在Next和Done Buttons关闭了Keyboard.
在Cocoa for Mac OS X中,您有下一个响应者链,您可以在其中询问文本字段接下来应该关注哪个控件.这就是使文本字段之间的标签工作的原因.但由于iOS设备没有键盘,只有触摸,这个概念还没有幸免于过渡到Cocoa Touch.
无论如何,这可以轻松完成,有两个假设:
所有"tabbable" UITextField
都在同一个父视图中.
他们的"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. }
添加更多代码,也可以忽略这些假设.
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!
还有一个很多更优雅的解决方案,它把我吓倒了,我第一次看到它.优点:
更接近OSX文本字段实现,其中文本字段知道下一个焦点应该去哪里
不依赖于设置或使用标签 - 这对于此用例来说是IMO脆弱的
可以扩展为使用UITextField
和UITextView
控件 - 或任何键盘输入UI控件
不会使用样板文件UITextField委托代码来混淆视图控制器
与IB完美集成,可以通过熟悉的选项 - 拖放来配置连接插座.
创建一个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中,更改您的UITextField以使用SOTextField
该类.其次,也是在IB,设置每个"SOTextFields'to'文件的所有者’(你把代码的委托方法是正确的- textFieldShouldReturn)的代表.这种设计的优点在于,现在您只需右键单击任何textField,并将nextField出口分配给SOTextField
您希望成为下一个响应者的下一个对象.
此外,你可以做很酷的事情,比如循环textFields,这样在最后一个失去焦点后,第一个会再次获得焦点.
这可以很容易地扩展到自动分配returnKeyType
的SOTextField
一个UIReturnKeyNext
,如果有分配nextField -少了一个东西手动配置.
这是一个没有代表团的人:
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
).
这是我解决这个问题的方法.
为了解决这个问题(因为我讨厌依靠标签做的东西),我决定自定义属性添加到UITextField对象.换句话说,我在UITextField上创建了一个类别,如下所示:
的UITextField + Extended.h
@interface UITextField (Extended) @property(retain, nonatomic)UITextField* nextTextField; @end
的UITextField + Extended.m
#import "UITextField+Extended.h" #importstatic 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的链接列表,每个人都知道谁在下一行.
希望它会有所帮助.
一个快速扩展,应用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])
对于除最后一个字段之外的所有字段,扩展名将返回按钮设置为"下一个",对于最后一个字段,将"返回"设置为"完成",并在点击这些字段时切换焦点/关闭键盘.
斯威夫特<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) } }
更一致和更健壮的方法是使用NextResponderTextField
您可以完全从界面构建器配置它,而无需设置委托或使用view.tag
.
你需要做的就是
设置类类型的的UITextField
是NextResponderTextField
然后设置nextResponderField
指向下一个响应者的出口,它可以是UITextField
任何子UIResponder
类或任何子类.它也可以是一个UIButton,并且该库足够智能,TouchUpInside
只有在启用时才触发按钮事件.
这是图书馆的行动:
我喜欢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事件.
这是使用UIControl上的类别进行Tab键的实现.这个解决方案具有Michael和Anth0方法的所有优点,但适用于所有UIControl,而不仅仅是UITextField
s.它还可以与Interface Builder和故事板无缝协作.
源和示例应用程序:UIControlsWithTabbing的GitHub存储库
用法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField transferFirstResponderToNextControl]; return NO; }
标题:
// // UIControl+NextControl.h // UIControlsWithTabbing // #import@interface UIControl (NextControl) @property (nonatomic, weak) IBOutlet UIControl *nextControl; - (BOOL)transferFirstResponderToNextControl; @end
执行:
#import "UIControl+NextControl.h" #importstatic 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
我尝试了很多代码,最后,这在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
where将使键盘返回键显示,因为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 }
退出一个文本字段后,调用[otherTextField becomeFirstResponder],下一个字段获得焦点.
这实际上可能是一个棘手的问题,因为您通常还需要滚动屏幕或以其他方式调整文本字段的位置,以便在编辑时很容易看到.只需确保以不同的方式进出文本字段并进行大量测试,并提前离开(总是让用户选择关闭键盘而不是去下一个字段,通常使用"完成"导航栏)
-(BOOL)textFieldShouldReturn:(UITextField *)textField { [[self.view viewWithTag:textField.tag+1] becomeFirstResponder]; return YES; }
按下"完成"按钮时,解除键盘的一种非常简单的方法是:
在标题中创建一个新的IBAction
- (IBAction)textFieldDoneEditing:(id)sender;
在实现文件(.m文件)中添加以下方法:
- (IBAction)textFieldDoneEditing:(id)sender { [sender resignFirstResponder]; }
然后,当您将IBAction链接到文本字段时 - 链接到"退出时结束"事件.
我很惊讶这里有多少答案无法理解一个简单的概念:浏览应用程序中的控件并不是视图本身应该做的事情.这是控制器的工作,以决定哪些控制,使下一个第一响应者.
此外,大多数答案仅适用于向前导航,但用户可能也想要倒退.
所以这就是我想出来的.您的表单应由视图控制器管理,视图控制器是响应程序链的一部分.所以你可以完全自由地实现以下方法:
#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),但在控制器级别,其中,说实话可以代替管理逻辑,是在正确的地方这样做.
首先在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; }