本文选自StackOverflow(简称:SOF)精选问答汇总系列文章之一,本系列文章将为读者分享国外最优质的精彩问与答,供读者学习和了解国外最新技术。本文将为读者讲解如何通过文本框导航。
问题:
在iPhone键盘上怎样才能使用“下一步”按钮通过文本框导航?
最后一个文本框用来关闭键盘。
我已经设置了IB(下一步/完成)按钮,就进行不下去了。
我执行了textFieldShouldReturn,但是下一步和完成按钮关闭了键盘。
谢谢大家帮助!
答案:
在Mac OS X 的Cocoa里,有下一个响应链,可以解决文本框下一个应该控制什么。这完成文本框之间的切换工作。但是由于iPhone没有键盘,只有触屏,所以这个概念没有顺利迁移到Cocoa Touch上。
无论如何,在下面两个前提下,这个还是容易解决的:
1.所以可切换的UITextFields在同一个父视图里。
2.切换顺序是通过TAG资源定义的。
假设这个可以覆盖textFieldShouldReturn,像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
NSInteger nextTag = textField.tag + 1;
// 尝试查找下一个响应
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if
(nextResponder) {
//发现下一个响应,所以设置
[nextResponder becomeFirstResponder];
}
else
{
//没有找到,所以移除键盘
[textField resignFirstResponder];
}
return
NO;
// 不想让UITextField插入换行符.
}
|
再加一些代码,这些假设也可以被忽略。
有一个更好的解决办法,我第一次看到觉得不错,好处如下:
- 回归OSX的文本框实现,导航下一步是什么
- 不需要依靠TAG
- 可以扩展到UITextField 和 UITextView控制,或者其他UI控制的键盘。
- 不会因为标准文本的UITextField委托代码弄乱视图控制器
- 很好地整合了IB ,可以通过相似的选择拖放链接网店
- 可以通过熟悉的拖拽方式链接outlet
创建一个UITextField子集合,这个子集合拥有名叫nextField 的IBOutlet 资源。这是头文件:
1
2
3
4
5
|
@interface SOTextField : UITextField
@property (nonatomic, readwrite, assign) IBOutlet UITextField *nextField;
@end
|
这是执行:
1
2
3
4
5
|
@implementation SOTextField
@synthesize nextField;
@end
|
在视图控制器里, 你将创建 -textFieldShouldReturn: 委托方法:
1
2
3
4
5
6
7
8
9
10
11
12
|
- (BOOL) textFieldShouldReturn:(UITextField *) textField {
BOOL didResign = [textField resignFirstResponder];
if
(!didResign)
return
NO;
if
([textField isKindOfClass:[SOTextField class]])
dispatch_async(dispatch_get_current_queue(),
^ { [[(SOTextField *)textField nextField] becomeFirstResponder]; });
return
YES;
}
|
在IB里。改变UITextFields使用SOTextField类。接着,还是在IB里,为每个SOTextFields设置委托至文件的所有者(放置委托函数textFieldShouldReturn的地方)。这个设计的优点在于,在任何的textField右键分配nextField出口到你所想让它成为下一个响应的SOTextField对象。
再者,还可以循环textField,使最后一个失去焦点时,第一个又成为焦点。
这个很容易被扩展到自动分配SOTextField的returnKeyType到UIReturnKeyNext,如果在有一个被分配的nextField——少一个手动配置的情况下。
这是我的解决方案。
为了解决这一问题,我决定给UITextField对象添加一个自定义属性。换句话说我在UITextField创建一个这样的类:
UITextField+Extended.h
1
2
3
4
5
|
<strong><i>@interface UITextField (Extended)
@property(retain, nonatomic)UITextField* nextTextField;
@end </i></strong>
|
UITextField+Extended.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<strong><i>
#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 </i></strong>
|
现在,看看我怎么使用它:
1
2
3
4
5
6
7
|
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函数:
1
2
3
4
5
6
7
8
9
10
11
|
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
UITextField *next = theTextField.nextTextField;
if
(next) {
[next becomeFirstResponder];
}
else
{
[theTextField resignFirstResponder];
}
return
NO;
}
|
现在我有了一种UITextField 链表,每一个都知道下一个是什么。
希望对你有所帮助。
这有一个非常简单的方法当按下“完成”键是驳回键盘。
在头文件里创建一个新的IBAction:
1
|
- (IBAction)textFieldDoneEditing:(id)sender;
|
在执行文件里(.m file)里添加下述函数:
1
2
3
4
5
|
In the implementation file (.m file) add the following method:
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
|
然后,当你链接IBAction至textfield,连接到Did End On Exit事件。
I我喜欢 OO 解决办法, Anth0和Answerbot都已提到过。然而,我正在做一个快而小的POC,我不想用子类和类别弄乱东西。
另一个简单的解决办法是创建字段的NSArray,当按下“下一步”的时候查找下一个。这不是00方法,但是简单、快捷、易执行。另外,你可以一目了然地查看和修改顺序。
这是我写的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@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。改变首个响应时即刻的,将立即触发KeyboardWasShown事件。
我已经完善了PeyloW的回答,以防你想实现上一个/下一个按钮的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender
{
NSInteger nextTag;
UITextView *currentTextField = [self.view findFirstResponderAndReturn];
if
(currentTextField != nil) {
//指定按钮的标签 0 代表上一个 1代表下一个
if
(sender.tag == 0) {
nextTag = currentTextField.tag - 1;
}
else
if
(sender.tag == 1) {
nextTag = currentTextField.tag + 1;
}
}
// 尝试查找下一个响应
UIResponder* nextResponder = [self.view viewWithTag:nextTag];
if
(nextResponder) {
//发现下一个响应,所以请设置
// 我在这里添加了resign 以保证有不同而适当的键盘 [currentTextField resignFirstResponder];
[nextResponder becomeFirstResponder];
}
else
{
//没有找到,所以移除键盘
[currentTextField resignFirstResponder];
}
}
|
继承 UIView是这样的:
1
2
3
4
5
6
7
8
9
10
11
|
@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
for
(UITextView *subView
in
self.subviews) {
if
(subView.isFirstResponder){
return
subView;
}
}
return
nil;
}
@end
|
原文链接:How to navigate through textfields (Next / Done Buttons)
文章选自StackOverFlow社区,鉴于其内容对于开发者有所帮助,现将文章翻译于此,供大家参考及学习。9Tech将每日持续更新,读者可点击StackOverflow(简称:SOF)精选问答汇总,查看全部译文内容。同时,我们也招募志同道合的技术朋友共同翻译,造福大家!报名请发邮件至zhangqi_wj#cyou-inc.com。(#换成@)