テキストフィールドをナビゲートする方法(次へ/完了ボタン)


487

iPhoneキーボードの[次へ]ボタンを使用してすべてのテキストフィールドをナビゲートするにはどうすればよいですか?

最後のテキストフィールドでキーボードを閉じる必要があります。

私はIB the Buttons(Next / Done)をセットアップしましたが、今は行き詰まっています。

textFieldShouldReturnアクションを実装しましたが、[次へ]ボタンと[完了]ボタンはキーボードを閉じます。


以下の私の答えを見てください。私の経験では、それは通常与えられる答えよりも優れた、より完全なソリューションです。なぜ誰もがそれを否定的な評価をするのか私にはわかりません。
11

同様の問題がありますが、CordovaでPhonegapを使用して解決する方法はありますか?

回答:


578

Cocoa for Mac OS Xでは、次のレスポンダーチェーンがあり、どのコントロールに次にフォーカスするかをテキストフィールドに尋ねることができます。これにより、テキストフィールド間のタブ移動が機能します。しかし、iOSデバイスにはキーボードがなく、タッチしかないため、このコンセプトはCocoa Touchへの移行を乗り越えていません。

これはとにかく簡単に行うことができますが、2つの仮定があります。

  1. すべての「タブ可能な」UITextFieldは同じ親ビューにあります。
  2. それらの「タブ順」は、タグプロパティによって定義されます。

これを想定すると、textFieldShouldReturn:を次のようにオーバーライドできます。

-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
  NSInteger nextTag = textField.tag + 1;
  // Try to find next responder
  UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
  if (nextResponder) {
    // Found next responder, so set it.
    [nextResponder becomeFirstResponder];
  } else {
    // Not found, so remove keyboard.
    [textField resignFirstResponder];
  }
  return NO; // We do not want UITextField to insert line-breaks.
}

さらにコードを追加すると、前提条件も無視できます。

Swift 4.0

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    // Try to find next responder
    let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!

    if nextResponder != nil {
        // Found next responder, so set it
        nextResponder?.becomeFirstResponder()
    } else {
        // Not found, so remove keyboard
        textField.resignFirstResponder()
    }

    return false
}

テキストフィールドのスーパービューがUITableViewCellの場合、次のレスポンダーは

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!

6
キーボードの上部に「次へ」ボタンと「前へ」ボタンがある場合があります。少なくともブラウザでは。
TimBüthe

31
念のため、この投稿で誤った情報をいくつか明らかにしたいと思います。OS Xでは、タブ順序はNSViewのsetNextKeyView:(setNextResponder :)メソッドではなく設定されます。レスポンダーチェーンはこれとは異なります。これは、「対象外」のアクションを処理するレスポンダーオブジェクトの階層です。たとえば、コントローラーオブジェクトに直接送信されるのではなく、最初のレスポンダーに送信されるアクションです。レスポンダーチェーンは通常、ウィンドウとそのコントローラー、そしてアプリデリゲートまで、ビュー階層に従います。多くの情報のためのグーグルの「レスポンダーチェーン」。
davehayden 2012年

テキストフィールドのスーパービューはになりますUITableViewCell。関連付けUITableViewられたセルにアクセスして、探しているテキストフィールドを含むセルを取得する必要があります。
Michael Mior

13
を追加してUITextFieldDelegate、テキストフィールドのデリゲートを設定することを忘れないでください。
2014

1
ストーリーボード(私の場合)からTAGオプションを設定するためのわかりやすいリマインダー。TextFieldsタグは昇順で手動で設定する必要があります。(最初は0、2番目は1など)このソリューションが機能するために。
Joakim 2016年

170

私が初めて見たときに私を驚かせたはるかにエレガントな解決策があります。利点:

  • フォーカスが次に移動する場所をテキストフィールドが認識しているOSXテキストフィールドの実装により近い
  • タグの設定や使用には依存しません-つまり、この使用例ではIMOは脆弱です
  • UITextFieldおよびUITextViewコントロールの両方、または任意のキーボード入力UIコントロールで機能するように拡張できます。
  • ボイラープレートUITextFieldデリゲートコードでビューコントローラーを乱雑にしない
  • IBとうまく統合され、おなじみのオプションドラッグドロップを使用してコンセントを接続するように構成できます。

IBOutletnextField というプロパティを持つUITextFieldサブクラスを作成します。ヘッダーは次のとおりです。

@interface SOTextField : UITextField

@property (weak, nonatomic) IBOutlet UITextField *nextField; 

@end

そしてここに実装があります:

@implementation SOTextField

@end

ビューコントローラーで、-textFieldShouldReturn:デリゲートメソッドを作成します。

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if ([textField isKindOfClass:[SOTextField class]]) {
        UITextField *nextField = [(SOTextField *)textField nextField];

        if (nextField) {
            dispatch_async(dispatch_get_current_queue(), ^{
                [nextField becomeFirstResponder];
            });
        }
        else {
            [textField resignFirstResponder];
        }
    }

    return YES;
}

IBで、SOTextFieldクラスを使用するようにUITextFieldsを変更します。次に、IBでも、各 'SOTextFields'のデリゲートを 'File's Owner'に設定します(これは、デリゲートメソッドのコードを配置する場所-textFieldShouldReturn)。このデザインのすばらしいところは、任意のtextFieldを右クリックしてSOTextField、次のレスポンダーにしたい次のオブジェクトにnextFieldアウトレットを割り当てることができることです。

IBでのnextFieldの割り当て

さらに、textFieldsのループなどのクールなことを実行して、最後のフォーカスがフォーカスを失った後、最初のフォーカスが再びフォーカスを受け取るようにすることができます。

これは、簡単に自動的に割り当てるように拡張することができるreturnKeyTypeのをSOTextFieldUIReturnKeyNext1以下の事を手動での設定し- nextField割り当てがある場合。


3
私はタグの使用よりもIB統合が本当に好きです-特にIBをIDEの残りの部分とうまく統合する新しいバージョンのXcodeでは-ボイラープレートtextFieldデリゲートコードでコントローラーを混乱させる必要がないのはおまけです。
11

4
タグの方が簡単ですが、この方法の方が優れており、優れたOO設計に適しています
Brain2000

1
私はこのソリューションが本当に好きですが、各テキストフィールドをの個別のセルビューで使用していますUITableView。私は持っているもののIBOutlet、各テキストフィールドのビューコントローラのプロパティを、私がリンクさせていないnextFieldファイルの所有者にこれらのプロパティにプロパティを。このシナリオでどのように機能させるかについてのアドバイスはありますか?
ジェイイマーマン

1
@JayImerman IBでは、セル間でコンセントを接続することはできません。したがって、このシナリオではIBは使用できません。ただし、コードを使用してnextFieldプロパティを接続することもできます。textField1.nextField = textField2; textField2.nextField = textField3;、など
memmons

2
この答えがxcodeとiosの新しいバージョンに更新される可能性はありますか?
lostintranslation 2014

82

委任のないものは次のとおりです。

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)

ObjC:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];

(ほとんど不明)UIControlEventEditingDidEndOnExit UITextFieldアクションを使用して動作します。

これをストーリーボードに簡単に接続できるので、委任コードは必要ありません。

編集:実際には、これをストーリーボードに接続する方法がわかりません。 becomeFirstResponder残念なことに、このコントロールイベントに対して提供されたアクションではないようです。それでも、すべてのテキストフィールドをViewControllerの単一のアクションにフックしてbecomeFirstResponder、送信者に基づいてどのtextFieldを決定することができます(ただし、上記のプログラムによるソリューションほどエレガントではないため、IMOは上記のコードでそれを行いますviewDidLoad)。


18
非常にシンプルで効果的なソリューション。チェーンの最後のものもトリガーでき[tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];ます。@mxclに感謝します。
msrdjan 2014

ストーリーボードでこれをどのように行いますか?
Pedro Borges

これがおそらくここでの最良の答えです。
ダスピアニスト

これはSwift 2でも完全に機能しました。これは、デリゲートを追加するよりもはるかに単純で迅速なソリューションです。
2015年

2
Swift 4:txtFirstname.addTarget(txtLastname、action:#selector(becomeFirstResponder)、for:UIControlEvents.editingDidEndOnExit)
realtimez

78

これがこの問題に対する私の解決策です。

これを解決するために(そして、タグに頼ることを嫌うので)、UITextFieldオブジェクトにカスタムプロパティを追加することにしました。つまり、UITextFieldに次のようなカテゴリを作成しました。

UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

UITextField + Extended.m

#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

今、私はそれをどのように使用するかです:

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メソッドを実装します。

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {

    UITextField *next = theTextField.nextTextField;
    if (next) {
        [next becomeFirstResponder];
    } else {
        [theTextField resignFirstResponder];
    }

    return NO; 
}

これで、UITextFieldのリンクされたリストのようなものができました。

お役に立てれば幸いです。


11
これが最良のソリューションであり、答えとして選択する必要があります。
Giovanni

4
このソリューションはうまくいきました。変更したのは、nextTextFieldとIBOutletを作成することだけだったので、ストーリーボード内からチェーンをセットアップできました。
zachzurn 2013年

お気に入りの答えを保存できたら、これもその1つです。きれいな解決策をありがとう!
Matt Becker

1
IBを使用する場合は、これらのプロパティをIBOutletsとして設定することもできます。。名前の衝突に注意してください-Appleは最終的にこれをUIResponderに追加する可能性があります。
Jasper Blues

このソリューションはインターフェイスビルダーと互換性がありますか?プログラムでnextTextFieldを設定する代わりに、IBOutletsとストーリーボード参照を使用できるということですか?IBでは、UITextFieldクラスをカテゴリに設定できないようです。
Pedro Borges

46

これを特に簡単にするためにmxclの回答を適用するSwift拡張(TravelerによりSwift 2.3に適応):

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for i in 0 ..< fields.count - 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit)
    }
}

使い方は簡単です:

UITextField.connectFields([field1, field2, field3])

拡張機能は、最後のフィールドを除くすべてのフィールドで[戻る]ボタンを[次へ]に設定し、最後のフィールドを[完了]に設定します。これらをタップすると、フォーカスが移動/キーボードが非表示になります。

スウィフト<2.3

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for var i = 0; i < fields.count - 1; i += 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
    }
}

SWIFT 3: このように使用-

UITextField.connectFields(fields: [field1, field2])

Extension:
    extension UITextField {
        class func connectFields(fields:[UITextField]) -> Void {
            guard let last = fields.last else {
                return
            }
            for i in 0 ..< fields.count - 1 {
                fields[i].returnKeyType = .next
                fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
            }
            last.returnKeyType = .go
            last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
        }
    }

5
ところで、Doneにアクションを実行させたい場合は、View Controllerをに準拠させUITextFieldDelegate、実装させるだけfunc textFieldDidEndEditing(textField: UITextField)です。もしそうなら、早く帰ってくださいtextField != field3
ライアン

25

NextResponderTextFieldを使用するのがより一貫性があり堅牢な方法ですview.tag。 デリゲートを設定したり、を使用したりすることなく、インターフェイスビルダーから完全に構成できます。

あなたがする必要があるのは

  1. あなたのクラスタイプを設定UITextFieldしますNextResponderTextField ここに画像の説明を入力してください
  2. 次にnextResponderField、次のレスポンダを指すようにのアウトレットを設定します。これは、何でもUITextField、どのUIResponderサブクラスでもかまいません。UIButtonにすることもできます。ライブラリはTouchUpInside、ボタンが有効になっている場合にのみボタンのイベントをトリガーするのに十分スマートです。 ここに画像の説明を入力してください ここに画像の説明を入力してください

動作中のライブラリは次のとおりです。

ここに画像の説明を入力してください


はい。Swift 3.1の非常にエレガントなソリューションで非常にうまく機能します。IBがそのままの状態で機能するはずです。
リチャード

質問は聞き取れませんでした。
mohamede1945 2017

1
NextResponderTextFieldが役に立ちました。それは迅速かつ使いやすく、1つのファイルだけで、大騒ぎすることなく、私が必要とするものを正確に実行しました。
Pat McG 2017年

これはうまくいきます!注:例のように「次へ」と「完了」を表示するには、Returnキーを手動で変更する必要がありました。
Mike Miller

14

私は、Anth0とAnswerbotによってすでに提案されているOOソリューションが好きです。ただし、私は迅速で小さなPOCに取り組んでいたため、サブクラスやカテゴリで乱雑にしたくありませんでした。

別の簡単な解決策は、フィールドのNSArrayを作成し、[次へ]を押したときに次のフィールドを検索することです。OOソリューションではありませんが、すばやく簡単に実装できます。また、順序を一目で確認および変更できます。

これが私のコードです(このスレッドの他の答えに基づいて構築されています):

@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は、キーボードからフィールドを覆い隠すためにスクロールが必要な場合に備えて、アクティブなフィールドを追跡します。同様のコードがある場合は、最初のレスポンダを変更する前に、必ずactiveFieldを割り当ててください。ファーストレスポンダの変更はすぐに行われ、KeyboardWasShownイベントがすぐに発生します。

いいね!シンプルで、コードはすべて1つのモデルに含まれています。AnthOもとても良いです。
Seamus 14

このコードでは、保持サイクルのリスクがあることに注意してください。fieldArrayは、すべてのIBOutletで使用される弱い参照を配列自体の強い参照に変換しています。
マイケルロング

11

UIControlのカテゴリを使用したタブの実装は次のとおりです。このソリューションには、MichaelとAnth0のメソッドのすべての利点がありますが、すべてのUIControlsだけでなく、UITextField s。また、Interface Builderおよびストーリーボードとシームレスに連携します。

ソースおよびサンプルアプリ: UIControlsWithTabbingのGitHubリポジトリ

使用法:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField transferFirstResponderToNextControl];
    return NO;
}

Interface BuilderでのnextControlの割り当て

ヘッダ:

//
// UIControl+NextControl.h
// UIControlsWithTabbing
//

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

@property (nonatomic, weak) IBOutlet UIControl *nextControl;

- (BOOL)transferFirstResponderToNextControl;

@end

実装:

#import "UIControl+NextControl.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UIControl (NextControl)

- (UIControl *)nextControl
{
    return objc_getAssociatedObject(self, &defaultHashKey);
}

- (void)setNextControl:(UIControl *)nextControl
{
    objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)transferFirstResponderToNextControl
{
    if (self.nextControl)
    {
        [self.nextControl becomeFirstResponder];

        return YES;
    }

    [self resignFirstResponder];

    return NO;
}

@end

1
時々、次のウィジェットはコントロールではなくUITextViewであり、そこにもタブを配置したい場合があります。タイプをUITextFieldからUIResponderに変更することを検討してください。
Pedro Borges

1
素晴らしく、素晴らしいドキュメントで動作します。数時間前にこの質問にスクロールダウンしたいと思います。:)
ハビエルカディス

10

私は多くのコードを試しましたが、最後に、これはSwift 3.0 Latest [March 2017]でうまくいきました

ViewControllerクラスが継承する必要がありますUITextFieldDelegate。このコードの作業を行うため。

class ViewController: UIViewController,UITextFieldDelegate  

適切なタグ番号のテキストフィールドを追加します。このタグ番号は、割り当てられた増分タグ番号に基づいて適切なテキストフィールドに制御を移すために使用されます。

override func viewDidLoad() {
    userNameTextField.delegate = self
    userNameTextField.tag = 0
    userNameTextField.returnKeyType = UIReturnKeyType.next
    passwordTextField.delegate = self
    passwordTextField.tag = 1
    passwordTextField.returnKeyType = UIReturnKeyType.go
}

上記のコードでは、アプリケーションが値を変更することに基づいて他のオプションもあるので、returnKeyType = UIReturnKeyType.nextwhereはキーパッドのリターンキーを表示しNextますJoin/Go

これ textFieldShouldReturnは、UITextFieldDelegateによって制御されるメソッドであり、ここでは、タグ値の増分に基づく次のフィールド選択があります。

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
        nextField.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
        return true;
    }
    return false
 }

これは、入力フィールドがの場合を除いて機能しますUITextView。これを組み合わせて行う方法はありますか?
T9b 2017年

@ T9b正確に何が必要ですか?アプリでどのタイプコントロールを使用していますか?
BHUVANESH MOHANKUMAR

テキスト入力には2つのタイプがあります。1つはUITextField(1行のテキストを入力)、もう1つはUITextView(複数行のテキストを入力)です。UITextView明らかにこのコードには応答しませんが、フォームで使用できます。
T9b 2017年

はい、UITextFieldのコードのこの部分のみです。UITextViewのイベントを変更してみましたか?
BHUVANESH MOHANKUMAR

9

1つのテキストフィールドを終了した後、[otherTextFieldがFirstResponder]を呼び出すと、次のフィールドにフォーカスが移ります。

編集時に見やすいように画面をスクロールしたり、テキストフィールドの位置を調整したりすることも多いため、これは実際に対処するのが難しい問題です。さまざまな方法でテキストフィールドに出入りする多くのテストを行い、早期に終了するようにしてください(通常、次のフィールドに移動する代わりに、通常は「完了」でキーボードを閉じるオプションをユーザーに提供しますナビゲーションバー)



7

「完了」ボタンが押されたときにキーボードを閉じる非常に簡単な方法は次のとおりです。

ヘッダーに新しいIBActionを作成します

- (IBAction)textFieldDoneEditing:(id)sender;

実装ファイル(.mファイル)に次のメソッドを追加します。

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
  [sender resignFirstResponder];
}

次に、IBActionをテキストフィールドにリンクするようになったら、「Did End On Exit」イベントにリンクします。


1
どういたしまして、テキストフィールドを移動することはできませんが、キーボードを閉じて別のフィールドを選択することができます。
jcrowson 2012

7

最初にキーボードのリターンキーをxibに設定します。それ以外の場合は、次のコードを記述できますviewdidload

passWord.returnKeyType = UIReturnKeyNext;

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if(textField == eMail) {
        [textField resignFirstResponder];
        [userName becomeFirstResponder];
    }
    if (textField==userName) {
        [textField resignFirstResponder];
        [passWord becomeFirstResponder];
    }
    if (textField==passWord) {
        [textField resignFirstResponder];
        [country becomeFirstResponder];
    }
    if (textField==country) {
        [textField resignFirstResponder];
    }
    return YES;
}

7

ここで回答の1つが1つの単純な概念を理解できていないことに驚いています。それはコントローラーです次の最初のレスポンダーを作成するためのコントロールを決定するの仕事です。

また、ほとんどの回答は前方にナビゲートする場合にのみ適用されますが、ユーザーは後方に移動することもできます。

これが私が思いついたものです。フォームはビューコントローラーで管理する必要があり、ビューコントローラーはレスポンダーチェーンの一部です。したがって、次のメソッドを完全に自由に実装できます。

#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の)代わりにコントローラレベルでロジックを管理することができる、のは、正直言ってみましょうところ、ある適切な場所そうします。


最も関連性の高い回答を見つけましたが、おそらくあなたが解決できる面白いことがわかりました。UIViewControllerによって管理されていないUITextFieldsを使用する場合、タブキーを押すと、デフォルトで次のUITextFieldが最初のレスポンダーになります。ただし、それぞれがVCによって管理されている場合、これは当てはまりません。私のVCは何もしていませんが、組み込みのタブ機能はなくなりました。VCのkeyCommandsがビューのレスポンダーチェーンに追加されることに気づきました。これにより、VCを使用している場合、keyCommandsを実装することが必須であると私は信じるようになります。
Joey Carson

4

前/次のボタン機能を実装したい場合に備えて、PeyloWの回答に追加しました。

- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender 
{
    NSInteger nextTag;
    UITextView *currentTextField = [self.view findFirstResponderAndReturn];

    if (currentTextField != nil) {
        // I assigned tags to the buttons.  0 represent prev & 1 represents next
        if (sender.tag == 0) {
            nextTag = currentTextField.tag - 1;

        } else if (sender.tag == 1) {
            nextTag = currentTextField.tag + 1;
        }
    }
    // Try to find next responder
    UIResponder* nextResponder = [self.view viewWithTag:nextTag];
    if (nextResponder) {
        // Found next responder, so set it.
        // I added the resign here in case there's different keyboards in place.
        [currentTextField resignFirstResponder];
        [nextResponder becomeFirstResponder];
    } else {
        // Not found, so remove keyboard.
        [currentTextField resignFirstResponder];

    }
}

このようにUIViewをサブクラス化する場所:

@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
    for (UITextView *subView in self.subviews) {
        if (subView.isFirstResponder){
            return subView;
        }
    }
    return nil;
}
@end

4

みなさんこんにちはこれを見ください

- (void)nextPrevious:(id)sender
{

  UIView *responder = [self.view findFirstResponder];   

  if (nil == responder || ![responder isKindOfClass:[GroupTextField class]]) {
    return;
  }

  switch([(UISegmentedControl *)sender selectedSegmentIndex]) {
    case 0:
      // previous
      if (nil != ((GroupTextField *)responder).previousControl) {
        [((GroupTextField *)responder).previousControl becomeFirstResponder];
        DebugLog(@"currentControl: %i previousControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).previousControl.tag);
      }
      break;
    case 1:
      // next
      if (nil != ((GroupTextField *)responder).nextControl) {
        [((GroupTextField *)responder).nextControl becomeFirstResponder];
        DebugLog(@"currentControl: %i nextControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).nextControl.tag);
      }     
      break;    
  }
}

UIView * responderの代わりに、UIRespoder * responderを使用するほうがより正確です
Pedro Borges


4

このGNTextFieldsCollectionManagerを処理するときに、新しいポッドを作成しました。次/最後のtextField問題を自動的に処理し、非常に使いやすいです。

[[GNTextFieldsCollectionManager alloc] initWithView:self.view];

ビュー階層に表示される(またはタグによって)ソートされたすべてのテキストフィールドを取得します。または、textFieldsの独自の配列を指定できます。


4

Swift 3.1での解決策、テキストフィールドIBOutletsを接続した後、viewDidLoadでテキストフィールドデリゲートを設定し、textFieldShouldReturnでアクションをナビゲートします。

class YourViewController: UIViewController,UITextFieldDelegate {

        @IBOutlet weak var passwordTextField: UITextField!
        @IBOutlet weak var phoneTextField: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            self.passwordTextField.delegate = self
            self.phoneTextField.delegate = self
            // Set your return type
            self.phoneTextField.returnKeyType = .next
            self.passwordTextField.returnKeyType = .done
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool{
            if textField == self.phoneTextField {
                self.passwordTextField.becomeFirstResponder()
            }else if textField == self.passwordTextField{
                // Call login api
                self.login()
            }
            return true
        }

    }

4

誰かがこのようにしたい場合。これは問題となっている要件に最も近いと思います

ここに画像の説明を入力してください

これは私がこれをどのように実装したかです

を使用して、設定する各テキストフィールドにアクセサリビューを追加します。

func setAccessoryViewFor(textField : UITextField)    {
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    toolBar.isTranslucent = true
    toolBar.sizeToFit()

    // Adds the buttons

    // Add previousButton
    let prevButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(previousPressed(sender:)))
    prevButton.tag = textField.tag
    if getPreviousResponderFor(tag: textField.tag) == nil {
        prevButton.isEnabled = false
    }

    // Add nextButton
    let nextButton = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(nextPressed(sender:)))
    nextButton.tag = textField.tag
    if getNextResponderFor(tag: textField.tag) == nil {
        nextButton.title = "Done"
    }

    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    toolBar.setItems([prevButton,spaceButton,nextButton], animated: false)
    toolBar.isUserInteractionEnabled = true
    textField.inputAccessoryView = toolBar
}

タップを処理するには、次の関数を使用します

func nextPressed(sender : UIBarButtonItem) {
    if let nextResponder = getNextResponderFor(tag: sender.tag) {
        nextResponder.becomeFirstResponder()
    } else {
        self.view.endEditing(true)
    }

}

func previousPressed(sender : UIBarButtonItem) {
    if let previousResponder = getPreviousResponderFor(tag : sender.tag)  {
        previousResponder.becomeFirstResponder()
    }
}

func getNextResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag + 1) as? UITextField
}

func getPreviousResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag - 1) as? UITextField
}

next / prevボタンを応答させたい順序で、textFieldsタグを与える必要があります。


3

私はむしろ:

@interface MyViewController : UIViewController
@property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields;
@end

NIBファイルで、textFieldsをこのinputFields配列に目的の順序でフックします。その後、ユーザーがリターンをタップしたことを報告するUITextFieldのインデックスの簡単なテストを行います。

// for UITextField
-(BOOL)textFieldShouldReturn:(UITextField*)textField {
    NSUInteger index = [_inputFields indexOfObject:textField];
    index++;
    if (index < _inputFields.count) {
        UIView *v = [_inputFields objectAtIndex:index];
        [v becomeFirstResponder];
    }
    return NO;
}

// for UITextView
-(BOOL)textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
    if ([@"\n" isEqualToString:text]) {
        NSUInteger index = [_inputFields indexOfObject:textView];
        index++;
        if (index < _inputFields.count) {
            UIView *v = [_inputFields objectAtIndex:index];
            [v becomeFirstResponder];
        } else {
            [self.view endEditing:YES];
        }
        return NO;
    }
    return YES;
}

3
if(セル== nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    txt_Input = [[UITextField alloc] initWithFrame:CGRectMake(0、10、150、30)];
    txt_Input.tag = indexPath.row + 1;
    [self.array_Textfields addObject:txt_Input]; // ViewDidLoadで可変配列を初期化します
}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{

    int tag =(int)textField.tag;
    UITextField * txt = [self.array_Textfields objectAtIndex:tag];
    [txtになるFirstResponder];
    YESを返します。
}

3

ストーリーボードに約10個以上のUITextFieldがあり、次の機能を有効にする方法は、UITextFieldの配列を作成し、次のUITextFieldをfirstResponderにすることでした。実装ファイルは次のとおりです。

#import "RegistrationTableViewController.h"

@interface RegistrationTableViewController ()
@property (weak, nonatomic) IBOutlet UITextField *fullNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *addressTextField;
@property (weak, nonatomic) IBOutlet UITextField *address2TextField;
@property (weak, nonatomic) IBOutlet UITextField *cityTextField;
@property (weak, nonatomic) IBOutlet UITextField *zipCodeTextField;
@property (weak, nonatomic) IBOutlet UITextField *urlTextField;
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UITextField *confirmPWTextField;

@end
NSArray *uiTextFieldArray;
@implementation RegistrationTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"view did load");
    uiTextFieldArray = @[self.fullNameTextField,self.addressTextField,self.address2TextField,self.cityTextField,self.zipCodeTextField,self.urlTextField,self.usernameTextField,self.emailTextField,self.passwordTextField,self.confirmPWTextField];
    for(UITextField *myField in uiTextFieldArray){
        myField.delegate = self;
    }


}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    long index = [uiTextFieldArray indexOfObject:textField];
    NSLog(@"%ld",index);
    if(index < (uiTextFieldArray.count - 1)){
        [uiTextFieldArray[++index] becomeFirstResponder];
    }else{
        [uiTextFieldArray[index] resignFirstResponder];
    }
    return YES;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

3

これはXamarin.iOS / Monotouchで私にとってはうまくいきました。キーボードボタンを[次へ]に変更し、コントロールを次のUITextFieldに渡して、最後のUITextFieldの後のキーボードを非表示にします。

private void SetShouldReturnDelegates(IEnumerable<UIView> subViewsToScout )
{
  foreach (var item in subViewsToScout.Where(item => item.GetType() == typeof (UITextField)))
  {
    (item as UITextField).ReturnKeyType = UIReturnKeyType.Next;
    (item as UITextField).ShouldReturn += (textField) =>
    {
        nint nextTag = textField.Tag + 1;
        var nextResponder = textField.Superview.ViewWithTag(nextTag);
        if (null != nextResponder)
            nextResponder.BecomeFirstResponder();
        else
            textField.Superview.EndEditing(true); 
            //You could also use textField.ResignFirstResponder(); 

        return false; // We do not want UITextField to insert line-breaks.
    };
  }
}

ViewDidLoad内には次のものがあります。

TextFieldにタグがない場合は、ここで設定します。

txtField1.Tag = 0;
txtField2.Tag = 1;
txtField3.Tag = 2;
//...

そしてただの電話

SetShouldReturnDelegates(yourViewWithTxtFields.Subviews.ToList());
//If you are not sure of which view contains your fields you can also call it in a safer way:
SetShouldReturnDelegates(txtField1.Superview.Subviews.ToList());
//You can also reuse the same method with different containerViews in case your UITextField are under different views.

3

これは、タグを使用せず、ストーリーボードのトリックを使用しない、迅速なシンプルなソリューションです...

この拡張機能を使用してください:

extension UITextField{

    func nextTextFieldField() -> UITextField?{
        //field to return
        var returnField : UITextField?
        if self.superview != nil{
            //for each view in superview
            for (_, view) in self.superview!.subviews.enumerate(){
                //if subview is a text's field
                if view.isKindOfClass(UITextField){
                    //cast curent view as text field
                    let currentTextField = view as! UITextField
                    //if text field is after the current one
                    if currentTextField.frame.origin.y > self.frame.origin.y{
                        //if there is no text field to return already
                        if returnField == nil {
                            //set as default return
                            returnField = currentTextField
                        }
                            //else if this this less far than the other
                        else if currentTextField.frame.origin.y < returnField!.frame.origin.y{
                            //this is the field to return
                            returnField = currentTextField
                        }
                    }
                }
            }
        }
        //end of the mdethod
        return returnField
    }

}

そして、テキストフィールドのデリゲートを使って(たとえば)このように呼び出します。

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}

これに関する1つのコメントは、メソッドはすべてのフィールドが単一のスーパービューで垂直に積み重ねられることを想定しているということです。グループ化されたフィールドは許可されておらず、テキストフィールドが並んでいる場合は機能しません。後者は、サイズクラスを使用して水平ビューのフォームを再配置するときや、フォームがタブレットに表示されるときに特に重要です。
マイケルロング

3

より安全で直接的な方法:

  • テキストフィールドのデリゲートがビューコントローラーに設定されている
  • すべてのテキストフィールドは同じビューのサブビューです
  • テキストフィールドには、進行したい順序でタグが付いています(たとえば、textField2.tag = 2、textField3.tag = 3など)。
  • キーボードの戻るボタンをタップすると、次のテキストフィールドに移動します(これをnextdoneなどに変更できます)。
  • あなたは最後のテキストフィールドの後にキーボードを消したい

Swift 4.1:

extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextTag = textField.tag + 1
        guard let nextTextField = textField.superview?.viewWithTag(nextTag) else {
            textField.resignFirstResponder()
            return false
        }

    nextTextField.becomeFirstResponder()

    return false

    }
}

2

textFieldShouldReturnで、現在クリックしているテキストフィールドが次にクリックしたときに最後のテキストフィールドではないこと、およびキーボードを閉じない場合はチェックする必要があります。


ねえ、これは機能しますが、「次へ」ボタンで次のテキストフィールドにジャンプするにはどうすればよいですか?
phx 2009

2

これは古い投稿ですが、ページランクが高いため、解決策を説明します。

同様の問題がありUIToolbar、セクションのある動的なtableViewで次/前/完了の機能を管理するためのサブクラスを作成することになりました:https : //github.com/jday001/DataEntryToolbar

ツールバーをテキストフィールドのinputAccessoryViewとして設定し、その辞書に追加します。これにより、動的コンテンツであっても、それらを前後に循環させることができます。textFieldナビゲーションが発生したときに独自の機能をトリガーする場合はデリゲートメソッドがありますが、タグの管理やファーストレスポンダステータスを処理する必要はありません。

実装の詳細を支援するために、GitHubリンクにコードスニペットとサンプルアプリがあります。フィールド内の値を追跡するには、独自のデータモデルが必要です。


2

タグを使用せず、nextField / nextTextFieldのプロパティを追加せずに、これを試してTABをエミュレートできます。「testInput」は現在のアクティブなフィールドです。

if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange([textInput.superview.subviews indexOfObject:textInput]+1,
                  [textInput.superview.subviews count]-[textInput.superview.subviews indexOfObject:textInput]-1)]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange(0,
                  [textInput.superview.subviews indexOfObject:textInput])]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];

2

マイケルG.エモンズの回答を1年ほど使用していますが、うまくいきます。最近、resignFirstResponderを呼び出してすぐにfirstFirstResponderを呼び出すと、キーボードが「グリッチ」し、消えてすぐに表示されることに気付きました。nextFieldが利用可能な場合、resignFirstResponderをスキップするように彼のバージョンを少し変更しました。

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{ 

    if([textField isKindOfClass:[NRTextField class]])
    {
        NRTextField * nText =(NRTextField *)textField;
        if([nText nextField]!= nil){
            dispatch_async(dispatch_get_main_queue()、
                           ^ {[[nText nextField] preventFirstResponder]; });

        }
        そうしないと{
            [textField resignFirstResponder];
        }
    }
    そうしないと{
        [textField resignFirstResponder];
    }

    trueを返します。

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.