Cara menavigasi melalui bidang teks (Tombol Berikutnya / Selesai)

487

Bagaimana saya bisa menavigasi semua bidang teks saya dengan Tombol "Next" pada Keyboard iPhone?

Bidang teks terakhir harus menutup Keyboard.

Saya sudah menyiapkan IB the Buttons (Next / Done) tapi sekarang saya macet.

Saya menerapkan tindakan textFieldShouldReturn tapi sekarang Tombol Berikutnya dan Selesai menutup Keyboard.

phx
sumber
Silakan lihat jawaban saya di bawah ini. Dalam pengalaman saya, ini adalah solusi yang lebih baik, lebih lengkap, daripada jawaban yang biasanya diberikan. Saya tidak tahu mengapa ada orang yang memberikan peringkat negatif.
memmons
Saya memiliki masalah serupa tetapi dengan Cordova, Phonegap Apakah ada cara untuk menyelesaikannya?

Jawaban:

578

Di Cocoa untuk Mac OS X, Anda memiliki rantai responden berikutnya, di mana Anda dapat menanyakan bidang teks kontrol apa yang harus difokuskan selanjutnya. Inilah yang membuat tabing antar bidang teks berfungsi. Tetapi karena perangkat iOS tidak memiliki keyboard, hanya sentuhan, konsep ini tidak selamat dari transisi ke Cocoa Touch.

Ini dapat dengan mudah dilakukan, dengan dua asumsi:

  1. Semua "tabbable" UITextFields berada pada tampilan induk yang sama.
  2. "Tab-order" mereka ditentukan oleh properti tag.

Dengan asumsi ini Anda dapat mengganti textFieldShouldReturn: karena ini:

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

Tambahkan beberapa kode lagi, dan asumsi tersebut dapat diabaikan juga.

Cepat 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
}

Jika superview dari bidang teks akan menjadi UITableViewCell maka responden selanjutnya adalah

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
PeyloW
sumber
6
Terkadang ada tombol "berikutnya" dan "sebelumnya" di atas keyboard. Paling tidak di browser.
Tim Büthe
31
Hanya untuk menjadi sombong, saya ingin menghapus beberapa informasi yang salah dalam posting ini. Pada OS X, urutan tab diatur melalui setNextKeyView NSView: (tidak setNextResponder :) metode. Rantai responden terpisah dari ini: ini adalah hierarki objek responden yang menangani tindakan "tidak ditargetkan", misalnya tindakan yang dikirim ke responden pertama alih-alih langsung ke objek pengontrol. Rantai responden biasanya mengikuti hierarki tampilan, hingga ke jendela dan pengontrolnya, dan kemudian delegasi aplikasi. Google "rantai responden" untuk banyak info.
davehayden
Superview bidang teks akan menjadi a UITableViewCell. Anda perlu mengakses UITableViewsel terkait dan mendapatkan sel yang berisi bidang teks yang Anda cari.
Michael Mior
13
Jangan lupa untuk menambahkan UITextFieldDelegatedan mengatur delegasi bidang teks.
Sal
1
Pengingat ramah untuk mengatur opsi TAG dari storyboard (dalam kasus saya). Anda harus secara manual mengatur tag TextFields dalam urutan menaik. (Pertama adalah 0, kedua adalah 1 dll.) Agar solusi ini berfungsi.
Joakim
170

Ada solusi yang jauh lebih elegan yang mengejutkan saya saat pertama kali saya melihatnya. Manfaat:

  • Lebih dekat dengan implementasi textfield OSX di mana textfield tahu kemana fokus selanjutnya
  • Tidak mengandalkan pengaturan atau penggunaan tag - yang, IMO rapuh untuk kasus penggunaan ini
  • Dapat diperluas untuk bekerja dengan baik UITextFielddan UITextViewkontrol - atau kontrol UI entri keyboard
  • Tidak mengacaukan pengontrol tampilan Anda dengan kode delegasi UITextField boilerplate
  • Terintegrasi dengan baik dengan IB dan dapat dikonfigurasikan melalui opsi-seret-jatuhkan yang dikenal untuk menghubungkan outlet.

Buat subclass UITextField yang memiliki IBOutletproperti yang disebut nextField. Inilah tajuknya:

@interface SOTextField : UITextField

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

@end

Dan inilah implementasinya:

@implementation SOTextField

@end

Di pengontrol tampilan Anda, Anda akan membuat -textFieldShouldReturn:metode delegasi:

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

Di IB, ubah UITextFields Anda untuk menggunakan SOTextFieldkelas. Selanjutnya, juga di IB, atur delegasi untuk masing-masing 'SOTextFields'to' Pemilik File '(yang tepat di mana Anda meletakkan kode untuk metode delegasi - textFieldShouldReturn). Keindahan desain ini adalah bahwa sekarang Anda cukup mengklik kanan pada TextField apa pun dan menetapkan outlet nextField ke SOTextFieldobjek berikutnya yang Anda inginkan untuk menjadi responden berikutnya.

Menetapkan Field berikutnya di IB

Selain itu, Anda dapat melakukan hal-hal keren seperti loop the textFields sehingga setelah yang terakhir kehilangan fokus, yang pertama akan menerima fokus lagi.

Ini dengan mudah dapat diperpanjang secara otomatis menetapkan returnKeyTypesatu SOTextFielduntuk UIReturnKeyNextjika ada nextField ditugaskan - satu hal yang kurang secara manual configure.

memmons
sumber
3
Saya sangat suka integrasi IB lebih dari menggunakan tag - terutama dengan versi Xcode yang lebih baru yang mengintegrasikan IB dengan baik dengan sisa IDE - dan tidak harus mengacaukan controller saya dengan kode delegasi boilerplate textField adalah bonus.
memmons
4
Tag lebih mudah, tetapi cara ini lebih baik, dan lebih sesuai untuk desain OO yang bagus
Brain2000
1
Saya sangat menyukai solusi ini, tetapi saya menggunakan setiap bidang teks dalam tampilan sel yang terpisah di a UITableView. Meskipun saya memiliki IBOutletproperti pada pengontrol tampilan untuk setiap bidang teks, itu tidak membiarkan saya menautkan nextFieldproperti ke properti pada Pemilik File. Adakah saran bagaimana saya bisa membuatnya bekerja dalam skenario ini?
Jay Imerman
1
@JayImerman IB tidak akan memungkinkan Anda untuk menghubungkan outlet antar sel. Jadi, IB akan menjadi no-go untuk skenario ini. Namun, Anda bisa menghubungkan properti NextField menggunakan kode. textField1.nextField = textField2; textField2.nextField = textField3;, dll.
memmons
2
Adakah kemungkinan jawaban ini dapat diperbarui untuk versi xcode dan ios yang lebih baru?
lostintranslation
82

Inilah satu tanpa delegasi:

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];

Bekerja menggunakan tindakan (kebanyakan tidak diketahui) UIControlEventEditingDidEndOnExit UITextField.

Anda juga dapat dengan mudah menghubungkan ini di storyboard, sehingga tidak ada delegasi atau kode yang diperlukan.

Sunting: sebenarnya saya tidak tahu bagaimana cara menghubungkan ini di storyboard. becomeFirstRespondertampaknya tidak menjadi tindakan yang ditawarkan untuk acara kontrol ini, yang sangat disayangkan. Namun, Anda dapat mengaitkan semua bidang teks Anda ke satu tindakan di ViewController Anda yang kemudian menentukan textField mana yang akan becomeFirstResponderdidasarkan pada pengirim (meskipun kemudian itu tidak seanggun solusi program di atas sehingga IMO melakukannya dengan kode di atas viewDidLoad).

mxcl
sumber
18
Solusi yang sangat sederhana dan efektif. Yang terakhir dalam rantai bahkan dapat memicu mis [tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];. Terima kasih @mxcl.
msrdjan
Bagaimana Anda melakukan ini di storyboard?
Pedro Borges
Ini mungkin jawaban terbaik di sini.
daspianist
Ini bekerja dengan baik untuk saya bahkan di Swift 2. Ini adalah solusi yang jauh lebih sederhana dan lebih cepat daripada menambahkan delegasi.
ton
2
Swift 4: txtFirstname.addTarget (txtLastname, action: #selector (menjadiFirstResponder), untuk: UIControlEvents.editingDidEndOnExit)
realtimez
78

Ini solusi saya untuk masalah ini.

Untuk mengatasi ini (dan karena saya benci mengandalkan tag untuk melakukan hal-hal), saya memutuskan untuk menambahkan properti kustom ke objek UITextField. Dengan kata lain saya membuat kategori di UITextField seperti ini:

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

Sekarang, inilah cara saya menggunakannya:

UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield

textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;

Dan mengimplementasikan metode textFieldShouldReturn:

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

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

    return NO; 
}

Saya sekarang memiliki semacam daftar UITextField yang ditautkan, masing-masing mengetahui siapa yang berada di baris selanjutnya.

Semoga ini bisa membantu.

Anth0
sumber
11
Ini adalah solusi terbaik dan harus dipilih sebagai jawabannya.
Giovanni
4
Solusi ini bekerja untuk saya, satu-satunya hal yang saya ubah adalah membuat NextTextField dan IBOutlet sehingga saya bisa mengatur rantai dari dalam Storyboard saya.
zachzurn
Jika saya bisa menyimpan jawaban favorit, ini akan menjadi salah satunya. Terima kasih atas solusi bersihnya!
Matt Becker
1
Perhatikan bahwa jika IB adalah milik Anda, Anda juga dapat mengatur properti ini sebagai IBOutlets. . hati-hati dengan benturan nama - Apple akhirnya dapat menambahkan ini ke UIResponder.
Jasper Blues
Apakah solusi ini kompatibel dengan pembangun antarmuka? Maksud saya, bisakah menggunakan IBOutlets dan referensi storyboard alih-alih mengatur nextTextField secara terprogram? IB sepertinya tidak memungkinkan Anda untuk mengatur kelas UITextField ke kategori.
Pedro Borges
46

Ekstensi cepat yang menerapkan jawaban mxcl untuk menjadikannya sangat mudah (disesuaikan dengan swift 2.3 oleh Traveler):

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)
    }
}

Mudah digunakan:

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

Ekstensi akan mengatur tombol kembali ke "Berikutnya" untuk semua kecuali bidang terakhir dan ke "Selesai" untuk bidang terakhir, dan alihkan fokus / abaikan keyboard saat ini diketuk.

Cepat <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: gunakan seperti ini -

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)
        }
    }
Amos Joshua
sumber
5
BTW, jika Anda ingin maka Selesai melakukan tindakan, Anda hanya bisa memiliki View Controller Anda sesuai UITextFieldDelegate, dan mengimplementasikan func textFieldDidEndEditing(textField: UITextField). Kembalilah lebih awal jika textField != field3.
Ryan
25

Cara yang lebih konsisten dan kuat adalah dengan menggunakan NextResponderTextField Anda dapat mengkonfigurasinya secara total dari pembuat antarmuka tanpa perlu mengatur delegasi atau menggunakan view.tag.

Yang perlu Anda lakukan adalah

  1. Atur tipe kelas Anda UITextFieldmenjadiNextResponderTextField masukkan deskripsi gambar di sini
  2. Kemudian atur outlet nextResponderFieldto point ke responder berikutnya itu bisa berupa apa saja UITextFieldatau UIRespondersubclass apa pun . Ini juga bisa menjadi UIButton dan pustaka cukup pintar untuk memicu TouchUpInsideacara tombol hanya jika diaktifkan. masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Berikut adalah perpustakaan yang sedang beraksi:

masukkan deskripsi gambar di sini

mohamede1945
sumber
Iya. Bekerja sangat baik dengan Swift 3.1, solusi yang sangat elegan, hanya bagaimana IB seharusnya bekerja di luar kotak.
Richard
Maaf, saya tidak mendapatkan pertanyaan.
mohamede1945
1
NextResponderTextField bekerja untuk saya. Itu cepat dan mudah digunakan, hanya satu file cepat, dan melakukan apa yang saya butuhkan tanpa repot.
Pat McG
Ini sangat bagus! Catatan: Saya harus mengubah tombol Return secara manual untuk menampilkan "Next" dan "Done" seperti pada contoh.
Mike Miller
14

Saya suka solusi OO yang telah disarankan oleh Anth0 dan Answerserbot. Namun, saya sedang mengerjakan POC cepat dan kecil, jadi saya tidak ingin mengacaukan hal-hal dengan subclass dan kategori.

Solusi sederhana lainnya adalah membuat NSArray bidang dan mencari bidang berikutnya saat Anda menekan berikutnya. Bukan solusi OO, tetapi cepat, sederhana, dan mudah diimplementasikan. Anda juga dapat melihat dan memodifikasi pemesanan secara sekilas.

Inilah kode saya (dibangun di atas jawaban lain di utas ini):

@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;
}
  • Saya selalu mengembalikan TIDAK karena saya tidak ingin ada jeda baris. Hanya berpikir saya akan menunjukkan itu sejak ketika saya kembali YA itu akan secara otomatis keluar dari bidang berikutnya atau menyisipkan satu baris di TextView saya. Butuh sedikit waktu untuk memikirkannya.
  • activeField melacak bidang aktif jika pengguliran diperlukan untuk membuka tutup bidang dari keyboard. Jika Anda memiliki kode yang serupa, pastikan Anda menetapkan ActiveField sebelum mengubah responden pertama. Mengganti responden pertama adalah langsung dan akan menjalankan acara KeyboardWasShown segera.
Marquee
sumber
Bagus! Sederhana dan kodenya semua dalam satu model. Antho sangat bagus juga.
Seamus
Ingatlah bahwa Anda menjalankan risiko mempertahankan siklus dengan kode ini. FieldArray Anda mengonversi referensi lemah yang digunakan di semua IBOutlets Anda ke referensi kuat di dalam array itu sendiri.
Michael Long
11

Berikut ini adalah implementasi tabbing menggunakan kategori di UIControl. Solusi ini memiliki semua kelebihan metode dari Michael dan Anth0, tetapi berfungsi untuk semua kontrol UIC, bukan hanya UITextFields. Ini juga berfungsi mulus dengan Interface Builder dan storyboard.

Sumber dan aplikasi sampel: repositori GitHub untuk UIControlsWithTabbing

Pemakaian:

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

Menetapkan kontrol selanjutnya dalam Pembuat Antarmuka

Tajuk:

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

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

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

- (BOOL)transferFirstResponderToNextControl;

@end

Penerapan:

#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
picciano
sumber
1
Terkadang widget berikutnya bukan kontrol yaitu UITextView dan Anda mungkin ingin tab di sana juga. Pertimbangkan mengubah jenis dari UITextField ke UIResponder.
Pedro Borges
1
Bekerja dengan baik dan dengan dokumentasi yang luar biasa. Saya berharap saya gulir ke bawah ke pertanyaan ini beberapa jam yang lalu. :)
Javier Cadiz
10

Saya telah mencoba banyak kode dan akhirnya, ini berhasil untuk saya di Swift 3.0 Latest [Maret 2017]

The ViewControllerkelas harus mewarisi UITextFieldDelegateuntuk membuat kode kerja ini.

class ViewController: UIViewController,UITextFieldDelegate  

Tambahkan bidang Teks dengan nomor Tag yang Tepat dan nomor tag ini digunakan untuk mengambil kendali ke bidang teks yang sesuai berdasarkan nomor tag tambahan yang ditetapkan untuknya.

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

Dalam kode di atas, di returnKeyType = UIReturnKeyType.nextmana akan membuat Key pad kembali kunci untuk ditampilkan karena NextAnda juga memiliki opsi lainJoin/Go lain, dll, berdasarkan aplikasi Anda mengubah nilai-nilai.

Ini textFieldShouldReturnadalah metode yang dikontrol oleh UITextFieldDelegate dan di sini kami memiliki pemilihan bidang berikutnya berdasarkan peningkatan nilai Tag

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
 }
MOHANKUMAR BHUVANESH
sumber
Ini berfungsi, kecuali jika bidang inputnya adalah UITextView. Adakah yang tahu bagaimana melakukan ini dalam kombinasi?
T9b
@ T9b Apa yang sebenarnya Anda butuhkan? kontrol tipe apa yang Anda gunakan di aplikasi Anda?
BHUVANESH MOHANKUMAR
Ada dua jenis input teks, yang pertama adalah UITextField(masukkan satu baris teks), yang lain adalah UITextView(masukkan beberapa baris teks). UITextViewjelas tidak merespons kode ini tetapi dapat digunakan dalam bentuk.
T9b
Ya ini bagian dari kode untuk UITextField saja dan sudahkah Anda mencoba memodifikasi acara untuk UITextView?
BHUVANESH MOHANKUMAR
9

Setelah Anda keluar dari satu bidang teks, Anda memanggil [otherTextField menjadiFirstResponder] dan bidang berikutnya mendapat fokus.

Ini sebenarnya bisa menjadi masalah yang sulit untuk dihadapi karena sering kali Anda juga ingin menggulir layar atau menyesuaikan posisi bidang teks sehingga mudah dilihat saat mengedit. Pastikan untuk melakukan banyak pengujian dengan masuk dan keluar dari bidang teks dengan cara yang berbeda dan juga pergi lebih awal (selalu memberi pengguna pilihan untuk mengabaikan keyboard daripada pergi ke bidang berikutnya, biasanya dengan "Selesai" di bilah nav)

Kendall Helmstetter Gelner
sumber
9
 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}
iKushal
sumber
Anda tidak boleh menggunakan tag untuk tujuan ini.
Christian Schnorr
7

Metode yang sangat mudah untuk mengabaikan keyboard ketika tombol 'Selesai' ditekan adalah:

Buat IBAction baru di header

- (IBAction)textFieldDoneEditing:(id)sender;

Dalam file implementasi (file .m) tambahkan metode berikut:

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

Kemudian, ketika Anda datang untuk menautkan IBAction ke bidang teks - tautan ke acara 'Did End On Exit'.

jcrowson
sumber
1
Sama-sama, itu tidak menavigasi melalui bidang teks tetapi itu menutup keyboard dan memungkinkan Anda untuk memilih bidang lain.
jcrowson
7

Setel tombol kembali keyboard pertama kali di xib, jika tidak, Anda dapat menulis kode di 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;
}
mahesh chowdary
sumber
7

Saya terkejut dengan banyaknya jawaban di sini yang gagal memahami satu konsep sederhana: menavigasi melalui kontrol di aplikasi Anda bukanlah sesuatu yang seharusnya dilakukan oleh pandangan itu sendiri. Adalah tugas pengontrol untuk memutuskan kontrol mana yang membuat responden pertama berikutnya.

Sebagian besar jawaban hanya berlaku untuk navigasi maju, tetapi pengguna juga mungkin ingin mundur.

Jadi inilah yang saya pikirkan. Formulir Anda harus dikelola oleh pengontrol tampilan, dan pengontrol tampilan adalah bagian dari rantai responden. Jadi, Anda bebas menerapkan metode berikut:

#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];
}

Logika tambahan untuk menggulir responden dari layar yang terlihat sebelumnya mungkin berlaku.

Keuntungan lain dari pendekatan ini adalah bahwa Anda tidak perlu mensubklasifikasikan semua jenis kontrol yang mungkin ingin Anda tampilkan (seperti UITextFields) tetapi sebaliknya dapat mengelola logika pada tingkat pengontrol, di mana, jujur ​​saja, adalah tempat yang tepat untuk melakukannya .

Christian Schnorr
sumber
Saya menemukan jawaban Anda yang paling relevan, tetapi saya perhatikan ada sesuatu yang lucu yang mungkin bisa Anda jelaskan. Saat menggunakan UITextFields yang tidak dikelola oleh UIViewController, menekan tombol tab akan membuat UITextField berikutnya menjadi responder pertama secara default. Namun, ketika masing-masing dikelola oleh VC, ini tidak benar. VC saya tidak melakukan apa-apa, tetapi fungsi tab bawaan sudah tidak ada. Saya perhatikan bahwa keyCommand dari VC akhirnya ditambahkan ke rantai pandangan responden. Ini membuat saya percaya bahwa jika Anda menggunakan VC, wajib untuk mengimplementasikan keyCommands.
Joey Carson
4

Saya telah menambahkan ke jawaban PeyloW jika Anda ingin menerapkan fungsi tombol sebelumnya / berikutnya:

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

    }
}

Di mana Anda mensubklasifikasikan UIView seperti ini:

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

Halo semua, tolong lihat yang ini

- (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;    
  }
}
Rithik
sumber
Alih-alih responden UIView * akan lebih akurat menggunakan responden UIRespoder *
Pedro Borges
4

Saya mencoba memecahkan masalah ini menggunakan pendekatan yang lebih canggih berdasarkan pada penugasan setiap sel (atau UITextField) dalam UITableViewnilai tag unik yang dapat diambil kembali: aktifkan-next-uitextfield-di-uitableview-ios

Saya harap ini membantu!

Fabiano Francesconi
sumber
4

Saya baru saja membuat Pod baru ketika menangani hal-hal ini GNTextFieldsCollectionManager . Secara otomatis menangani masalah textField berikutnya / terakhir dan sangat mudah digunakan:

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

Raih semua bidang teks yang diurutkan dengan muncul dalam hierarki tampilan (atau dengan tag), atau Anda dapat menentukan larik textFields Anda sendiri.

JakubKnejzlik
sumber
4

Solusi di Swift 3.1, Setelah menghubungkan bidang teks Anda, IBOutlets mengatur delegasi bidang teks Anda di viewDidLoad, Dan kemudian menavigasi tindakan Anda di 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
        }

    }
Krishna Thakur
sumber
4

Jika seseorang ingin seperti ini. Saya pikir ini adalah yang paling dekat dengan persyaratan yang diminta

masukkan deskripsi gambar di sini

Inilah cara saya menerapkan yang satu ini

Tambahkan tampilan aksesori untuk setiap bidang teks yang Anda inginkan pengaturannya, gunakan

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
}

Gunakan fungsi berikut untuk menangani keran

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
}

Anda perlu memberikan tag textFields secara berurutan di mana Anda ingin tombol next / prev untuk merespons.

Mohammad Sadiq
sumber
3

Saya lebih suka:

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

Dalam file NIB saya menghubungkan textFields dalam urutan yang diinginkan ke dalam array inputFields ini. Setelah itu saya melakukan tes sederhana untuk indeks UITextField yang melaporkan bahwa pengguna mengetuk kembali:

// 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;
}
Anticro
sumber
3
if (cell == nil)
{
    cell = [[Alokasi UITableViewCell] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: cellIdentifier];
    txt_Input = [[Alokasi UITextField] initWithFrame: CGRectMake (0, 10, 150, 30)];
    txt_Input.tag = indexPath.row + 1;
    [self.array_Textfields addObject: txt_Input]; // Inisialisasi array yang dapat diubah di ViewDidLoad
}

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

    int tag = (int) textField.tag;
    UITextField * txt = [self.array_Textfields objectAtIndex: tag];
    [txt menjadiFirstResponder];
    mengembalikan YA;
}
Rebecca Curties
sumber
3

Saya memiliki sekitar 10+ UITextField di papan cerita saya dan cara saya mengaktifkan fungsionalitas berikutnya adalah dengan membuat array UITextField dan membuat UITextField berikutnya sebagai responden pertama. Inilah file implementasi:

#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
Argumen Ilegal
sumber
3

Ini bekerja untuk saya di Xamarin.iOS / Monotouch. Ubah tombol keyboard ke Berikutnya, berikan kontrol ke UITextField berikutnya dan sembunyikan keyboard setelah UITextField terakhir.

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

Di dalam ViewDidLoad Anda akan memiliki:

Jika TextFields Anda belum memiliki Tag, atur sekarang:

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

dan hanya panggilan

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

Ini adalah solusi sederhana dengan cepat, tanpa menggunakan tag, tanpa trik papan cerita ...

Cukup gunakan ekstensi ini:

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
    }

}

Dan menyebutnya seperti ini (misalnya) dengan delegasi bidang teks Anda:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}
Kevin ABRIOUX
sumber
Satu komentar tentang ini adalah bahwa metode Anda mengasumsikan semua bidang ditumpuk secara vertikal dalam satu superview. Itu tidak memungkinkan untuk bidang yang dikelompokkan dan tidak berfungsi saat bidang teks mungkin berdampingan. Yang belakangan sangat penting saat ini ketika seseorang menggunakan kelas ukuran untuk mengatur ulang formulir untuk tampilan horizontal dan ketika formulir ditampilkan di tablet.
Michael Long
3

Cara yang lebih aman dan lebih langsung, dengan asumsi:

  • delegasi bidang teks diatur ke pengontrol tampilan Anda
  • semua bidang teks adalah subview dari tampilan yang sama
  • bidang teks memiliki tag dalam urutan yang Anda inginkan untuk maju (misalnya, textField2.tag = 2, textField3.tag = 3, dll.)
  • pindah ke bidang teks berikutnya akan terjadi ketika Anda mengetuk tombol kembali pada keyboard (Anda dapat mengubahnya ke berikutnya , selesai , dll.)
  • Anda ingin keyboard diberhentikan setelah bidang teks terakhir

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

    }
}
jason z
sumber
2

di textFieldShouldReturn Anda harus memeriksa bahwa bidang teks Anda saat ini bukan yang terakhir ketika mereka mengklik berikutnya dan jika itu tidak mengabaikan keyboard ..

Daniel
sumber
Hei, ok ini berfungsi, tapi bagaimana saya bisa melompat ke textfield berikutnya dengan Tombol "Next"?
phx
2

Ini adalah posting lama, tetapi memiliki peringkat halaman yang tinggi jadi saya akan berpadu dengan solusi saya.

Saya memiliki masalah serupa dan akhirnya membuat subclass UIToolbaruntuk mengelola fungsionalitas berikutnya / sebelumnya / selesai dalam tableView dinamis dengan bagian: https://github.com/jday001/DataEntryToolbar

Anda mengatur bilah alat sebagai inputAccessoryView dari bidang teks Anda dan menambahkannya ke kamus. Ini memungkinkan Anda untuk menggilir maju dan mundur, bahkan dengan konten dinamis. Ada metode pendelegasian jika Anda ingin memicu fungsionalitas Anda sendiri ketika navigasi TextField terjadi, tetapi Anda tidak harus berurusan dengan mengelola tag apa pun atau status responden pertama.

Ada cuplikan kode & contoh aplikasi di tautan GitHub untuk membantu dengan detail implementasi. Anda akan membutuhkan model data Anda sendiri untuk melacak nilai-nilai di dalam bidang.

jday
sumber
2

Tanpa tag penggunaan dan tanpa menambahkan properti untuk nextField / nextTextField, Anda dapat mencoba ini untuk meniru TAB, di mana "testInput" adalah bidang aktif Anda saat ini:

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];
                                                    }];
Cur
sumber
2

Saya telah menggunakan jawaban Michael G. Emmons selama sekitar satu tahun sekarang, bekerja dengan baik. Baru-baru ini saya memperhatikan bahwa memanggil resignFirstResponder dan kemudian menjadiFirstResponder segera dapat menyebabkan keyboard "glitch", menghilang dan kemudian segera muncul. Saya mengubah versinya sedikit untuk melewati resignFirstResponder jika nextField tersedia.

- (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] menjadiFirstResponder]; });

        }
        lain{
            [textField resignFirstResponder];
        }
    }
    lain{
        [textField resignFirstResponder];
    }

    kembali benar;

}
arinmorf
sumber