web-dev-qa-db-ja.com

テキストフィールド内を移動する方法(Next / Doneボタン)

IPhoneキーボードの[次へ]ボタンを使用して、すべてのテキストフィールドを移動する方法はありますか。

最後のテキストフィールドはキーボードを閉じるはずです。

私はIBのボタンを設定しました(Next/Done)が、今立ち往生しています。

TextFieldShouldReturnアクションを実装しましたが、[Next]ボタンと[Done]ボタンでキーボードを閉じます。

474
phx

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

これは、2つの仮定を使用して、とにかく簡単に実行できます。

  1. すべての "tabbable" UITextFieldは同じ親ビューにあります。
  2. それらの "タブオーダー"はtagプロパティによって定義されます。

これを前提として、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!
566
PeyloW

もっともっと洗練された解決策がありますが、それを初めて見たときには気がつかなかった。利点:

  • 次にフォーカスを向けるべき場所をテキストフィールドが知っているOSXテキストフィールドの実装により近い
  • タグの設定や使用には依存しません - IMOはこのユースケースに対して脆弱です
  • UITextFieldコントロールとUITextViewコントロールの両方、または任意のキーボード入力UIコントロールと連携するように拡張することができます。
  • 定型のUITextFieldデリゲートコードでView Controllerを乱雑にしない
  • IBとうまく統合し、アウトレットを接続するためにおなじみのoption-drag-dropを通して設定することができます。

NextFieldというIBOutletプロパティを持つUITextFieldサブクラスを作成します。これがヘッダです。

@interface SOTextField : UITextField

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

@end

そして、これが実装です。

@implementation SOTextField

@end

View Controllerで、-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クラスを使用するようにUITextFieldを変更します。次に、IBでも、各 'SOTextFields'に 'File's Owner'(デリゲートメソッドのコードを配置する場所 - textFieldShouldReturn)のそれぞれにデリゲートを設定します。このデザインの長所は、textFieldを右クリックしてnextFieldアウトレットを次のレスポンダにしたい次のSOTextFieldオブジェクトに割り当てることができるという点です。

Assigning nextField in IB

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

NextFieldが割り当てられている場合は、これを簡単に拡張してreturnKeyTypeSOTextFieldUIReturnKeyNextに自動的に割り当てることができます。手動で設定する必要が1つ少なくなります。

170
memmons

これは委任なしのものです。

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

目的:

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

(ほとんど未知の)UIControlEventEditingDidEndOnExitUITextFieldアクションを使って動作します。

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

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

78
mxcl

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

これを解決するために(そして、私はタグに頼ることが嫌いなので)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の一種のリンクされたリストを持っていて、それぞれが行の次にいる人を知っています。

それが役立つことを願っています。

78
Anth0

これを特に簡単にするために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])

拡張機能は、最後のフィールドを除くすべてのフィールドでリターンボタンを「次へ」に設定し、最後のフィールドを「完了」に設定し、これらをタップするとフォーカスを移動/キーボードを閉じます。

Swift <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)
        }
    }
45
Amos Joshua

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

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

  1. UITextFieldのクラス型をNextResponderTextFieldに設定します。 enter image description here
  2. 次に、次のレスポンダを指すようにnextResponderFieldのアウトレットを設定します。それは、UITextFieldまたは任意のUIResponderサブクラスにすることができます。これはUIButtonでも構いませんし、ライブラリはボタンのTouchUpInsideイベントが有効になっている場合にのみトリガするのに十分スマートです。 enter image description hereenter image description here

これが実行中のライブラリです。

enter image description here

25
mohamede1945

私はすでにAnth0とAnswerbotによって提案されているOO解決策が好きです。しかし、私はすばやく小さなPOCに取り組んでいたので、サブクラスやカテゴリを使って物事を整理したくはありませんでした。

もう1つの簡単な解決策は、フィールドのNSArrayを作成し、nextを押すと次のフィールドを検索することです。 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イベントが発生します。
14
Marquee

これがUIControlのカテゴリを使ったタブ操作の実装です。このソリューションは、MichaelとAnth0のメソッドのすべての利点を持っていますが、UITextFieldsだけでなく、すべてのUIControlに対して機能します。また、Interface Builderおよびストーリーボードとシームレスに連携します。

ソースとサンプルアプリ: UIControlsWithTabbing用のGitHubリポジトリ

使用法:

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

Assigning nextControl in Interface Builder

ヘッダ:

//
// 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
11
picciano

私は多くのコードを試してみました、そして最後に、これはSwift 3.0 Latest [2017年3月]で私のために働きました

このコードを機能させるには、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.next whereキーパッドが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
 }
10

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

画面をスクロールしたり、テキストフィールドの位置を調整したりして編集時に見やすくすることもよくあるので、これは実際には扱いにくい問題になる可能性があります。さまざまな方法でテキストフィールドに出入りし、また早めに(多くの場合は "Done"で次のフィールドに移動するのではなく、キーボードを閉じるオプションを常に与えるようにして)たくさんのテストを行うようにしてください。ナビゲーションバー)

 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}
9
iKushal

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

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

- (IBAction)textFieldDoneEditing:(id)sender;

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

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

それから、IBActionをtextfieldにリンクするとき - 'Did End On Exit'イベントにリンクします。

7
jcrowson

ここでの答えがいくつかの単純な概念を理解するのに失敗することに驚いています。あなたのアプリのコントロールをナビゲートすることは、ビュー自体がするべきことではありません。次の最初のレスポンダにするコントロールを決めるのはコントローラの仕事です。

また、ほとんどの回答は前方へのナビゲートにしか適用されませんでしたが、ユーザーは後方への移動も望んでいる可能性があります。

だからここに私が思い付いたものです。フォームはView Controllerによって管理する必要があり、View Controllerはレスポンダチェーンの一部です。だから、あなたは完全に次のメソッドを実装するのは自由です:

#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のように)表示したいすべての種類のコントロールをサブクラス化する必要がなく、代わりにコントローラレベルでロジックを管理できることです。ここで、正直なところ、です。 )正しい場所.

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;
}
6
mahesh chowdary

このものを扱うときに私はちょうど新しいPodを作成しました GNTextFieldsCollectionManager 。それは自動的に次の/最後のtextField問題を処理し、そしてとても使いやすいです:

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

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

4
JakubKnejzlik

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
Krishna Thakur

みなさん、こんにちは。 こちらをご覧ください

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

UITextField内の各セル(またはUITableView)に、後で取得できる一意のタグ値を割り当てることに基づく、より洗練されたアプローチを使用してこの問題を解決しようとしました。 activate-next-uitextfield-in- uitableview-ios

これが役に立つことを願っています!

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
James Mertz

これは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の中には、次のものがあります。

あなたのTextFieldsがTagを持っていないなら今それを設定します:

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
Daniele D.

これはSwiftのシンプルなソリューションです。タグを使用せず、ストーリーボードのトリックも不要です。

この拡張子を使うだけです:

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
}
3
Kevin ABRIOUX

私はむしろ好きです:

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

NIBファイルで、私はこのinputFields配列に望みの順序でtextFieldsをフックします。その後、ユーザーがreturnをタップしたことを報告する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
Anticro
if(cell == 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_Textfield addObject:txt_Input]; [10。 // ViewDidLoadの可変配列を初期化する
} 
 
  - (BOOL)textFieldShouldReturn:(UITextField *)textField 
 {
 
 int tag =(int)textField.tag; 
 UITextField * txt = [self.array_Textfields objectAtIndex:tag]; 
 [txt becomeFirstResponder]; 
 YES; 
 ]} 
3
Rebecca Curties

私のストーリーボードには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

誰かがこのように欲しいなら。私はこれが問題に求められている要件に最も近いと思います

enter image description here

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

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

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
}

TextFieldsタグを、next/prevボタンを反応させたい順番に並べる必要があります。

3
Mohammad Sadiq

タグを使わずに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
Cœur

textFieldShouldReturnでは、現在クリックしているテキストフィールドが、次にクリックするときに最後のものではないこと、およびキーボードを消さないことを確認してください。

2
Daniel

私は約1年間Michael G. Emmonsの答えを使ってきました。私は最近resignFirstResponderを呼び出してからすぐにfirstFirstResponderを呼び出すとキーボードが「グリッチ」し、消えてすぐに表示される可能性があることに最近気付きました。 nextFieldが利用可能であればresignFirstResponderをスキップするように私は彼のバージョンをわずかに変更しました。

  - (BOOL)textFieldShouldReturn:(UITextField *)textField 
 {
 if([textField isKindOfClass:[NRTextFieldクラス]])
 { 
 NRTextField * nText =(NRTextField *)textField; 
 if([nText nextField]!= nil){
 dispatch_async(dispatch_get_main_queue()、
 ^ {[ [nText nextField] becomeFirstResponder];}); 
 
} 
 else {
 [textField resignFirstResponder]; 
} 
} 
 else {
 [textField resignFirstResponder]; 
} 
 
 trueを返します; 
 
} 
2
arinmorf

これはAnth0の答えのSwift 3バージョンです。私はここに投稿して、Swiftの開発者が彼のすばらしい答えを利用したいと思っているのを助けます。関連付けられたオブジェクトを設定するときに、戻りキーの種類として "Next"を追加することができました。

extension UITextField {

  @nonobjc static var NextHashKey: UniChar = 0

  var nextTextField: UITextField? {
    get {
      return objc_getAssociatedObject(self, 
        &UITextField.NextHashKey) as? UITextField
    }
    set(next) {
     self.returnKeyType = UIReturnKeyType.next
     objc_setAssociatedObject(self,
      &UITextField.NextHashKey,next,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
}

UITextFieldのリストを巡回するために上記のコードを使用する可能性を示す別の拡張です。

extension UIViewController: UITextFieldDelegate {
 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
   guard let next = textField.nextTextField else {
     textField.resignFirstResponder()
     return true
   }

    next.becomeFirstResponder()
    return false
  }
}

そして、あなたのViewControllerまたはどこでも、あなたはそのようにあなたのテキストフィールドを設定することができます...

@IBOutlet fileprivate weak var textfield1: UITextField!
@IBOutlet fileprivate weak var textfield2: UITextField!
@IBOutlet fileprivate weak var textfield3: UITextField!

...

[textfield1, textfield2, textfield3].forEach{ $0?.delegate = self }

textfield1.nextTextField = textfield2
textfield2.nextTextField = textfield3
// We don't assign a nextTextField to textfield3 because we want 
// textfield3 to be the last one and resignFirstResponder when 
// the return button on the soft keyboard is tapped.
2
Justin Wright

これは古い投稿ですが、ページランクが高いため、このソリューションを使用します。

私は同様の問題を抱えていて、セクションを持つ動的tableViewでnext/previous/done機能を管理するためにUIToolbarのサブクラスを作成することになりました: https://github.com/jday001/DataEntryToolbar

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

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

2
jday

これを行うにはIQKeyboardManagerライブラリを使用できます。それはあらゆることを処理します、あなたは追加の設定を必要としません。それをインストールするためにCocoaPodsを通して利用可能です。

pod 'IQKeyboardManager'

またはデモプロジェクトからIQKeyBoardManagerディレクトリをプロジェクトにドラッグアンドドロップするだけです。それでおしまい。あなたは からIQKeyBoardManagerディレクトリを見つけることができますhttps://github.com/hackiftekhar/IQKeyboardManager

2

より安全で直接的な方法です。

  • テキストフィールドのデリゲートはあなたのView Controllerに設定されます。
  • すべてのテキストフィールドは同じビューのサブビューです。
  • テキストフィールドには、進んでいく順序でタグがあります(たとえば、textField2.tag = 2、textField3.tag = 3など)。
  • キーボードのreturnボタンをタップすると、次のテキストフィールドに移動します(これをnextに変更できます。完了など)
  • 最後のテキストフィールドの後でキーボードに表示させたくない場合

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

    }
}
1
jason z

Swift 3 SolutionUITextField'sの順序配列を使用

func nextTextField() {
    let textFields = // Your textfields array

    for i in 0 ..< textFields.count{
        if let textfield = textFields[i], textfield.isFirstResponder{
            textfield.resignFirstResponder()
            if i+1 < textFields.count, let nextextfield = textFields[i+1]{
                nextextfield.becomeFirstResponder()
                return
            }
        }
    }
}
0
Maor