提问人:phx 提问时间:8/28/2009 最后编辑:piccianophx 更新时间:12/21/2021 访问量:222121
如何浏览文本字段(“下一步”/“完成”按钮)
How to navigate through textfields (Next / Done Buttons)
问:
如何使用 iPhone 键盘上的“下一步”按钮浏览所有文本字段?
最后一个文本字段应关闭键盘。
我已经设置了 IB 按钮(下一步/完成),但现在我被卡住了。
我实现了textFieldShouldReturn操作,但现在Next和Done按钮关闭了键盘。
答:
在textFieldShouldReturn中,当他们单击“下一步”时,您应该检查您当前所在的文本字段是否不是最后一个,如果它没有关闭键盘。
评论
退出一个文本字段后,调用 [otherTextField becomeFirstResponder],下一个字段将获得焦点。
这实际上可能是一个棘手的问题,因为您通常还需要滚动屏幕或以其他方式调整文本字段的位置,以便在编辑时轻松查看。只要确保做大量的测试,以不同的方式进入和离开文本字段,并提前离开(总是给用户一个关闭键盘的选项,而不是转到下一个字段,通常在导航栏中使用“完成”)
在 Cocoa for Mac OS X 中,你有下一个响应器链,你可以在其中询问文本字段下一个应该有焦点的控件。这就是使文本字段之间的 Tab 键起作用的原因。但是由于iOS设备没有键盘,只有触摸,因此这个概念在向Cocoa Touch的过渡中并没有幸存下来。
无论如何,这都可以很容易地完成,但有两个假设:
- 所有“tabbable”都位于同一父视图上。
UITextField
- 它们的“制表符顺序”由 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
}
如果文本字段的 superview 将是 UITableViewCell,则下一个响应者将是
let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
评论
UITextFieldDelegate
大家好,请看这个
- (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;
}
}
评论
有一个更优雅的解决方案,当我第一次看到它时,它让我大吃一惊。好处:
- 更接近 OSX 文本字段实现,其中文本字段知道下一步应该关注的地方
- 不依赖于设置或使用标签 - 对于此用例而言,IMO 是脆弱的
- 可以扩展为同时使用 和 控件 -- 或任何键盘输入 UI 控件
UITextField
UITextView
- 不会使用样板 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 中,更改 UITextFields 以使用该类。接下来,同样在 IB 中,将每个“SOTextFields”的委托设置为“File's Owner”(就在您放置委托方法代码 - textFieldShouldReturn 的位置)。这种设计的美妙之处在于,现在您只需右键单击任何 textField,即可将 nextField 出口分配给您希望成为下一个响应者的下一个对象。SOTextField
SOTextField
此外,您可以做一些很酷的事情,例如循环 textFields,以便在最后一个失去焦点后,第一个将再次获得焦点。
这可以很容易地扩展为在分配了 nextField 时自动将 分配给 -- 手动配置少了一件事。returnKeyType
SOTextField
UIReturnKeyNext
评论
UITableView
IBOutlet
nextField
textField1.nextField = textField2; textField2.nextField = textField3;
这是我对这个问题的解决方案。
为了解决这个问题(因为我讨厌依赖标签来做事),我决定向 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 的链接列表,每个链接列表都知道谁是下一个。
希望它会有所帮助。
评论
如果不使用 tags 并且不添加 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];
}];
按下“完成”按钮时关闭键盘的一种非常简单的方法是:
在标头中创建新的 IBAction
- (IBAction)textFieldDoneEditing:(id)sender;
在实现文件(.m 文件)中,添加以下方法:
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
然后,当您将 IBAction 链接到文本字段时 - 链接到“Did End On Exit”事件。
评论
我喜欢 Anth0 和 Answerbot 已经建议的 OO 解决方案。但是,我正在开发一个快速而小的 POC,所以我不想用子类和类别来混淆东西。
另一个简单的解决方案是创建一个字段的 NSArray,并在按下一步时查找下一个字段。不是面向对象的解决方案,而是快速、简单且易于实施。此外,您可以一目了然地查看和修改排序。
这是我的代码(基于此线程中的其他答案构建):
@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 事件。
评论
我已添加到 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
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}
评论
我试图使用一种更复杂的方法解决这个问题,该方法基于在以后可以检索的唯一标签值中分配每个单元格(或):activate-next-uitextfield-in-uitableview-iosUITextField
UITableView
我希望这会有所帮助!
我宁愿:
@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;
}
首先在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;
}
下面是一个没有委托的:
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
您还可以轻松地将其挂接到情节提要中,因此不需要委派或代码。
编辑:实际上我无法弄清楚如何在故事板中将其连接起来。 似乎没有为这个控制事件提供动作,这很遗憾。不过,您可以将所有文本字段挂接到 ViewController 中的单个操作,然后根据发送者确定要使用的 textField(尽管它不像上面的编程解决方案那样优雅,因此 IMO 使用上述代码来完成)。becomeFirstResponder
becomeFirstResponder
viewDidLoad
评论
[tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];
下面是在 UIControl 上使用类别的 Tab 键实现。此解决方案具有 Michael 和 Anth0 方法的所有优点,但适用于所有 UIControls,而不仅仅是 s。它还可以与Interface Builder和故事板无缝协作。UITextField
源和示例应用:UIControlsWithTabbing 的 GitHub 存储库Source and sample app: GitHub repository for UIControlsWithTabbing
用法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField transferFirstResponderToNextControl];
return NO;
}
页眉:
//
// 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
评论
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 ; }
我的故事板中大约有 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
这是一篇旧文章,但页面排名很高,所以我会附和我的解决方案。
我遇到了类似的问题,最终创建了一个子类,用于在动态 tableView 中管理下一个/上一个/完成的功能,其中包含以下部分: https://github.com/jday001/DataEntryToolbarUIToolbar
将工具栏设置为文本字段的 inputAccessoryView,并将其添加到其字典中。这允许您向前和向后循环浏览它们,即使是动态内容也是如此。如果要在 textField 导航发生时触发自己的功能,则可以使用委托方法,但不必处理管理任何标记或第一响应者状态。
GitHub 链接上有代码片段和示例应用程序,可帮助了解实现细节。您将需要自己的数据模型来跟踪字段中的值。
我已经使用Michael G. Emmons的答案大约一年了,效果很好。我最近确实注意到,立即调用 resignFirstResponder 然后成为 befrontResponder 可能会导致键盘“故障”,消失然后立即出现。如果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; }
令我惊讶的是,这里有多少答案无法理解一个简单的概念:在应用程序中的控件中导航不是视图本身应该做的事情。控制器的工作是决定将哪个控制作为下一个第一响应者。
此外,大多数答案仅适用于向前导航,但用户可能也希望向后导航。
这就是我想出的。表单应由视图控制器管理,视图控制器是响应程序链的一部分。因此,您可以完全自由地实现以下方法:
#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];
}
可能适用用于滚动事先可见的屏幕外响应者的其他逻辑。
这种方法的另一个优点是,你不需要对你可能想要显示的所有类型的控件(如s)进行子类化,而是可以在控制器级别管理逻辑,老实说,这是这样做的正确位置。UITextField
评论
您可以使用 IQKeyboardManager 库来执行此操作。它可以处理所有事情,您不需要任何额外的设置。IQKeyboardManager 可通过 CocoaPods 获得,要安装它,只需将以下行添加到您的 Podfile 中:
pod 'IQKeyboardManager'
或 只需将 IQKeyBoardManager 目录从演示项目拖放到您的项目中即可。就是这样。 您可以从以下位置找到 IQKeyBoardManager 目录 https://github.com/hackiftekhar/IQKeyboardManager
一种更一致和健壮的方法是使用 NextResponderTextField 您可以完全从接口构建器配置它,而无需设置委托或使用 .view.tag
您需要做的就是
- 将 your 的类类型设置为
UITextField
NextResponderTextField
- 然后将 to 的出口设置为指向下一个响应者,它可以是任何东西或任何子类。它也可以是 UIButton,并且该库足够智能,只有在启用按钮时才会触发按钮的事件。
nextResponderField
UITextField
UIResponder
TouchUpInside
以下是运行中的库:
评论
在处理这些东西时,我刚刚创建了新的 Pod GNTextFieldsCollectionManager。它会自动处理下一个/最后一个 textField 问题,并且非常易于使用:
[[GNTextFieldsCollectionManager alloc] initWithView:self.view];
抓取按视图层次结构中显示(或按标记)排序的所有文本字段,或者您可以指定自己的 textField 数组。
这在 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 没有 Tag,请立即设置它:
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.
一个快速扩展,应用了 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)
}
}
评论
UITextFieldDelegate
func textFieldDidEndEditing(textField: UITextField)
textField != field3
这是 swift 中的一个简单的解决方案,没有使用标签,没有故事板技巧......
只需使用此扩展:
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
}
评论
这是 Anth0 的答案的 Swift 3 版本。我在这里发布它是为了帮助任何想要利用他的伟大答案的快速开发人员!当您设置关联对象时,我冒昧地添加了返回键类型“Next”。
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)
}
}
}
下面是另一个扩展,它显示了使用上述代码循环浏览 UITextFields 列表的可能性。
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.
我尝试了很多代码,最后,这在 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
}
在上面的代码中,where 将使键盘返回键显示,因为您还有其他选项,例如,根据您的应用程序更改值。returnKeyType = UIReturnKeyType.next
Next
Join/Go
这是 UITextFieldDelegate 控制的方法,这里我们根据 Tag 值递增选择下一个字段textFieldShouldReturn
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
UITextField
UITextView
UITextView
在 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
}
}
如果有人想要这样。我认为这是最接近所要求的要求
这是我如何实现这个
为要设置的每个文本字段添加附件视图,使用
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 标记。
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
}
}
}
}
Swift 4 for mxcl 答案:
txtFirstname.addTarget(txtLastname, action:
#selector(becomeFirstResponder), for: UIControlEvents.editingDidEndOnExit)
一种更安全、更直接的方式,假设:
- 文本字段委托将设置为视图控制器
- 所有文本字段都是同一视图的子视图
- 文本字段具有按您想要进度顺序排列的标签(例如,textField2.tag = 2、textField3.tag = 3 等)
- 当您点击键盘上的返回按钮时,将移动到下一个文本字段(您可以将其更改为下一个、完成等)。
- 您希望键盘在最后一个文本字段后关闭
斯威夫特 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
}
}
评论
我实现了一个动态解决方案,用于在视图层次结构中浏览响应程序。这个想法是,在整个层次结构中搜索下一个可能的响应者,根据需要向前或向后。
用于搜索的 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];
}
}
上一个:在 iOS 模拟器中截取屏幕截图
评论