如何在iOS中设置Tab键顺序?

时间:2011-03-15 20:10:40

标签: ios ipad interface-builder

是否有办法(在IB或代码中)在视图中的文本字段之间设置Tab键顺序?

请注意,在按下返回(或“下一步”)按钮后,我不是在谈论下一个表单字段 - 许多蓝牙键盘都有一个tab键,它似乎以完全不同的顺序循环遍历字段。在我的特定情况下,此顺序与视图中字段的位置或甚至添加字段的顺序不对应。手动修改xib文件以更改NSNextKeyView似乎也没有什么区别。

有谁知道如何更改此订单?

8 个答案:

答案 0 :(得分:7)

@sprocket的回答只是有点帮助。仅仅因为开箱即用的东西并不意味着你应该停止思考一种更好的方式 - 甚至是正确的方式 - 做某事。他注意到这种行为没有记录,但大部分时间都符合我们的需求。

但这对我来说还不够。想一想RTL语言,标签仍然会从左到右标签,更不用说行为与模拟器完全不同(设备不会将第一个输入集中在标签上)。最重要的是,Apple的未记录的实现似乎只考虑当前安装在视图层次结构中的视图。

想象一下表格形式(没有双关语)的表格视图。每个单元格都包含一个控件,因此并非所有表单元素都可以同时显示。一旦你到达最底层(在屏幕上!)控制,苹果只会循环回来,而不是向下滚动。这种行为绝对不是我们想要的。

所以这就是我想出的。您的表单应由视图控制器管理,视图控制器是响应程序链的一部分。因此,您完全可以自由地实施以下方法:

#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),而是可以在控制器级别管理逻辑,其中,让&老实说,这是正确的地方

答案 1 :(得分:4)

我有兴趣解决同样的问题,虽然到目前为止,默认顺序似乎是从左到右,然后从上到下,是我想要的。

我测试了光标在子视图和超视图树中以深度优先顺序移动的假设,但为真。更改子视图的顺序而不更改其位置不会更改选项卡按下的字段顺序。

一个可能有用的功能是文本字段委托的textFieldShouldBeginEditing方法似乎是为应用程序窗口中的每个文本字段调用的。如果返回NO,则不会选择文本字段,因此如果您可以定义所需的订单并且仅使用正确的订单,则返回YES,这可能会解决您的问题。

答案 2 :(得分:3)

ios中的Tab键行为如下: - 当你按下外接键盘上的标签时 - 控制器只通过调用shouldBeginEditing方法遍历该屏幕中的所有文本字段,其中返回值也由Apple决定不能覆盖。 扫描完所有字段后,它会计算相对于当前文本字段的视图偏移量的最近x位置文本字段,然后是最近的Y定位字段。

在控制到textFieldDidBeginEditing方法之前,也无法做任何事情。

苹果限制的原因可能是让开发人员遵循UI的指导原则,其中下一个响应者应该是它最接近定位的Field而不是任何其他字段。

答案 3 :(得分:3)

答案 4 :(得分:2)

注册UIKeyCommand以检测按下的Tab键。我在当前的视图控制器中执行了此操作。

    self.addKeyCommand(UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(tabKeyPressed)))

在密钥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]
}

答案 5 :(得分:0)

您可以通过为每个文本字段设置标记并在textfieldShouldReturn方法中处理它来完成此操作。

请参阅此博文了解相关信息: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen

答案 6 :(得分:0)

我发现从物理键盘唯一地检测Tab键击的唯一方法是在canBecomeFirstResponder的自定义对象上实现UIKeyInput协议的insertText:方法。

- (void)insertText:(NSString *)text {
    NSLog(@"text is equal to tab character: %i", [text isEqualToString:@"\t"]);
}

遗憾的是,在子类化UITextField的时候,我没有让它工作,因为UITextField不允许调用insertText:protocol方法。

虽然可能会帮助你...

答案 7 :(得分:0)

我通过将UITextField子类化为NextableTextField来解决这个问题。该子类具有类UITextField的属性,IBOutlet是一个连接。

在IB中构建界面。将文本字段的类设置为NextableTextField。使用连接Inspector将连接拖动到要标签的“下一个”字段。

在文本字段委托类中,添加此委托方法...

- (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;
}
顺便说一句 - 我没想出来;只记得看到别人的想法。