ビューのテキストフィールド間のタブ順序を設定する方法(IBまたはコードのいずれか)はありますか?
戻る(または「次へ」)ボタンが押された後の次のフォームフィールドについて話しているわけではないことに注意してください。多くのBluetoothキーボードにはタブキーがあり、フィールドを完全に異なる順序で循環しているようです。私の特定のケースでは、この順序はビュー内のフィールドの位置、またはフィールドが追加された順序にも対応していません。 NSNextKeyViewを変更するために手動でxibファイルを変更しても、違いはないようです。
誰もがこの順序を変更する方法を知っていますか?
私は同じ問題を解決することに興味がありますが、これまでのところ、左から右、次に上から下に表示されるデフォルトの順序が必要です。
カーソルがサブビューとスーパービューのツリーを介して深さ優先で移動するという仮説をテストしましたが、それはnot trueです。位置を変更せずにサブビューの順序を変更しても、tabプレスが通過するフィールドの順序は変更されませんでした。
おそらく役立つ機能の1つは、テキストフィールドデリゲートのtextFieldShouldBeginEditingメソッドが、アプリケーションのウィンドウ内のすべてのテキストフィールドに対して呼び出されているように見えることです。それがNOを返す場合、テキストフィールドは選択されないため、目的の順序を定義して正しいものだけをYESに戻すことができれば、問題が解決する可能性があります。
@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つの利点は、表示したいすべての種類のコントロール(UITextField
sなど)をサブクラス化する必要がなく、代わりにコントローラーレベルでロジックを管理できることです。 正しい場所そうする。
IOSでのTabキーの動作は次のようになります。-外部キーボードでTabキーを押すと、コントロールは、その戻り値もAppleこれはオーバーライドできません。すべてのフィールドをスキャンした後、現在のテキストフィールドからのビューオフセットに対して最も近いx位置のテキストフィールドを計算し、次に最も近いY位置のフィールドを計算します。
また、コントロールがtextFieldDidBeginEditingメソッドに到達するまで、何もできません。
Appleの制限の理由は、フィールドの次のレスポンダーが他のフィールドではなく最も近い位置にあるフィールドであるUIのガイドラインに従うことを開発者に許可することです。
押されたタブキーを検出する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]
}
これを行うには、各テキストフィールドにタグを設定し、textfieldShouldReturnメソッドでこれを処理します。
それに関するこのブログ投稿を参照してください: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
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;
}
ところで-私はこれを思いつきませんでした。他の誰かのアイデアを見たことを覚えておいてください。
私が物理キーボードからTabキーストロークを一意に検出する唯一の方法は、UIKeyInputプロトコルのinsertText:メソッドを、canBecomeFirstResponderであるカスタムオブジェクトに実装することです。
- (void)insertText:(NSString *)text {
NSLog(@"text is equal to tab character: %i", [text isEqualToString:@"\t"]);
}
残念ながら、UITextFieldはinsertText:プロトコルメソッドの呼び出しを許可しないため、UITextFieldをサブクラス化するときにこれを機能させませんでした。
しかし、途中であなたを助けるかもしれません。