Is there a way (either in IB or code) to set the tab order between text fields in a view?
有没有办法(在IB或代码中)在视图中的文本字段之间设置Tab键顺序?
Note that I'm not talking about the next form field after the return (or "Next") button is pressed -- many bluetooth keyboards have a tab key, which seems to cycle through the fields in completely different order. In my particular case, this order doesn't correspond to the fields' position in the view or even the order in which the fields were added. Modifying the xib file by hand to change the NSNextKeyView doesn't seem to make a difference either.
请注意,在按下返回(或“下一步”)按钮后,我不是在谈论下一个表单字段 - 许多蓝牙键盘都有一个tab键,它似乎以完全不同的顺序循环遍历字段。在我的特定情况下,此顺序与视图中字段的位置或甚至是添加字段的顺序不对应。手动修改xib文件以更改NSNextKeyView似乎也没有什么区别。
Does anyone know how to change this order?
有谁知道如何更改此订单?
8 个解决方案
#1
4
I'm interested in solving the same problem, although so far the default order, which appears to be left to right, then top to bottom, is the one I want.
我有兴趣解决同样的问题,虽然到目前为止默认顺序似乎是从左到右,然后从上到下,是我想要的。
I tested the hypothesis that the cursor moves in depth-first order through the tree of subviews and superview, but that is not true. Changing the order of subviews without changing their location didn't change the order of fields traversed by tab presses.
我测试了光标通过子视图树和超视图以深度优先顺序移动的假设,但事实并非如此。更改子视图的顺序而不更改其位置不会更改选项卡按下所遍历的字段的顺序。
One possibly useful feature is that the text field delegate's textFieldShouldBeginEditing method appears to be called for every text field in the application's window. If that returns NO, then the text field won't be chosen, so if you can define your desired order and make only the right one return YES, that might solve your problem.
一个可能有用的特性是文本字段委托的textFieldShouldBeginEditing方法似乎是为应用程序窗口中的每个文本字段调用的。如果返回NO,则不会选择文本字段,因此如果您可以定义所需的订单并且仅使用正确的订单返回YES,则可能会解决您的问题。
#2
6
@sprocket's answer was only somewhat helpful. Just because something works out of the box doesn't mean you should stop thinking about a better way -- or even the right way -- of doing something. As he noticed the behavior is undocumented but fits our needs most of the time.
@sprocket的回答只是有点帮助。仅仅因为开箱即用的东西并不意味着你应该停止思考一种更好的方式 - 甚至是正确的方式 - 做某事。他注意到这种行为没有记录,但大部分时间都符合我们的需求。
This wasn't enough for me though. Think of a RTL language and tabs would still tab left-to-right, not to mention the behavior is entirely different from simulator to device (device doesn't focus the first input upon tab). Most importantly though, Apple's undocumented implementation seems to only consider views currently installed in the view hierarchy.
但这对我来说还不够。想想RTL语言,标签仍然是从左到右的标签,更不用说行为与模拟器完全不同(设备不会将第一个输入集中在标签上)。最重要的是,Apple的无证实现似乎只考虑当前安装在视图层次结构中的视图。
Think of a form in form of (no pun intended) a table view. Each cell holds a single control, hence not all form elements may be visible at the same time. Apple would just cycle back up once you reached the bottommost (on screen!) control, instead of scrolling further down. This behavior is most definitely not what we desire.
想象一下表格形式(没有双关语)的表格视图。每个单元格都包含一个控件,因此并非所有表单元素都可以同时显示。一旦你到达最底层(在屏幕上!)控制,苹果只会循环回来,而不是向下滚动。这种行为绝对不是我们想要的。
So here's what I've come up with. Your form should be managed by a view controller, and view controllers are part of the responder chain. So you're perfectly free to implement the following methods:
所以这就是我想出来的。您的表单应由视图控制器管理,视图控制器是响应程序链的一部分。所以你可以完全*地实现以下方法:
#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];
}
Additional logic for scrolling offscreen responders visible beforehand may apply.
可以应用用于预先滚动屏幕外响应者的附加逻辑。
Another advantage of this approach is that you don't need to subclass all kinds of controls you may want to display (like UITextField
s) but can instead manage the logic at controller level, where, let's be honest, is the right place to do so.
这种方法的另一个优点是你不需要子类化你可能想要显示的各种控件(比如UITextFields),而是可以在控制器级别管理逻辑,诚然,这是正确的地方。 。
#3
3
This is how you set the tab order on iOS:
这是您在iOS上设置Tab键顺序的方法:
http://weaklyreferenced.wordpress.com/2012/11/13/responding-to-the-tab-and-shift-tab-keys-on-ios-5-ios-6-with-an-external-keyboard/
#4
3
The Tab key behaviour in ios will be as follows:- when u press tab on external keyboard- the control traverses across all the textfields in that screen by calling only shouldBeginEditing method where its return value is also determined by Apple which cant be override. After scanning all the fields it calculates nearest x positioned Textfield relative to view offset from the current Textfield and then nearest Y Positioned Field.
ios中的Tab键行为如下: - 当你按外部键盘上的tab时 - 控件遍历该屏幕中的所有文本字段,只调用shouldBeginEditing方法,其返回值也由Apple决定不能覆盖。扫描完所有字段后,它会计算相对于当前文本字段的视图偏移量最近的x定位文本字段,然后是最近的Y定位字段。
Also can't be done anything until control comes to textFieldDidBeginEditing method.
在控制到textFieldDidBeginEditing方法之前也无法做任何事情。
Reason for apple's restriction might be to let devs to follow the guidelines of UI where next responder of field should be it's closest positioned Field rather than any other field .
苹果限制的原因可能是让开发人员遵循UI的指导原则,其中下一个响应者应该是它最接近定位的Field而不是任何其他领域。
#5
2
Register a UIKeyCommand to detect the tab key pressed. I did this in my current view controller.
注册UIKeyCommand以检测按下的Tab键。我在当前的视图控制器中执行了此操作。
self.addKeyCommand(UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(tabKeyPressed)))
Inside the key tabKeyPressed handler find your current active field then set your next responder. orderedTextFields is an array of UITextField in the tab order I want.
在关键tabKeyPressed处理程序内找到您当前的活动字段,然后设置您的下一个响应者。 orderedTextFields是我想要的Tab键顺序中的UITextField数组。
func tabKeyPressed(){
let activeField = getActiveField()
if(activeField == nil){
return
}
let nextResponder = getNextTextField(activeField!)
nextResponder?.becomeFirstResponder()
}
func getActiveField() -> UITextField? {
for textField in orderedTextFields {
if(textField.isFirstResponder()){
return textField
}
}
return nil
}
func getNextTextField(current: UITextField) -> UITextField? {
let index = orderedTextField.indexOf(current)
if(orderedTextField.count-1 <= index!){
return nil
}
return orderedTextField[index! + 1]
}
#6
0
You can do this by setting the tag for each textfield and handling this in the textfieldShouldReturn method.
您可以通过为每个文本字段设置标记并在textfieldShouldReturn方法中处理它来完成此操作。
See this blogpost about it: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
请参阅以下博文:http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
#7
0
The only way I've found to uniquely detect a Tab keystroke from a physical keyboard, is implementing the UIKeyInput protocol's insertText: method on a custom object that canBecomeFirstResponder.
我发现从物理键盘唯一检测Tab键击的唯一方法是在一个自定义对象canBecomeFirstResponder上实现UIKeyInput协议的insertText:方法。
- (void)insertText:(NSString *)text {
NSLog(@"text is equal to tab character: %i", [text isEqualToString:@"\t"]);
}
I didn't get this to work while subclassing UITextField, unfortunately, as UITextField won't allow the insertText: protocol method to get called.
遗憾的是,在子类化UITextField时,我没有让它工作,因为UITextField不允许调用insertText:protocol方法。
Might help you on the way, though..
虽然可能会帮助你。
#8
0
I solved this by subclassing UITextField as NextableTextField. That subclass has a property of class UITextField with IBOutlet a hookup.
我通过将UITextField子类化为NextableTextField来解决这个问题。该子类具有类UITextField的属性,IBOutlet是一个连接。
Build the interface in IB. Set the class of your text field to NextableTextField. Use the connections Inspector to drag a connection to the 'next' field you want to tab to.
在IB中构建界面。将文本字段的类设置为NextableTextField。使用连接Inspector将连接拖动到要选择的“下一个”字段。
In your text field delegate class, add this delegate method...
在您的文本字段委托类中,添加此委托方法...
- (BOOL)textFieldShouldReturn:(UITextField *) textField
{
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
if ([textField isKindOfClass:[NextableTextField class]])
dispatch_async(dispatch_get_current_queue(), ^{ [[(NextableTextField *)textField nextField] becomeFirstResponder]; });
return YES;
}
BTW - I didn't come up with this; just remember seeing someone else's idea.
顺便说一句 - 我没想出来;只记得看到别人的想法。
#1
4
I'm interested in solving the same problem, although so far the default order, which appears to be left to right, then top to bottom, is the one I want.
我有兴趣解决同样的问题,虽然到目前为止默认顺序似乎是从左到右,然后从上到下,是我想要的。
I tested the hypothesis that the cursor moves in depth-first order through the tree of subviews and superview, but that is not true. Changing the order of subviews without changing their location didn't change the order of fields traversed by tab presses.
我测试了光标通过子视图树和超视图以深度优先顺序移动的假设,但事实并非如此。更改子视图的顺序而不更改其位置不会更改选项卡按下所遍历的字段的顺序。
One possibly useful feature is that the text field delegate's textFieldShouldBeginEditing method appears to be called for every text field in the application's window. If that returns NO, then the text field won't be chosen, so if you can define your desired order and make only the right one return YES, that might solve your problem.
一个可能有用的特性是文本字段委托的textFieldShouldBeginEditing方法似乎是为应用程序窗口中的每个文本字段调用的。如果返回NO,则不会选择文本字段,因此如果您可以定义所需的订单并且仅使用正确的订单返回YES,则可能会解决您的问题。
#2
6
@sprocket's answer was only somewhat helpful. Just because something works out of the box doesn't mean you should stop thinking about a better way -- or even the right way -- of doing something. As he noticed the behavior is undocumented but fits our needs most of the time.
@sprocket的回答只是有点帮助。仅仅因为开箱即用的东西并不意味着你应该停止思考一种更好的方式 - 甚至是正确的方式 - 做某事。他注意到这种行为没有记录,但大部分时间都符合我们的需求。
This wasn't enough for me though. Think of a RTL language and tabs would still tab left-to-right, not to mention the behavior is entirely different from simulator to device (device doesn't focus the first input upon tab). Most importantly though, Apple's undocumented implementation seems to only consider views currently installed in the view hierarchy.
但这对我来说还不够。想想RTL语言,标签仍然是从左到右的标签,更不用说行为与模拟器完全不同(设备不会将第一个输入集中在标签上)。最重要的是,Apple的无证实现似乎只考虑当前安装在视图层次结构中的视图。
Think of a form in form of (no pun intended) a table view. Each cell holds a single control, hence not all form elements may be visible at the same time. Apple would just cycle back up once you reached the bottommost (on screen!) control, instead of scrolling further down. This behavior is most definitely not what we desire.
想象一下表格形式(没有双关语)的表格视图。每个单元格都包含一个控件,因此并非所有表单元素都可以同时显示。一旦你到达最底层(在屏幕上!)控制,苹果只会循环回来,而不是向下滚动。这种行为绝对不是我们想要的。
So here's what I've come up with. Your form should be managed by a view controller, and view controllers are part of the responder chain. So you're perfectly free to implement the following methods:
所以这就是我想出来的。您的表单应由视图控制器管理,视图控制器是响应程序链的一部分。所以你可以完全*地实现以下方法:
#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];
}
Additional logic for scrolling offscreen responders visible beforehand may apply.
可以应用用于预先滚动屏幕外响应者的附加逻辑。
Another advantage of this approach is that you don't need to subclass all kinds of controls you may want to display (like UITextField
s) but can instead manage the logic at controller level, where, let's be honest, is the right place to do so.
这种方法的另一个优点是你不需要子类化你可能想要显示的各种控件(比如UITextFields),而是可以在控制器级别管理逻辑,诚然,这是正确的地方。 。
#3
3
This is how you set the tab order on iOS:
这是您在iOS上设置Tab键顺序的方法:
http://weaklyreferenced.wordpress.com/2012/11/13/responding-to-the-tab-and-shift-tab-keys-on-ios-5-ios-6-with-an-external-keyboard/
#4
3
The Tab key behaviour in ios will be as follows:- when u press tab on external keyboard- the control traverses across all the textfields in that screen by calling only shouldBeginEditing method where its return value is also determined by Apple which cant be override. After scanning all the fields it calculates nearest x positioned Textfield relative to view offset from the current Textfield and then nearest Y Positioned Field.
ios中的Tab键行为如下: - 当你按外部键盘上的tab时 - 控件遍历该屏幕中的所有文本字段,只调用shouldBeginEditing方法,其返回值也由Apple决定不能覆盖。扫描完所有字段后,它会计算相对于当前文本字段的视图偏移量最近的x定位文本字段,然后是最近的Y定位字段。
Also can't be done anything until control comes to textFieldDidBeginEditing method.
在控制到textFieldDidBeginEditing方法之前也无法做任何事情。
Reason for apple's restriction might be to let devs to follow the guidelines of UI where next responder of field should be it's closest positioned Field rather than any other field .
苹果限制的原因可能是让开发人员遵循UI的指导原则,其中下一个响应者应该是它最接近定位的Field而不是任何其他领域。
#5
2
Register a UIKeyCommand to detect the tab key pressed. I did this in my current view controller.
注册UIKeyCommand以检测按下的Tab键。我在当前的视图控制器中执行了此操作。
self.addKeyCommand(UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(tabKeyPressed)))
Inside the key tabKeyPressed handler find your current active field then set your next responder. orderedTextFields is an array of UITextField in the tab order I want.
在关键tabKeyPressed处理程序内找到您当前的活动字段,然后设置您的下一个响应者。 orderedTextFields是我想要的Tab键顺序中的UITextField数组。
func tabKeyPressed(){
let activeField = getActiveField()
if(activeField == nil){
return
}
let nextResponder = getNextTextField(activeField!)
nextResponder?.becomeFirstResponder()
}
func getActiveField() -> UITextField? {
for textField in orderedTextFields {
if(textField.isFirstResponder()){
return textField
}
}
return nil
}
func getNextTextField(current: UITextField) -> UITextField? {
let index = orderedTextField.indexOf(current)
if(orderedTextField.count-1 <= index!){
return nil
}
return orderedTextField[index! + 1]
}
#6
0
You can do this by setting the tag for each textfield and handling this in the textfieldShouldReturn method.
您可以通过为每个文本字段设置标记并在textfieldShouldReturn方法中处理它来完成此操作。
See this blogpost about it: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
请参阅以下博文:http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
#7
0
The only way I've found to uniquely detect a Tab keystroke from a physical keyboard, is implementing the UIKeyInput protocol's insertText: method on a custom object that canBecomeFirstResponder.
我发现从物理键盘唯一检测Tab键击的唯一方法是在一个自定义对象canBecomeFirstResponder上实现UIKeyInput协议的insertText:方法。
- (void)insertText:(NSString *)text {
NSLog(@"text is equal to tab character: %i", [text isEqualToString:@"\t"]);
}
I didn't get this to work while subclassing UITextField, unfortunately, as UITextField won't allow the insertText: protocol method to get called.
遗憾的是,在子类化UITextField时,我没有让它工作,因为UITextField不允许调用insertText:protocol方法。
Might help you on the way, though..
虽然可能会帮助你。
#8
0
I solved this by subclassing UITextField as NextableTextField. That subclass has a property of class UITextField with IBOutlet a hookup.
我通过将UITextField子类化为NextableTextField来解决这个问题。该子类具有类UITextField的属性,IBOutlet是一个连接。
Build the interface in IB. Set the class of your text field to NextableTextField. Use the connections Inspector to drag a connection to the 'next' field you want to tab to.
在IB中构建界面。将文本字段的类设置为NextableTextField。使用连接Inspector将连接拖动到要选择的“下一个”字段。
In your text field delegate class, add this delegate method...
在您的文本字段委托类中,添加此委托方法...
- (BOOL)textFieldShouldReturn:(UITextField *) textField
{
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
if ([textField isKindOfClass:[NextableTextField class]])
dispatch_async(dispatch_get_current_queue(), ^{ [[(NextableTextField *)textField nextField] becomeFirstResponder]; });
return YES;
}
BTW - I didn't come up with this; just remember seeing someone else's idea.
顺便说一句 - 我没想出来;只记得看到别人的想法。