Bagaimana saya bisa membuat UITextField bergerak ke atas saat keyboard ada - saat mulai mengedit?

1691

Dengan SDK iOS:

Saya memiliki UIViewdengan UITextFields yang memunculkan keyboard. Saya membutuhkannya untuk dapat:

  1. Izinkan menggulir konten UIScrollViewuntuk melihat bidang teks lainnya setelah keyboard ditampilkan

  2. Secara otomatis "melompat" (dengan menggulir ke atas) atau memendek

Saya tahu bahwa saya membutuhkan UIScrollView. Saya sudah mencoba mengubah kelas saya UIViewmenjadi UIScrollViewtetapi saya masih tidak dapat menggulir kotak teks ke atas atau ke bawah.

Apakah saya harus baik UIViewdan UIScrollView? Apakah satu masuk ke yang lain?

Apa yang perlu diterapkan untuk menggulir secara otomatis ke bidang teks aktif?

Idealnya sebanyak mungkin pengaturan komponen mungkin dilakukan di Interface Builder. Saya hanya ingin menulis kode untuk apa yang membutuhkannya.

Catatan: UIView(atau UIScrollView) yang saya kerjakan ditampilkan oleh tabbar ( UITabBar), yang perlu berfungsi seperti biasa.


Sunting: Saya menambahkan bilah gulir hanya untuk saat keyboard muncul. Meskipun itu tidak diperlukan, saya merasa seperti itu menyediakan antarmuka yang lebih baik karena dengan begitu pengguna dapat menggulir dan mengubah kotak teks, misalnya.

Saya sudah membuatnya bekerja di mana saya mengubah ukuran bingkai UIScrollViewketika keyboard naik dan turun. Saya hanya menggunakan:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Namun, ini tidak secara otomatis "bergerak ke atas" atau memusatkan bidang teks yang lebih rendah di area yang terlihat, yang benar-benar saya inginkan.

philfreo
sumber
6
Lihat ini. Tidak ada kerumitan untukmu. TPKeyboardAvoiding
Aruna
21
Ini didokumentasikan oleh Apple, saya pikir ini adalah cara terbaik: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639
58
Gunakan kode ini. Anda hanya perlu 1 baris dalam file appdelegate.m dan berfungsi. github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal
9
Cara terbaik yang saya temukan sejauh ini adalah open source TPKeyboardAvoiding
Mongi Zaidi
2
Cara lain adalah menambahkan bidang teks konten seperti itu dan semuanya di TableViewController dan biarkan tableview menangani ini.
Vicky Dhas

Jawaban:

1036
  1. Anda hanya perlu ScrollViewjika konten yang Anda miliki sekarang tidak muat di layar iPhone. (Jika Anda menambahkan ScrollViewsebagai superview komponen hanya untuk membuat TextFieldgulir ketika keyboard muncul, maka itu tidak diperlukan.)

  2. Cara standar untuk mencegah agar TextFields tidak tercakup oleh keyboard adalah menggerakkan tampilan ke atas / bawah setiap kali keyboard ditampilkan.

Berikut ini beberapa contoh kode:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}
RPDP
sumber
3
Apa yang dimaksud dengan _textField? Saya menyalinnya ke dalam kode saya, katanya _textField tidak dideklarasikan.
Cocoa Dev
Itu bidang yang Anda gunakan untuk mengatakan "ketika pengguna mengedit di sini tampilan harus geser ke atas" atau sesuatu yang ... Namun Anda dapat menghapus itu jika, jika Anda lebih banyak bidang.
patrick
bukan adonan untuk memanggil - (void) setViewMovedUp: (BOOL) pindah ke acara keyBoardWillSHow dan KeyBoardWillHide !!
Abduliam Rehmanius
4
Tidak terlalu berguna jika Anda mendukung rotasi tampilan utama.
FractalDoctor
2
Untuk membuat ini berhasil, saya harus mengomentari textFieldDidBeginEditingbagian ini.
avance
445

Saya juga mengalami banyak masalah dengan UIScrollViewpenyusunan beberapa UITextFields, di antaranya, satu atau lebih dari mereka akan dikaburkan oleh keyboard ketika mereka sedang diedit.

Berikut adalah beberapa hal yang perlu dipertimbangkan jika Anda UIScrollViewtidak menggulir dengan benar.

1) Pastikan ukuran konten Anda lebih besar dari UIScrollViewukuran bingkai. Cara untuk memahami UIScrollViewsadalah UIScrollViewseperti jendela tampilan pada konten yang didefinisikan dalam contentSize. Jadi ketika UIScrollviewuntuk menggulir di mana saja, contentSize harus lebih besar dari UIScrollView. Selain itu, tidak ada gulir yang diperlukan karena semua yang didefinisikan dalam contentSize sudah terlihat. BTW, contentSize standar = CGSizeZero.

2) Sekarang setelah Anda memahami bahwa UIScrollViewitu benar-benar jendela ke "konten" Anda, cara untuk memastikan bahwa keyboard tidak mengaburkan UIScrollView's"jendela" penglihatan Anda adalah dengan mengubah ukuran UIScrollViewsehingga ketika keyboard ada, Anda memiliki UIScrollViewjendela berukuran hanya UIScrollViewbingkai asli. ukuran. tinggi minus ketinggian keyboard. Ini akan memastikan bahwa jendela Anda hanya area kecil yang bisa dilihat.

3) Inilah intinya : Ketika saya pertama kali menerapkan ini saya pikir saya harus mendapatkan CGRectdari bidang teks yang diedit dan memanggil UIScrollView'smetode scrollRecToVisible. Saya menerapkan UITextFieldDelegatemetode textFieldDidBeginEditingdengan panggilan ke scrollRecToVisiblemetode. Ini benar-benar bekerja dengan efek samping aneh yang bergulir akan mengambil yang UITextFieldke posisinya. Untuk waktu yang paling lama saya tidak tahu apa itu. Lalu saya berkomentar textFieldDidBeginEditingmetode Delegasi dan semuanya bekerja !! (???). Ternyata, saya percaya yang UIScrollViewsebenarnya secara implisit membawa yang saat ini diedit UITextFieldke jendela yang dapat dilihat secara implisit. Implementasi saya terhadap UITextFieldDelegatemetode dan panggilan selanjutnya ke scrollRecToVisibleredundant dan merupakan penyebab efek samping yang aneh.

Jadi, inilah langkah-langkah untuk menggulirkan Anda UITextFielddengan benar UIScrollViewke tempatnya saat keyboard muncul.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Daftarkan notifikasi keyboard di viewDidLoad
  2. Batalkan pendaftaran untuk nofitikasi keyboard di viewDidUnload
  3. Pastikan bahwa contentSizediatur dan lebih besar dari UIScrollViewpada AndaviewDidLoad
  4. Kecilkan yang UIScrollViewketika keyboard hadir
  5. Memulihkan kembali yang UIScrollViewsaat keyboard hilang.
  6. Gunakan Ivar untuk mendeteksi jika keyboard sudah ditampilkan di layar sejak pemberitahuan keyboard dikirim setiap kali UITextFieldadalah tab bahkan jika keyboard sudah ada untuk menghindari menyusut yang UIScrollViewketika itu sudah menyusut

Satu hal yang perlu diperhatikan adalah UIKeyboardWillShowNotificationkehendak menyala bahkan saat keyboard sudah ada di layar saat Anda tab pada yang lain UITextField. Saya menangani ini dengan menggunakan ivar untuk menghindari mengubah ukuran UIScrollViewketika keyboard sudah ada di layar. Mengubah ukuran secara tidak sengaja UIScrollViewketika keyboard sudah ada akan menjadi bencana!

Semoga kode ini menghemat beberapa dari Anda sakit kepala.

Shiun
sumber
3
Hebat, tetapi ada dua masalah: 1. UIKeyboardBoundsUserInfoKeysudah usang. 2. keyboardSize ada di "koordinat layar", sehingga perhitungan viewFrame Anda akan gagal jika frame diputar atau diskalakan.
Martin Wickman
21
@ Martin Wickman - Gunakan CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;sebagai gantiUIKeyboardBoundsUserInfoKey
sottenad
1
HI, saya melakukan hal yang sama, tetapi tampilan teks hanya bergerak ketika pengguna mulai mengetik? Apakah itu perilaku yang diharapkan atau saya kehilangan sesuatu?
3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizeseharusnya [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Solusi hebat!
j7nn7k
1
Saya suka solusi Anda, tetapi saya pikir saya bisa membuatnya lebih sederhana: jangan repot-repot dengan hal-hal Pemberitahuan Pengamat; alih-alih panggil rutin animasi yang tepat di dalam metode delegasi yang sesuai - untuk UITextView mereka adalah textViewDidBeginEditing dan textViewDidEndEditing.
AlexChaffee
270

Sebenarnya yang terbaik hanya menggunakan implementasi Apple, seperti yang disediakan dalam dokumen . Namun, kode yang mereka berikan salah. Ganti bagian yang ditemukan keyboardWasShown:tepat di bawah komentar sebagai berikut:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Masalah dengan kode Apple adalah sebagai berikut: (1) Mereka selalu menghitung jika intinya ada dalam bingkai tampilan, tetapi ini adalah ScrollView, jadi mungkin sudah digulir dan Anda harus memperhitungkan offset itu:

origin.y -= scrollView.contentOffset.y

(2) Mereka menggeser kontenOffset oleh ketinggian keyboard, tetapi kami menginginkan yang sebaliknya (kami ingin menggeser contentOffsetoleh ketinggian yang terlihat di layar, bukan apa yang tidak):

activeField.frame.origin.y-(aRect.size.height)
DK_
sumber
1
Dalam situasi di mana tampilan gulir tidak memenuhi layar, aRect harus diatur ke bingkai tampilan gulir
mblackwell8
2
Tidakkah Anda ingin CGPoint origin = activeField.frame.origin + activeField.frame.size.height?, Karena Anda ingin seluruh bidang teks ditampilkan dan jika kebetulan hanya beberapa piksel yang terlihat maka kode tidak akan masuk ke kondisi.
htafoya
1
Solusi ini tidak berfungsi dalam orientasi Lansekap - bidang teks terbang dari bagian atas port tampilan. iPad dengan iOS 7.1.
Andrew
4
Untuk dukungan iOS 8 yang lebih baik, saya sarankan menggunakan UIKeyboardFrameEndUserInfoKeydaripada UIKeyboardFrameBeginUserInfoKeysaat mendapatkan ukuran keyboard, karena ini akan mengambil hal-hal seperti perubahan keyboard khusus dan mengaktifkan / menonaktifkan teks prediktif.
Endareth
1
@ Egor: Perbaikan Anda membuatnya bekerja lebih baik - tetapi baris terakhir harus terbalik:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard
244

Di textFieldDidBeginEdittingdan di textFieldDidEndEditingpanggilan fungsi [self animateTextField:textField up:YES]seperti:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Saya harap kode ini akan membantu Anda.

Dalam Swift 2

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }
sumanthkodi
sumber
1
mengapa tidak digunakan [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn
2
ini bekerja dengan baik, meskipun const int movementDistance = -130; // tweak sesuai kebutuhan perlu diubah menjadi lebih fleksibel
Hammer
7
Sangat sederhana pada implementasi kecil. Tidak ada masalah dengan ScrollViews dan masalah tata letak otomatis yang ambigu.
James Perih
4
Erm ... Anda tidak menggunakan parameter textField sama sekali. Lalu mengapa memilikinya sebagai parameter fungsi? Selain itu, Anda dapat menggunakan operator ternary juga di Swift. Membuat kode lebih tidak banyak bicara.
stk
1
Jika warna latar belakang Tampilan adalah sesuatu selain hitam, pastikan Anda mengatur warna Window agar sesuai dengan tampilan Anda sehingga pengguna tidak melihat di belakangnya. yaitu self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps
134

Hanya menggunakan TextFields:

1a) Menggunakan Interface Builder: Pilih Semua TextFields => Edit => Embed In => ScrollView

1b) Secara manual menanamkan TextFields di UIScrollView yang disebut scrollView

2) Atur UITextFieldDelegate

3) Atur masing-masing textField.delegate = self;(atau buat koneksi di Interface Builder)

4) Salin / Tempel:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}
DAS
sumber
8
Tetapi juga bergerak ke atas tampilan saat textFieldsudah terlihat.
TheTiger
1
Perlu diubah CGPointMake(0, textField.frame.origin.y);keCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury
@Egor Bahkan setelah komentar Anda itu tidak berfungsi. Seperti "TheTiger" disebutkan itu bergerak naik tampilan bahkan setelah bidang teks terlihat.
rak appdev
Ubah untuk XCode 10: "Pilih Semua TextFields => Editor => Embed In => Scroll View"
tibalt
116

Untuk Solusi Universal , Inilah pendekatan saya untuk mengimplementasikan IQKeyboardManager .

masukkan deskripsi gambar di sini

Langkah 1: - Saya Ditambahkan pemberitahuan global UITextField, UITextViewdan UIKeyboarddi kelas tunggal. Saya menyebutnya IQKeyboardManager .

Langkah2: - Jika ditemukan UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationatau UITextViewTextDidBeginEditingNotificationpemberitahuan, saya mencoba untuk mendapatkan topMostViewControllercontoh dari UIWindow.rootViewControllerhierarki. Agar dapat menemukan UITextField/ UITextViewdi atasnya, topMostViewController.viewbingkai harus disesuaikan.

Langkah 3 : - Saya menghitung jarak bergerak yang diharapkan topMostViewController.viewsehubungan dengan tanggapan pertama UITextField/ UITextView.

Step4: - Saya bergerak topMostViewController.view.framenaik / turun sesuai dengan jarak bergerak yang diharapkan.

Langkah 5: - Jika ditemukan UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationatau UITextViewTextDidEndEditingNotificationnotifikasi, saya kembali mencoba untuk mendapatkan topMostViewControllercontoh dari UIWindow.rootViewControllerhierarki.

Langkah6: - Saya menghitung jarak terganggu topMostViewController.viewyang perlu dikembalikan ke posisi semula.

Step7: - Saya mengembalikan topMostViewController.view.frameturun sesuai dengan jarak yang terganggu.

Langkah8: - Saya instantiated instance kelas IQKeyboardManager singleton pada aplikasi load, sehingga setiap UITextField/ UITextViewdalam aplikasi akan menyesuaikan secara otomatis sesuai dengan jarak pergerakan yang diharapkan.

Itu semua yang dilakukan IQKeyboardManager untuk Anda tanpa BANYAK KODE !! hanya perlu menarik dan melepas file sumber terkait ke proyek. IQKeyboardManager juga mendukung Orientasi Perangkat , Manajemen UIToolbar Otomatis , KeybkeyboardDistanceFromTextField dan lebih dari yang Anda pikirkan.

Mohd Iftekhar Qurashi
sumber
Tambahkan direktori IQKeyBoardManagerSwift ke proyek saya dan tidak berfungsi. Tidak dapat mengaktifkan karena tidak mengenalinya di AppDelegate ...
user3722523
2
ini terasa seperti phishing solusi yang sebenarnya tidak ditampilkan tetapi kami melihat iklan untuk akun GitHub ini.
Brian
101

Saya telah membuat subclass universal, drop-in UIScrollView, UITableViewdan bahkan UICollectionViewyang menangani memindahkan semua bidang teks di dalamnya keluar dari keyboard.

Ketika keyboard akan segera muncul, subclass akan menemukan subview yang akan diedit, dan menyesuaikan frame dan konten offset untuk memastikan tampilan terlihat, dengan animasi yang cocok dengan pop-up keyboard. Saat keyboard hilang, keyboard akan mengembalikan ukuran sebelumnya.

Seharusnya pada dasarnya bekerja dengan pengaturan apa pun, baik UITableViewantarmuka berbasis, atau yang terdiri dari tampilan yang ditempatkan secara manual.

Ini dia: solusi untuk memindahkan bidang teks dari keyboard

Michael Tyson
sumber
Ini dia! Ini adalah solusi terbaik, paling efisien, dan sempurna! Ini juga menangani rotasi dengan benar untuk tampilan gulir. Jika memutar pastikan untuk autoresize secara vertikal tetapi jangan jangkar di bagian bawah. Saya menambahkan UITextView ke tampilan gulir dalam kasus saya. Terima kasih banyak!
Christopher
Kerja yang sangat bagus! Tentu, saya malas menggunakan solusi Anda daripada yang DIY, tapi bos saya lebih bahagia, jadi ya! Bahkan jika seseorang ingin melakukannya sendiri, saya suka pendekatan subclass Anda, daripada menambahkan kode ke masing-masing controller. Saya kaget iOS tidak melakukan ini secara default seperti Android - sekali lagi, saya menemukan banyak hal yang kurang di iOS dan MacOS :(
eselk
Apakah hal-hal aneh seperti scrollview saya semuanya cocok di layar, sehingga tidak dapat digulir. Setelah membuka dan menutup keyboard, konten sekarang lebih besar (sepertinya ada sesuatu yang tidak terlihat ditambahkan dan tidak dihapus di bagian bawah halaman), dan dapat digulir.
Almo
91

Untuk Pemrogram Swift :

Ini akan melakukan segalanya untuk Anda, cukup taruh ini di kelas controller tampilan Anda dan implementasikan UITextFieldDelegateke view controller Anda & setel delegasi textField keself

textField.delegate = self // Setting delegate of your UITextField to self

Menerapkan metode panggilan balik delegasi:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Untuk Swift 4, 4.2, 5: Ubah

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

untuk

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Catatan terakhir tentang implementasi ini: Jika Anda mendorong pengontrol tampilan lain ke tumpukan saat keyboard ditampilkan, ini akan membuat kesalahan ketika tampilan dikembalikan kembali ke frame tengahnya tetapi offset keyboard tidak diatur ulang. Misalnya, keyboard Anda adalah responder pertama untuk nameField, tetapi kemudian Anda menekan tombol yang mendorong Help View Controller ke tumpukan Anda. Untuk memperbaiki kesalahan offset, pastikan untuk memanggil nameField.resignFirstResponder () sebelum meninggalkan pengontrol tampilan, memastikan bahwa metode delegasi textFieldDidEndEditing dipanggil juga. Saya melakukan ini dalam metode viewWillDisappear.

Satnam Sync
sumber
3
SwiftLint tidak suka self.view.frame = CGRectOffset(self.view.frame, 0, movement)jadi saya mengubah baris itu menjadiself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian
2
Swift 4 ubah self.view.frame = CGRectOffset (self.view.frame, 0, movement) menjadi self.view.frame.offsetBy (dx: 0, dy: movement)
Asinox
FYI, agar ini berfungsi, Anda harus memasukkan self.view.frame = self.view.frame.offsetBy (dx: 0, dy: movement)
Josh Wolff
64

Sudah ada banyak jawaban, tetapi masih belum ada solusi di atas yang memiliki semua hal penentuan posisi mewah yang diperlukan untuk animasi "sempurna" bebas bug, kompatibel dengan belakang, dan bebas berkedip-kedip. (bug saat menjiwai bingkai / batas dan contentOffset bersama, orientasi antarmuka yang berbeda, keyboard perpecahan iPad, ...)
Biarkan saya berbagi solusi saya:
(dengan asumsi Anda telah mengatur UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}
Martin Ullrich
sumber
Peningkatan yang bagus dari jawaban @Shiun. Tetapi setelah keyboard hilang, tampilan tidak kembali ke posisi pertama. Ini masih merupakan pekerjaan yang luar biasa :)
Lucien
2
Terima kasih, ini adalah solusi terbaik untuk saya pada tahun 2017. Perhatikan bahwa Anda tidak perlu melacak fokusControl sendiri, Anda dapat menentukannya dengan UIApplication.shared.sendAction(...). Inilah versi Swift 3 dari jawaban Anda (dikurangi porsi akan disembunyikan), dengan sendActionpenerapan: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod
@ xaphod dalam kasus saya, saya perlu memfokuskan lebih banyak kontrol - misalnya tombol di bawah bidang input. tapi ya kode itu sekarang sudah 4 tahun dan mungkin mendapat manfaat dari perbaikan.
Martin Ullrich
Ini mungkin solusi yang tepat. Notifikasi keyboard membawa data animasi, delegasi bidang teks tidak tahu tentang durasi animasi, itu hanya akan menjadi tebakan.
XY
62

Shiun berkata "Ternyata, saya percaya UIScrollView sebenarnya secara implisit membawa UITextField yang saat ini diedit ke dalam jendela yang dapat dilihat secara implisit" Ini tampaknya berlaku untuk iOS 3.1.3, tetapi tidak 3.2, 4.0, atau 4.1. Saya harus menambahkan scrollRectToVisible eksplisit untuk membuat UITextField terlihat di iOS> = 3.2.

cbranch
sumber
Ini bukan UIScrollView yang menggulir secara implisit tampilan UITextField yang diedit, melainkan UITextField yang memanggil [UITextField scrollTextFieldToVisibleIfNecessary]metode pribadi yang kemudian memanggil [UIScrollView scrollRectToVisible]saat [UITextField becomeFirstResponder]dipanggil. Lihat github.com/leopatras/ios_textfields_on_scrollview . Jika kendala dan pengontrol tampilan diatur dengan benar, sebenarnya tidak perlu menelepon scrollRectToVisiblesecara eksplisit (setidaknya sejak iOS 11).
Leo
48

Satu hal yang perlu dipertimbangkan adalah apakah Anda pernah ingin menggunakan UITextFieldsendiri. Saya belum menemukan aplikasi iPhone yang dirancang dengan baik yang benar-benar digunakan di UITextFieldsluar UITableViewCells.

Ini akan menjadi pekerjaan tambahan, tapi saya sarankan Anda menerapkan semua tampilan entri data tampilan tabel. Tambahkan UITextViewke UITableViewCells.

Jonathan Sterling
sumber
1
Salah satu aplikasi saya harus memungkinkan pengguna untuk menambahkan catatan bentuk bebas- jadi ya kadang-kadang berguna untuk menggunakan UITextField.
Peter Johnson
1
Saya setuju dengan metode ini. Tidak ada pekerjaan atau kode dengan cara ini. Sekalipun Anda membutuhkan nota formulir gratis, Anda masih bisa menggunakan sel tabel
RJH
UITableViewsayangnya satu-satunya cara untuk pergi. Notifikasi keyboard rapuh dan telah berubah dari waktu ke waktu. Contoh kode pada Stack Overflow: stackoverflow.com/a/32390936/218152
SwiftArchitect
Jawaban ini sekitar lima tahun kedaluwarsa. Satu-satunya solusi modern adalah sesuatu seperti ini ... stackoverflow.com/a/41808338/294884
Fattie
47

Dokumen ini merinci solusi untuk masalah ini. Lihatlah kode sumber di bawah 'Memindahkan Konten yang Terletak Di Bawah Keyboard'. Cukup mudah.

EDIT: Melihat ada kesalahan wee dalam contoh. Anda mungkin ingin mendengarkan UIKeyboardWillHideNotificationbukan UIKeyboardDidHideNotification. Kalau tidak, tampilan gulir di belakang keyboard akan terpotong selama durasi animasi penutupan keyboard.

Mihai Damian
sumber
32

Solusi termudah ditemukan

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}
Jawad Zeb
sumber
Layar bergerak ke atas walaupun tidak di bagian bawah. yaitu, jika bidang teks di atas itu bergerak keluar dari layar. Bagaimana cara mengontrol kasing itu?
MELWIN
@MELWIN Cukup tambahkan setelah baris ini: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Tolong jangan bahwa keyboardvariabel adalah CGRect dari keyboard yang muncul yang Anda dapatkan dengan melakukan:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree
31

Sedikit perbaikan yang berfungsi untuk banyak UITextFields

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}
tt.Kilew
sumber
rect.origin.y=+currTextField.frame.origin.ybekerja dengan baik, terima kasih
u.gen
30

Kode RPDP berhasil memindahkan bidang teks dari keyboard. Tetapi ketika Anda menggulir ke atas setelah menggunakan dan mengabaikan keyboard, bagian atas telah digulir keluar dari tampilan. Ini berlaku untuk Simulator dan perangkat. Untuk membaca konten di bagian atas tampilan itu, seseorang harus memuat ulang tampilan.

Bukankah kode berikut ini seharusnya menurunkan tampilan?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
Steve
sumber
23

Saya tidak yakin apakah menggerakkan tampilan ke atas adalah pendekatan yang benar, saya melakukannya dengan cara yang berbeda, mengubah ukuran UIScrollView. Saya menjelaskannya secara terperinci pada sebuah artikel kecil

Jose Muanis
sumber
Tautan ke artikel sudah mati.
Teo
22

Untuk mengembalikan ke kondisi tampilan asli, tambahkan:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}
Nicolas Marchand
sumber
20

Coba trik singkat ini.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}
Sourabh Sharma
sumber
19

Ada begitu banyak solusi, tetapi saya telah menghabiskan beberapa jam sebelum mulai bekerja. Jadi, saya meletakkan kode ini di sini (cukup tempel ke proyek, modifikasi apa pun tidak perlu):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

PS: Saya harap kode membantu seseorang membuat efek yang diinginkan dengan cepat. (Xcode 4.5)

HotJard
sumber
Halo Hotjard, saya mendapatkan EXE_BAD_ACCESS di [self.view addSubview: natView];
Bala
18

@ user271753

Untuk mengembalikan tampilan Anda ke yang asli, tambahkan:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}
pengguna436179
sumber
16

Itu tidak memerlukan tampilan gulir untuk dapat memindahkan bingkai tampilan. Anda dapat mengubah bingkai viewcontroller'stampilan sehingga seluruh tampilan bergerak cukup tinggi untuk menempatkan bidang teks firstresponder di atas keyboard. Ketika saya mengalami masalah ini, saya membuat subkelas UIViewControlleryang melakukan ini. Itu mengamati untuk keyboard akan muncul pemberitahuan dan menemukan subview responden pertama dan (jika perlu) itu menghidupkan tampilan utama ke atas cukup sehingga responden pertama di atas keyboard. Saat keyboard bersembunyi, ia menghidupkan kembali tampilan tempat itu.

Untuk menggunakan subkelas ini, buat pengontrol tampilan khusus Anda menjadi subkelas GMKeyboardVC dan itu mewarisi fitur ini (pastikan Anda menerapkan viewWillAppeardan viewWillDisappearmereka harus memanggil super). Kelas ada di github .

program
sumber
Lisensi apa? Beberapa file Anda di sana memiliki lisensi sumber terbuka dan beberapa tidak.
jaime
Peringatan: kode ini tidak ramah dengan proyek ARC.
Almo
Anda cukup menambahkan opsi build untuk menentukan bahwa itu adalah file non-ARC atau selamat datang untuk mengubahnya menjadi ARC dan mengirimkan permintaan tarik.
program
14

Cepat 4 .

Anda Dapat Dengan Mudah Bergerak Naik Turun UITextFieldAtau UIViewDengan UIKeyBoardDenganAnimation masukkan deskripsi gambar di sini

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

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

}
ZAFAR007
sumber
2
Hampir sempurna. Di iPhone X Anda mendapatkan celah aneh antara keyboard dan textfield.
Houman
12

Inilah solusi retas yang saya buat untuk tata letak tertentu. Solusi ini mirip dengan solusi Matt Gallagher dalam hal ini menggulir bagian ke tampilan. Saya masih baru dalam pengembangan iPhone, dan saya tidak terbiasa dengan cara kerja tata letak. Demikian, retas ini.

Implementasi saya diperlukan untuk mendukung pengguliran saat mengklik di bidang, dan juga menggulir ketika pengguna memilih berikutnya pada keyboard.

Saya punya UIView dengan ketinggian 775. Kontrol pada dasarnya tersebar dalam kelompok 3 di ruang yang besar. Saya berakhir dengan tata letak IB berikut.

UIView -> UIScrollView -> [UI Components]

Ini dia hacknya

Saya mengatur ketinggian UIScrollView menjadi 500 unit lebih besar dari tata letak yang sebenarnya (1250). Saya kemudian membuat array dengan posisi absolut yang harus saya gulir, dan fungsi sederhana untuk mendapatkannya berdasarkan nomor Tag IB.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Sekarang yang perlu Anda lakukan adalah menggunakan dua baris kode berikut di textFieldDidBeginEditing dan textFieldShouldReturn (yang terakhir jika Anda membuat navigasi bidang berikutnya)

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Sebuah contoh.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


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

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Metode ini tidak 'gulir kembali' seperti metode lainnya. Ini bukan persyaratan. Sekali lagi ini untuk UIView yang cukup 'tinggi', dan saya tidak punya waktu untuk mempelajari mesin tata letak internal.

Steve McFarlin
sumber
12

Sesuai dokumen , pada iOS 3.0, UITableViewControllerkelas secara otomatis mengubah ukuran dan reposisi tampilan tabelnya ketika ada pengeditan in-line bidang teks. Saya pikir itu tidak cukup untuk menempatkan bidang teks di dalam UITableViewCellseperti yang telah ditunjukkan beberapa orang.

Dari dokumen :

Pengontrol tampilan tabel mendukung pengeditan inline baris tampilan tabel; jika, misalnya, baris telah menyematkan bidang teks dalam mode pengeditan, baris itu akan diedit di atas papan ketik virtual yang ditampilkan.

Dheeraj VS
sumber
Saya menemukan komentar yang sama. Ya itu benar. Yang aneh adalah, bahwa itu berfungsi dalam satu UITabelViewController dan yang kedua tidak. Tetapi saya tidak dapat menemukan perbedaan dalam implementasi saya.
Morpheus78
11

Di sini saya menemukan solusi paling sederhana untuk menangani tombol.

Anda hanya perlu menyalin-tempel kode contoh di bawah ini dan mengubah bidang teks Anda atau tampilan apa pun yang ingin Anda naikkan.

Langkah 1

Cukup salin-tempel di bawah dua metode di controller Anda

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Langkah 2

register & deregister Notifikasi Keypad di viewWillAppear dan metode viewWillDisappear .

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Langkah-3

Inilah bagian jiwa, Cukup ganti bidang teks Anda, dan ubah tinggi badan sebanyak yang Anda inginkan.

- (void)keyboardWasShown:(NSNotification *)notification
{
    NSDictionary* info = [notification userInfo];
    CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Referensi : baik, Harap hargai orang ini , yang berbagi snip kode cantik ini, solusi bersih.

Semoga ini bisa membantu seseorang yang bermuka masam di luar sana.

swiftBoy
sumber
Saya pikir ini bukan yang terbaik. Ithink @Dheeraj VS benar: Ini dapat dilakukan dengan mudah & otomatis jika bidang teks itu ada di dalam sel tabel (bahkan ketika table.scrollable = TIDAK). CATATAN bahwa: posisi dan ukuran meja harus masuk akal. misalnya: - jika posisi tabel y 100 dihitung dari bawah tampilan, maka keyboard 300 tinggi akan tumpang tindih seluruh tabel. - jika tinggi tabel = 10, dan bidang teks di dalamnya harus digulir ke atas 100 saat keyboard muncul agar dapat terlihat, maka bidang teks itu akan keluar dari batas tabel.
samthui7
@ samthui7 Jawaban Dheeraj hanya berfungsi jika Anda menggunakan TableViewController, bukan hanya tampilan tabel. Itu membuatnya menjadi kendala yang terkadang tidak cocok.
Ben G
10

Telah mencari tutorial yang bagus untuk pemula tentang masalah ini, temukan tutorial terbaik di sini .

Pada MIScrollView.hcontoh di bagian bawah tutorial pastikan untuk memberi spasi

@property (nonatomic, retain) id backgroundTapDelegate;

seperti yang kamu lihat.

savagenoob
sumber
Hai savagenoob, terima kasih atas tautan yang diberikan dan selamat datang di stackoverflow. Silakan coba dan berikan info sebanyak mungkin saat menjawab pertanyaan (yang akan datang) - tautan sederhana agak rapuh. Yang mengatakan, jika jawabannya adalah tautan ke tutorial yang bagus yang mungkin terlewatkan.
Maarten Bodewes
10

Ketika UITextFieldsedang dalam UITableViewCellbergulir harus diatur secara otomatis.

Jika tidak, mungkin karena kode / pengaturan tampilan tabel yang salah.

Misalnya ketika saya memuat ulang meja panjang saya dengan satu UITextFielddi bagian bawah sebagai berikut,

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

kemudian textfield saya di bagian bawah dikaburkan oleh keyboard yang muncul ketika saya mengklik di dalam textfield.

Untuk memperbaikinya saya harus melakukan ini -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}
lakersgonna3peat
sumber
Saya bingung untuk apa kode ini? Saat keyboard ditampilkan, viewWillAppeartidak dipanggil. Dan reloadDatatidak membuat baris tertutup menjadi terlihat.
Adam Johns
10

Gunakan pihak ketiga ini yang tidak perlu Anda tulis satu baris pun

https://github.com/hackiftekhar/IQKeyboardManager

unduh proyek dan seret dan lepas IQKeyboardManagerdi proyek Anda. Jika Anda menemukan masalah, baca READMEdokumen.

Benar-benar menghapus sakit kepala untuk mengelola keyboard.

Arvind Kumar
sumber
8

Catatan : jawaban ini mengasumsikan textField Anda dalam scrollView.

Saya lebih suka berurusan dengan ini menggunakan scrollContentInset dan scrollContentOffset daripada mengacaukan bingkai pandangan saya.

Pertama mari kita dengarkan notifikasi keyboard

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Langkah selanjutnya adalah menjaga properti yang mewakili responden pertama saat ini (UITextfield / UITextVIew yang saat ini memiliki keyboard).

Kami menggunakan metode delegasi untuk mengatur properti ini. Jika Anda menggunakan komponen lain, Anda akan memerlukan sesuatu yang serupa.

Perhatikan bahwa untuk bidang teks kita atur di didBeginEditing dan untuk textView di shouldBeginEditing. Ini karena textViewDidBeginEditing dipanggil setelah UIKeyboardWillShowNotification karena beberapa alasan.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Akhirnya, inilah keajaibannya

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}
Juraj Petrik
sumber
8

Ini solusinya menggunakan Swift.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}
Homam
sumber
Jawaban yang benar, tetapi saya mengalami masalah nil saat menggunakan TextField dan TextView. Ada bantuan?
Thiha Aung
@Thiha Aung, Apakah variabel IBOutlet Anda dalam kode sumber Anda terhubung ke IB?
Homam
Ya mereka terhubung juga. Baru saja mengalami kesalahan itu ketika menggunakan UITextView di baris itu: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung
Berarti self.activeTextField tidak ada
Thiha Aung