web-dev-qa-db-ja.com

iOSでタブオーダーをどのように設定しますか?

ビューのテキストフィールド間のタブ順序を設定する方法(IBまたはコードのいずれか)はありますか?

戻る(または「次へ」)ボタンが押された後の次のフォームフィールドについて話しているわけではないことに注意してください。多くのBluetoothキーボードにはタブキーがあり、フィールドを完全に異なる順序で循環しているようです。私の特定のケースでは、この順序はビュー内のフィールドの位置、またはフィールドが追加された順序にも対応していません。 NSNextKeyViewを変更するために手動でxibファイルを変更しても、違いはないようです。

誰もがこの順序を変更する方法を知っていますか?

47
lazycs

私は同じ問題を解決することに興味がありますが、これまでのところ、左から右、次に上から下に表示されるデフォルトの順序が必要です。

カーソルがサブビューとスーパービューのツリーを介して深さ優先で移動するという仮説をテストしましたが、それはnot trueです。位置を変更せずにサブビューの順序を変更しても、tabプレスが通過するフィールドの順序は変更されませんでした。

おそらく役立つ機能の1つは、テキストフィールドデリゲートのtextFieldShouldBeginEditingメソッドが、アプリケーションのウィンドウ内のすべてのテキストフィールドに対して呼び出されているように見えることです。それがNOを返す場合、テキストフィールドは選択されないため、目的の順序を定義して正しいものだけをYESに戻すことができれば、問題が解決する可能性があります。

4
sprocket

@sprocketの回答はやや役に立ちました。箱から出して何かがうまくいくからといって、何かを行うためのより良い方法、あるいは正しい方法-について考えるのをやめるべきではありません。彼が気づいたように、動作は文書化されていませんが、ほとんどの場合私たちのニーズに適合しています。

私にはこれで十分ではありませんでした。 RTL言語について考えてみてください。タブは左から右にタブで移動します。言うまでもなく、動作はシミュレータごとに異なります(デバイスは最初の入力をタブにフォーカスしません)。最も重要なことですが、Appleの文書化されていない実装では、ビュー階層に現在インストールされているビューのみが考慮されるようです。

テーブルビュー(しゃれはありません)のフォームを考えます。各セルは単一のコントロールを保持しているため、すべてのフォーム要素が同時に表示されるとは限りません。 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];
}

事前に表示されている画面外のレスポンダーをスクロールするための追加のロジックが適用される場合があります。

このアプローチのもう1つの利点は、表示したいすべての種類のコントロール(UITextFieldsなど)をサブクラス化する必要がなく、代わりにコントローラーレベルでロジックを管理できることです。 正しい場所そうする。

8

IOSでのTabキーの動作は次のようになります。-外部キーボードでTabキーを押すと、コントロールは、その戻り値もAppleこれはオーバーライドできません。すべてのフィールドをスキャンした後、現在のテキストフィールドからのビューオフセットに対して最も近いx位置のテキストフィールドを計算し、次に最も近いY位置のフィールドを計算します。

また、コントロールがtextFieldDidBeginEditingメソッドに到達するまで、何もできません。

Appleの制限の理由は、フィールドの次のレスポンダーが他のフィールドではなく最も近い位置にあるフィールドであるUIのガイドラインに従うことを開発者に許可することです。

3
vrk
3
AlleyGator

押されたタブキーを検出するUIKeyCommandを登録します。私は現在のビューコントローラーでこれを行いました。

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

キーのtabKeyPressedハンドラー内で現在アクティブなフィールドを見つけ、次のレスポンダを設定します。 orderedTextFieldsは、必要なタブオーダーの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]
}
2

これを行うには、各テキストフィールドにタグを設定し、textfieldShouldReturnメソッドでこれを処理します。

それに関するこのブログ投稿を参照してください: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen

0
Milk78

UITextFieldをNextableTextFieldとしてサブクラス化することでこれを解決しました。そのサブクラスには、IBOutletフックアップを持つUITextFieldクラスのプロパティがあります。

IBでインターフェースを作成します。テキストフィールドのクラスをNextableTextFieldに設定します。接続インスペクタを使用して、タブを移動する「次の」フィールドに接続をドラッグします。

テキストフィールドのデリゲートクラスに、このデリゲートメソッドを追加します...

- (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;
}

ところで-私はこれを思いつきませんでした。他の誰かのアイデアを見たことを覚えておいてください。

0

私が物理キーボードからTabキーストロークを一意に検出する唯一の方法は、UIKeyInputプロトコルのinsertText:メソッドを、canBecomeFirstResponderであるカスタムオブジェクトに実装することです。

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

残念ながら、UITextFieldはinsertText:プロトコルメソッドの呼び出しを許可しないため、UITextFieldをサブクラス化するときにこれを機能させませんでした。

しかし、途中であなたを助けるかもしれません。

0
hsdev