Membuat gulir UITableView ketika bidang teks dipilih

251

Setelah banyak coba-coba, saya menyerah dan mengajukan pertanyaan. Saya telah melihat banyak orang dengan masalah yang sama tetapi tidak bisa mendapatkan semua jawaban untuk bekerja dengan benar.

Saya punya UITableViewyang terdiri dari sel-sel khusus. Sel-sel terbuat dari 5 bidang teks yang bersebelahan (semacam kisi).

Ketika saya mencoba untuk menggulir dan mengedit sel di bagian bawah UITableView, saya tidak dapat mengatur agar sel saya diposisikan dengan benar di atas keyboard.

Saya telah melihat banyak jawaban berbicara tentang mengubah ukuran tampilan, dll ... tetapi tidak satupun dari mereka yang bekerja dengan baik sejauh ini.

Adakah yang bisa menjelaskan cara "benar" untuk melakukan ini dengan contoh kode konkret?

Jonathan
sumber
11
Dokumentasi Applle ini menguraikan langkah-langkah untuk mengimplementasikan solusi untuk pertanyaan ini. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP
@ChrisP Tautan itu menyatakan belum diperbarui untuk iOS 4.0
Bae
Kode ini dapat membantu: gist.github.com/TimMedcalf/9505416
landonandrey
Ikuti di bawah Url, ini akan berfungsi: stackoverflow.com/questions/48922266/…
Venkatesh G

Jawaban:

126

Jika Anda menggunakan UITableViewController alih-alih UIViewController, itu akan secara otomatis melakukannya.

Sam Ho
sumber
13
Apakah Anda mencoba dan ternyata tidak berfungsi? Atau apakah solusinya terlalu sederhana untuk Anda percayai? Cukup rentangkan UITableViewController alih-alih UIViewController dan sel yang berisi bidang teks akan menggulir di atas keyboard setiap kali bidang teks menjadi responden pertama. Tidak diperlukan kode tambahan.
Sam Ho
3
Ya, tetapi terutama di iPad kita perlu cara untuk melakukan ini yang tidak melibatkan UITableViewController.
Bob Spryn
13
Untuk memperjelas, itu bukan jawaban yang masuk akal untuk mengatakan bahwa setiap kali Anda menggunakan tampilan tabel itu harus layar penuh, terutama pada iPad. Ada banyak contoh aplikasi hebat yang tidak melakukannya. Misalnya, banyak dari Apple sendiri, termasuk aplikasi Kontak di iPad.
Bob Spryn
32
Ini tidak akan berfungsi jika Anda menimpa [super viewWillAppear: YES]. Selain itu, itu harus bekerja.
Rambatino
18
Jika Anda mengganti animasi viewWillAppear: (BOOL), jangan lupa untuk memanggil [super viewWillAppear: animated]; :)
Médéric Petit
93

Fungsi yang melakukan pengguliran bisa lebih sederhana:

- (void) textFieldDidBeginEditing:(UITextField *)textField {
    UITableViewCell *cell;

    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
    // Load resources for iOS 6.1 or earlier
        cell = (UITableViewCell *) textField.superview.superview;

    } else {
        // Load resources for iOS 7 or later
        cell = (UITableViewCell *) textField.superview.superview.superview; 
       // TextField -> UITableVieCellContentView -> (in iOS 7!)ScrollView -> Cell!
    }
    [tView scrollToRowAtIndexPath:[tView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

Itu dia. Tidak ada perhitungan sama sekali.

pengguna91083
sumber
2
Dan kenapa tidak?! Cukup ganti UITableViewScrollPositionTop dengan UITableViewScrollPositionMiddle. Anda hanya perlu mengubah skala UITableView untuk menyesuaikan area yang terlihat, tentu saja.
Mihai Damian
3
Tampaknya tidak berfungsi jika UITableViewController menangani perubahan ukuran tampilan tabel saat keyboard ditampilkan: pengontrol mengurangi ukuran yang terlihat dengan a contentInset, yang tampaknya tidak diperhitungkan saat meminta visibleRowsatau indexPathsForVisibleRows.
Julian D.
16
Tidak berfungsi untuk beberapa baris terakhir tampilan tabel. Keyboard masih akan mengaburkan semua baris yang tidak dapat digulir di atas keyboard.
Alex Zavatone
3
Agar perilaku gulir otomatis berfungsi pada beberapa baris terakhir tabel, deteksi kapan baris ini mulai diedit, dan tambahkan footer ke ujung tampilan tabel dengan tampilan kosong ketinggian tertentu. Ini akan memungkinkan tampilan tabel untuk menggulir sel ke tempat yang benar.
Sammio2
10
Masuk ke sel melalui rantai panggilan ke superview tidak dapat diandalkan, kecuali Anda memastikan Anda benar-benar masuk ke sel. Lihat stackoverflow.com/a/17757851/1371070 dan stackoverflow.com/a/17758021/1371070
Cezar
70

Saya melakukan sesuatu yang sangat mirip dengan generik, tidak perlu menghitung sesuatu yang spesifik untuk kode Anda. Periksa saja komentar pada kode:

Di MyUIViewController.h

@interface MyUIViewController: UIViewController <UITableViewDelegate, UITableViewDataSource>
{
     UITableView *myTableView;
     UITextField *actifText;
}

@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) IBOutlet UITextField *actifText;

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField;
- (IBAction)textFieldDidEndEditing:(UITextField *)textField;

-(void) keyboardWillHide:(NSNotification *)note;
-(void) keyboardWillShow:(NSNotification *)note;

@end

Di MyUIViewController.m

@implementation MyUIViewController

@synthesize myTableView;
@synthesize actifText;

- (void)viewDidLoad 
{
    // Register notification when the keyboard will be show
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillShow:)
                                          name:UIKeyboardWillShowNotification
                                          object:nil];

    // Register notification when the keyboard will be hide
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillHide:)
                                          name:UIKeyboardWillHideNotification
                                          object:nil];
}

// To be link with your TextField event "Editing Did Begin"
//  memoryze the current TextField
- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.actifText = textField;
}

// To be link with your TextField event "Editing Did End"
//  release current TextField
- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.actifText = nil;
}

-(void) keyboardWillShow:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    // Start animation
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Reduce size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height -= keyboardBounds.size.height;
    else 
        frame.size.height -= keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    // Scroll the table view to see the TextField just above the keyboard
    if (self.actifText)
      {
        CGRect textFieldRect = [self.myTableView convertRect:self.actifText.bounds fromView:self.actifText];
        [self.myTableView scrollRectToVisible:textFieldRect animated:NO];
      }

    [UIView commitAnimations];
}

-(void) keyboardWillHide:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Increase size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height += keyboardBounds.size.height;
    else 
        frame.size.height += keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    [UIView commitAnimations];
}

@end

Versi Swift 1.2+:

class ViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var activeText: UITextField!
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillShow:"),
            name: UIKeyboardWillShowNotification,
            object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillHide:"),
            name: UIKeyboardWillHideNotification,
            object: nil)
    }

    func textFieldDidBeginEditing(textField: UITextField) {
        activeText = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        activeText = nil
    }

    func keyboardWillShow(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height -= keyboardSize.height
            tableView.frame = frame
            if activeText != nil {
                let rect = tableView.convertRect(activeText.bounds, fromView: activeText)
                tableView.scrollRectToVisible(rect, animated: false)
            }
            UIView.commitAnimations()
        }
    }

    func keyboardWillHide(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height += keyboardSize.height
            tableView.frame = frame
            UIView.commitAnimations()
        }
    }
}
ZeLegolas
sumber
menggunakan notifikasi dan mendapatkan ketinggian keyboard sambil memasukkan orientasi perangkat sangat mengagumkan, terima kasih untuk itu! bagian pengguliran tidak bekerja untuk saya untuk beberapa alasan, jadi saya harus menggunakan ini:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber
7
Ini adalah jawaban terbaik di sini menurut saya. Sangat bersih. Hanya dua hal: 1) viewDidLoad Anda tidak memanggil [super viewDidLoad] dan 2) saya harus memiliki dalam beberapa matematika tabbar pada garis frame.size.height. Jika tidak sempurna! Terima kasih.
toxaq
3
Berikut penjelasan toxaq modifikasi: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Kemudian kurangi tabBarHeight dari ketinggian keyboard di mana pun Anda menggunakan ketinggian keyboard.
Steve N
1
Jika pengguna mengetuk bidang teks, itu berfungsi dengan sempurna. tetapi jika pengguna mengetuk bidang teks lain tanpa menekan tombol kembali maka ukuran tabelnya berkurang.
Bhavin Ramani
1
@ BhavinRamani setuju. Saya menambahkan properti boolean sederhana untuk mengingat apakah keyboard sudah ditampilkan atau tidak, dan melewatkan kode eksekusi ulang ketika tidak perlu.
Dirty Henry
46

Solusi paling sederhana untuk Swift 3 , berdasarkan pada solusi Bartłomiej Semańczyk :

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

// MARK: Keyboard Notifications

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.2, animations: {
        // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    })
}
squall2022
sumber
Detail kecil ... Menggunakan Notificationalih-alih NSNotificationakan lebih "Swift 3-y" :-)
Nicolas Miari
Ini akan membantu dengan reposisi jika ada navbar - surround UIView.animate dengan ini jika let - if biarkan frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev
ketika rotasi terjadi ada kesalahan dalam memuat dan beberapa sel menghilang ketika tampilan tabel digulir secara manual
jothikenpachi
Solusi yang bagus, terima kasih! Catatan - tidak perlu lagi melakukan removeObserver.
Nick McConnell
44

Saya memiliki masalah yang sama tetapi memperhatikan bahwa itu hanya muncul dalam satu tampilan. Jadi saya mulai mencari perbedaan dalam pengontrol.

Saya menemukan bahwa perilaku bergulir diatur dalam - (void)viewWillAppear:(BOOL)animatedinstance super.

Jadi pastikan untuk menerapkan seperti ini:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // your code
}

Dan tidak masalah jika Anda menggunakan UIViewControlleratau UITableViewController; memeriksanya dengan menempatkan UITableViewsebagai subview dari self.view di UIViewController. Itu perilaku yang sama. Tampilan tidak memungkinkan untuk menggulir jika panggilan [super viewWillAppear:animated];tidak ada.

phse
sumber
1
Ini bekerja dengan sangat baik. Saya bertanya-tanya mengapa orang mengatakan UITableView akan melakukannya untuk saya dan ini menyelesaikannya. Terima kasih!
olivaresF
5
Saya punya masalah ini juga, jawaban ini harus naik ke atas!
Amiel Martin
Saya kehilangan banyak waktu untuk mencari tahu sendiri ... terima kasih;)
budidino
+1 mulai menangis sedikit, saya memiliki baris itu tetapi diperlukan juga [tableViewController viewWillAppear: animated]; karena saya menambahkan UITableViewController ke UIViewController. tidak ada lagi air mata :)
colin lamarre
41

Saya mungkin telah melewatkan ini, karena saya tidak membaca seluruh posting di sini, tetapi apa yang saya buat tampak sederhana. saya belum memasukkan ini melalui pemeras, pengujian dalam semua situasi, tetapi sepertinya itu akan berfungsi dengan baik.

cukup sesuaikan kontenInset tampilan tabel dengan ketinggian keyboard, dan kemudian gulir sel ke bawah:

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

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.myTableView.contentInset = contentInsets;
    self.myTableView.scrollIndicatorInsets = contentInsets;

    [self.myTableView scrollToRowAtIndexPath:self.currentField.indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

dan tentu saja

- (void)keyboardWasHidden:(NSNotification *)aNotification
{
    [UIView animateWithDuration:.3 animations:^(void) 
    {
        self.myTableView.contentInset = UIEdgeInsetsZero;
        self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
    }];
}

Apakah ini terlalu sederhana? apakah saya melewatkan sesuatu? sejauh ini bekerja untuk saya baik-baik saja, tetapi seperti yang saya katakan, saya belum memasukkannya ke alat pemeras ...

mickm
sumber
IMO, ini solusi terbaik. Satu-satunya hal yang saya ubah adalah durasi hardcoded Anda untuk[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy
Ini sangat sederhana. Tetapi satu masalah yang saya temukan adalah bahwa itu tidak akan menghidupkan perubahan contentInsetdan secara tajam mengubah batas gulir.
Geek
Yang ini bekerja paling baik untuk saya, namun, beberapa masalah. 1) Saya tidak tahu dari mana Anda bisa mendapatkan "currentField.indexPath", jadi saya harus menyimpan indexPath.row sebagai tag isian dan membuat indexPath nanti. 2) Tidak berfungsi untuk baris di bagian atas tabel, itu menggulirkannya dari layar. Harus menambahkan beberapa kode hanya gulir jika indexPath currentField lebih besar dari apa yang bisa muat di layar. 3) harus menggunakan kbSize.Width (bukan ketinggian) di iPad jika lanskap
Travis M.
maaf, kita menjadi begitu terbiasa dengan kode kita sendiri sehingga kita terkadang lupa, eh? currentField adalah bidang teks saat ini yang sedang saya kerjakan, dan indexPath adalah ekstensi yang saya tambahkan ke kelas yang hanya menambahkan NSIndexPath jadi saya tahu sel apa ini.
mickm
Ini adalah cara yang harus ditempuh, bukan memindahkan bingkai hanya dengan memodifikasi properti tabel.
Nextorlg
35

Saya pikir saya telah menemukan solusi untuk mencocokkan perilaku aplikasi Apple.

Pertama, di viewWillAppear Anda: berlangganan notifikasi keyboard, sehingga Anda tahu kapan keyboard akan ditampilkan dan disembunyikan, dan sistem akan memberi tahu Anda ukuran keyboard, tetapi jangan lupa untuk membatalkan registrasi di viewWillDisappear :.

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

Terapkan metode yang mirip dengan di bawah ini sehingga Anda menyesuaikan ukuran tableView Anda agar sesuai dengan area yang terlihat setelah keyboard ditampilkan. Di sini saya melacak keadaan keyboard secara terpisah sehingga saya dapat memilih kapan untuk mengatur sendiri tableView kembali ke ketinggian penuh, karena Anda mendapatkan notifikasi ini pada setiap perubahan bidang. Jangan lupa untuk mengimplementasikan keyboardWillHide: dan pilih tempat yang sesuai untuk memperbaiki ukuran tableView Anda.

-(void) keyboardWillShow:(NSNotification *)note
{
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
    keyboardHeight = keyboardBounds.size.height;
    if (keyboardIsShowing == NO)
    {
        keyboardIsShowing = YES;
        CGRect frame = self.view.frame;
        frame.size.height -= keyboardHeight;

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.3f];
        self.view.frame = frame;
        [UIView commitAnimations];
    }
}

Sekarang di sini bit pengguliran, kita kerjakan beberapa ukuran terlebih dahulu, kemudian kita lihat di mana kita berada di area yang terlihat, dan atur rect yang ingin kita gulir menjadi setengah tampilan di atas atau di bawah bagian tengah bidang teks berdasarkan di tempat itu dalam tampilan. Dalam hal ini, kami memiliki larik UITextFields dan enum yang melacaknya, jadi mengalikan barisHeight dengan nomor baris memberi kami offset bingkai yang sebenarnya dalam tampilan luar ini.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame = textField.frame;
    CGFloat rowHeight = self.tableView.rowHeight;
    if (textField == textFields[CELL_FIELD_ONE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_ONE;
    }
    else if (textField == textFields[CELL_FIELD_TWO])
    {
        frame.origin.y += rowHeight * CELL_FIELD_TWO;
    }
    else if (textField == textFields[CELL_FIELD_THREE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_THREE;
    }
    else if (textField == textFields[CELL_FIELD_FOUR])
    {
        frame.origin.y += rowHeight * CELL_FIELD_FOUR;
    }
    CGFloat viewHeight = self.tableView.frame.size.height;
    CGFloat halfHeight = viewHeight / 2;
    CGFloat midpoint = frame.origin.y + (textField.frame.size.height / 2);
    if (midpoint < halfHeight)
    {
        frame.origin.y = 0;
        frame.size.height = midpoint;
    }
    else
    {
        frame.origin.y = midpoint;
        frame.size.height = midpoint;
    }
    [self.tableView scrollRectToVisible:frame animated:YES];
}

Ini tampaknya bekerja dengan sangat baik.

Michael Baltaks
sumber
Solusi bagus Terima kasih telah mempostingnya.
Alex Reynolds
2
UIKeyboardBoundsUserInfoKeysudah ditinggalkan sejak iOS 3.2. Lihat solusi saya di bawah ini yang bekerja di semua rilis iOS saat ini ≥ 3.0. / @ iPhoneDev
Ortwin Gentz
Ini lebih rumit dari yang seharusnya. Jawaban @ user91083 sederhana dan berfungsi.
Richard Brightwell
1
Ada masalah kecil dalam solusi ini. keyboardWillShow disebut SETELAH textFieldDidBeginEditing, jadi ketika kita ingin menggulir ke beberapa sel, bingkai tableView belum berubah, jadi itu tidak akan berfungsi
HiveHicks
35

Jika Anda dapat menggunakan UITableViewController, Anda mendapatkan fungsionalitas secara gratis. Namun, kadang-kadang, ini bukan opsi, khususnya jika Anda memerlukan banyak tampilan, bukan hanya UITableView.

Beberapa solusi yang disajikan di sini tidak bekerja di iOS ≥4, beberapa tidak berfungsi di iPad atau dalam mode lansekap, beberapa tidak bekerja untuk keyboard Bluetooth (di mana kami tidak ingin ada pengguliran), beberapa tidak berfungsi saat beralih di antara beberapa bidang teks. Jadi, jika Anda memilih solusi apa pun, pastikan untuk menguji kasus ini. Ini adalah solusi yang kami gunakan digunakan di InAppSettingsKit :

- (void)_keyboardWillShow:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
        NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
        if (!keyboardFrameValue) {
            keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
        }

        // Reduce the tableView height by the part of the keyboard that actually covers the tableView
        CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            windowRect = IASKCGRectSwap(windowRect);
        }
        CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        }
        CGRect frame = _tableView.frame;
        frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = frame;
        [UIView commitAnimations];

        UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
        NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

        // iOS 3 sends hide and show notifications right after each other
        // when switching between textFields, so cancel -scrollToOldPosition requests
        [NSObject cancelPreviousPerformRequestsWithTarget:self];

        [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
    }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)_keyboardWillHide:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = self.view.bounds;
        [UIView commitAnimations];

        [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
    }
}   

Berikut kode lengkap kelas di InAppSettingsKit. Untuk mengujinya, gunakan panel anak "Daftar Lengkap" tempat Anda dapat menguji skenario yang disebutkan di atas.

Ortwin Gentz
sumber
Saya tidak tahu apakah berguna menggunakan string daripada konstanta, karena jika Apple datang untuk mengubah String secara internal karena beberapa alasan, solusi Anda tidak berfungsi lagi. Demikian juga Anda tidak mendapatkan peringatan ketika menjadi usang. Saya pikir
@ iPortable: tidak ideal, saya tahu. Bisakah Anda menyarankan solusi yang lebih baik yang berjalan di semua versi ≥3.0?
Ortwin Gentz
1
Bekerja seperti pesona, tetapi tidak untuk UIInterfaceOrientationPortraitUpsideDown. Maka perhitungan pengurangan ketinggian harus didasarkan terbalik juga: CGFloat reductionHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect);
Klaas
Ini memiliki gangguan visual yang sangat mencolok di iPad dan Simulator saya (4.3). Terlalu mencolok untuk digunakan. :(
Bob Spryn
Saya suka bahwa solusi ini akan menjelaskan toolbar di bagian bawah layar.
pdemarest
24

Solusi paling sederhana untuk Swift :

override func viewDidLoad() {
    super.viewDidLoad()

    searchBar?.becomeFirstResponder()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
            tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) })
    // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
    }
Bartłomiej Semańczyk
sumber
Bekerja dengan sempurna, perhitungan minimum diperlukan. Saya telah menambahkan beberapa kode yang mengembalikan insets tabel kembali untuk membuat jawaban ini selesai.
Vitalii
Solusi terbaik, terima kasih. Saya telah memposting versi Swift 3 di sini: stackoverflow.com/a/41040630/1064438
squall2022
Solusi super sempurna yang pernah dilihat, saya mencoba yang lain tetapi memiliki beberapa masalah. Solusi Anda berfungsi sempurna di iOS 10.2.
Wangdu Lin
8

Saya harap kalian sudah mendapat solusi membaca semua itu. Tetapi saya menemukan solusi saya sebagai berikut. Saya berharap Anda sudah memiliki sel UITextField. Jadi pada persiapan, pertahankan saja indeks baris ke dalam tag bidang teks.

cell.textField.tag = IndexPath.row;

Buat activeTextFieldinstance UITextFielddengan lingkup global seperti di bawah ini:

@interface EditViewController (){

    UITextField *activeTextField;

}

Jadi, sekarang Anda tinggal menyalin paste kode saya di akhir. Dan juga jangan lupa menambahkanUITextFieldDelegate

#pragma mark - TextField Delegation

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

    activeTextField = textField;

    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField{

    activeTextField = nil;

}

Mendaftarkan keyboard notifications

#pragma mark - Keyboard Activity

- (void)registerForKeyboardNotifications

{

    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWasShown:)

                                             name:UIKeyboardDidShowNotification object:nil];



    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWillBeHidden:)

                                             name:UIKeyboardWillHideNotification object:nil];



}

Menangani Keyboard Notifications:

Dipanggil ketika UIKeyboardDidShowNotificationdikirim.

- (void)keyboardWasShown:(NSNotification*)aNotification

{

    NSDictionary* info = [aNotification userInfo];

    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

    NSIndexPath *currentRowIndex = [NSIndexPath indexPathForRow:activeTextField.tag inSection:0];

    [self.tableView scrollToRowAtIndexPath:currentRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

}

Disebut saat itu UIKeyboardWillHideNotification dikirim

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Sekarang satu hal yang tersisa, Panggil registerForKeyboardNotifications metode ke ViewDidLoadmetode sebagai berikut:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Anda selesai, semoga Anda textFieldstidak lagi disembunyikan oleh keyboard.

Hussain KMR Behestee
sumber
6

Menggabungkan dan mengisi bagian yang kosong dari beberapa jawaban (khususnya Ortwin Gentz, pengguna 98013) dan pos lainnya, ini akan berfungsi di luar kotak untuk SDK 4.3 pada iPad dalam mode Portrait atau Landscape:

@implementation UIView (FindFirstResponder)
- (UIResponder *)findFirstResponder
{
  if (self.isFirstResponder) {        
    return self;     
  }

  for (UIView *subView in self.subviews) {
    UIResponder *firstResponder = [subView findFirstResponder];
    if (firstResponder != nil) {
      return firstResponder;
    }
  }

  return nil;
}
@end

@implementation MyViewController

- (UIResponder *)currentFirstResponder {
  return [self.view findFirstResponder];
}

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

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

- (void)textFieldDidBeginEditing:(UITextField *)textField {
  UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
  [_tableView scrollToRowAtIndexPath:[_tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillShow:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {
    NSDictionary* userInfo = [notification userInfo];

    // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
    NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
    if (!keyboardFrameValue) {
      keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
    }

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect frame = _tableView.frame;
    if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
      windowRect = CGRectMake(windowRect.origin.y, windowRect.origin.x, windowRect.size.height, windowRect.size.width);
      viewRectAbsolute = CGRectMake(viewRectAbsolute.origin.y, viewRectAbsolute.origin.x, viewRectAbsolute.size.height, viewRectAbsolute.size.width);
    }
    frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = frame;
    [UIView commitAnimations];

    UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
    NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

    // iOS 3 sends hide and show notifications right after each other
    // when switching between textFields, so cancel -scrollToOldPosition requests
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    _topmostRowBeforeKeyboardWasShown = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
    [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {

    NSDictionary* userInfo = [notification userInfo];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = self.view.bounds;
    [UIView commitAnimations];

    [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
  }
}   

@end
Colin
sumber
Saya menggunakan kode ini di iOS 4.x baik-baik saja, tetapi di iOS5 itu crash di scrollToOldPosition karena _topmostRowBeforeKeyboardWasShown sudah dibebaskan pada waktu itu sudah. Belum yakin apa solusinya. Mungkin ingat indeks, bukan objek.
Thomas Tempelmann
5

Jika Anda menggunakan tampilan uitable untuk menempatkan bidang teks Anda ( dari Jeff Lamarche ), Anda bisa menggulir tampilan tabel menggunakan metode delegasi seperti itu.

(Catatan: bidang teks saya disimpan dalam array dengan indeks yang sama dengan baris yang ada di tampilan tabel)

- (void) textFieldDidBeginEditing:(UITextField *)textField
    {

        int index;
        for(UITextField *aField in textFields){

            if (textField == aField){
                index = [textFields indexOfObject:aField]-1;
            }
        }

         if(index >= 0) 
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];

        [super textFieldDidBeginEditing:textField];
    }
Corey Floyd
sumber
Anda tidak memperbarui bingkai tableView. Kemudian, scrollBars dan perilaku scrolling salah saat keyboard ditampilkan. Lihat solusi saya.
Ortwin Gentz
5

Notifikasi keyboard berfungsi, tetapi kode sampel Apple untuk itu mengasumsikan bahwa tampilan gulir adalah tampilan root dari jendela. Ini biasanya tidak demikian. Anda harus mengimbangi bilah tab, dll., Untuk mendapatkan offset yang tepat.

Itu lebih mudah daripada kedengarannya. Berikut adalah kode yang saya gunakan dalam UITableViewController. Ini memiliki dua variabel instan, hiddenRect dan keyboardShown.

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
    if (keyboardShown)
        return;

    NSDictionary* info = [aNotification userInfo];

    // Get the frame of the keyboard.
    NSValue *centerValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
    NSValue *boundsValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    CGPoint keyboardCenter = [centerValue CGPointValue];
    CGRect keyboardBounds = [boundsValue CGRectValue];
    CGPoint keyboardOrigin = CGPointMake(keyboardCenter.x - keyboardBounds.size.width / 2.0,
                                         keyboardCenter.y - keyboardBounds.size.height / 2.0);
    CGRect keyboardScreenFrame = { keyboardOrigin, keyboardBounds.size };


    // Resize the scroll view.
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = scrollView.frame;
    CGRect keyboardFrame = [scrollView.superview convertRect:keyboardScreenFrame fromView:nil];
    hiddenRect = CGRectIntersection(viewFrame, keyboardFrame);

    CGRect remainder, slice;
    CGRectDivide(viewFrame, &slice, &remainder, CGRectGetHeight(hiddenRect), CGRectMaxYEdge);
    scrollView.frame = remainder;

    // Scroll the active text field into view.
    CGRect textFieldRect = [/* selected cell */ frame];
    [scrollView scrollRectToVisible:textFieldRect animated:YES];

    keyboardShown = YES;
}


// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
    // Reset the height of the scroll view to its original value
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = [scrollView frame];
    scrollView.frame = CGRectUnion(viewFrame, hiddenRect);

    keyboardShown = NO;
}
Donovan Voss
sumber
UIKeyboardCenterEndUserInfoKeydan UIKeyboardBoundsUserInfoKeysudah tidak digunakan lagi di iOS 3.2. Lihat solusi saya di bawah ini yang bekerja di semua rilis iOS saat ini ≥ 3.0.
Ortwin Gentz
5

Jika Anda menggunakan Three20, maka gunakan autoresizesForKeyboardproperti. Cukup setel pada -initWithNibName:bundlemetode pengontrol tampilan Anda

self.autoresizesForKeyboard = YES

Ini menangani:

  1. Mendengarkan notifikasi keyboard dan menyesuaikan bingkai tampilan tabel
  2. Menggulir ke responden pertama

Dilakukan dan dilakukan.

Pikirkan Top Down
sumber
Apa yang dimaksud dengan Three20 di sini? Bisakah Anda menentukannya?
Mubin Mall
5

Pendekatan saya:

Saya pertama-tama subkelas UITextField dan menambahkan properti indexPath. Di cellFor ... Metode saya menyerahkan properti indexPath.

Lalu saya tambahkan kode berikut:

UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:textField.indexPath];

CGPoint cellPoint = [cell convertPoint:textField.center toView:self.tableView];
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, cellPoint.y-50);}];

ke textFieldShould / WillBegin ... dll.

Ketika Keyboard menghilang, Anda harus membaliknya dengan:

[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];
Sven
sumber
4

Solusi yang lebih stream-lined. Itu tergelincir ke dalam metode delegasi UITextField, sehingga tidak perlu mengacaukan notifikasi UIKeyboard.

Catatan implementasi:

kSettingsRowHeight - ketinggian UITableViewCell.

offsetTarget dan offsetThreshold didukung dari kSettingsRowHeight. Jika Anda menggunakan ketinggian baris yang berbeda, atur nilai-nilai itu ke properti titik y. [alt: hitung offset baris dengan cara yang berbeda.]

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget    = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 2nd-to-last row)

CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
if (point.y > offsetThreshold) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y + kSettingsRowHeight,
                      frame.size.width,
                      frame.size.height);
} else if (point.y > offsetTarget) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y,
                      frame.size.width,
                      frame.size.height);
} else {
    self.tableView.frame = CGRectMake(0.0f,
                      0.0f,
                      frame.size.width,
                      frame.size.height);
}

[UIView commitAnimations];

return YES;

}

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

[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
self.tableView.frame = CGRectMake(0.0f,
                  0.0f,
                  frame.size.width,
                  frame.size.height);

[UIView commitAnimations];

return YES;

}

Kelvin
sumber
4

Gunakan UITextField's delegatemetode:

Cepat

func textFieldShouldBeginEditing(textField: UITextField) -> bool {
  let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere)
  let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition)
  if indexPath != nil {
     yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true)
  }
  return true
}

Objektif-C

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
  CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere];
  NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition));
  NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];

  if (indexPath != nil) {
     [yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
  return YES;
}
Paresh Navadiya
sumber
Hai, saya mengalami masalah agar ini berfungsi di Swift. UITextFields saya terhubung ke UITableViewCell. Jika saya menerapkan kode ini di dalam UIViewController saya, saya tidak memiliki akses ke UITextFields. Ada ide?
Vetuka
4

Solusi lengkap Swift 4.2

Saya telah membuat GIST dengan seperangkat protokol yang menyederhanakan pekerjaan dengan menambahkan ruang ekstra saat keyboard ditampilkan, disembunyikan, atau diubah.

Fitur :

  • Bekerja dengan benar dengan perubahan bingkai keyboard (mis. Perubahan ketinggian keyboard seperti emojii → keyboard normal).
  • Dukungan TabBar & ToolBar untuk contoh UITableView (pada contoh lain Anda menerima inset yang salah).
  • Durasi animasi dinamis (bukan kode keras).
  • Pendekatan berorientasi protokol yang dapat dengan mudah dimodifikasi untuk tujuan Anda.

Pemakaian

Contoh penggunaan dasar dalam pengontrol tampilan yang berisi beberapa tampilan gulir (tampilan tabel tentu saja didukung).

class SomeViewController: UIViewController {
  @IBOutlet weak var scrollView: UIScrollView!

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    addKeyboardFrameChangesObserver()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    removeKeyboardFrameChangesObserver()
  }
}

extension SomeViewController: ModifableInsetsOnKeyboardFrameChanges {
  var scrollViewToModify: UIScrollView { return scrollView }
}

Inti: pengamat perubahan bingkai

Protokol KeyboardChangeFrameObserverakan memecat acara setiap kali bingkai keyboard diubah (termasuk menampilkan, menyembunyikan, mengubah bingkai).

  1. Panggil addKeyboardFrameChangesObserver()di viewWillAppear()atau metode serupa.
  2. Panggil removeKeyboardFrameChangesObserver()di viewWillDisappear()atau metode serupa.

Implementasi: tampilan gulir

ModifableInsetsOnKeyboardFrameChangesprotokol menambahkan UIScrollViewdukungan ke protokol inti. Ini mengubah insets tampilan gulir ketika bingkai keyboard diubah.

Kelas Anda perlu mengatur tampilan gulir, insets seseorang akan bertambah / berkurang pada perubahan bingkai keyboard.

var scrollViewToModify: UIScrollView { get }
Vasily
sumber
3

Karena Anda memiliki bidang teks dalam tabel, cara terbaik sebenarnya adalah mengubah ukuran tabel - Anda perlu mengatur ketinggian tableView.frame menjadi lebih kecil dengan ukuran keyboard (saya pikir sekitar 165 piksel) dan kemudian perluas lagi ketika keyboard diberhentikan.

Anda juga dapat menonaktifkan interaksi pengguna untuk tableView pada saat itu, jika Anda tidak ingin pengguna menggulir.

Kendall Helmstetter Gelner
sumber
Saya kedua ini, dan mendaftar untuk UIKeyboardWillShowNotification untuk menemukan ukuran keyboard secara dinamis.
benzado
Namun, nomor yang dikembalikan oleh objek notifikasi tidak berfungsi. Atau setidaknya tidak di 2.2, nomor yang dikembalikan tidak benar dan saya harus hard-code nilai 165 untuk menyesuaikan ketinggian dengan benar (itu dimatikan oleh lima hingga sepuluh piksel)
Kendall Helmstetter Gelner
2

Ini berfungsi dengan baik, dan juga pada iPad.

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

    if(textField == textfield1){
            [accountName1TextField becomeFirstResponder];
        }else if(textField == textfield2){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield3 becomeFirstResponder];

        }else if(textField == textfield3){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield4 becomeFirstResponder];

        }else if(textField == textfield4){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield5 becomeFirstResponder];

        }else if(textField == textfield5){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield6 becomeFirstResponder];

        }else if(textField == textfield6){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:4 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield7 becomeFirstResponder];

        }else if(textField == textfield7){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield8 becomeFirstResponder];

        }else if(textField == textfield8){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield9 becomeFirstResponder];

        }else if(textField == textfield9){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:7 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textField resignFirstResponder];
        }
WrightsCS
sumber
Mengapa Anda mencari dan menggunakan kasus khusus untuk setiap bidang teks? ID setiap bidang teks dari NSIndexPath sel dan ubah pernyataan jahat itu menjadi 2 baris kode. Anda benar-benar menginginkan panggilan cellForRowAtIndexPath dan kemudian dapatkan textField dari sel.
Alex Zavatone
Sebenarnya mengingat betapa sangat flakeynya situasi ini di iOS, saya pikir tidak apa-apa untuk menulis kode "sepenuhnya tidak dicabut, sangat harfiah" untuk situasi ini.
Fattie
Mempertimbangkan jawaban ini diberikan lebih dari 6 tahun yang lalu.
WrightsCS
2

Saya mencoba pendekatan yang hampir sama dan menghasilkan kode yang lebih sederhana dan lebih kecil untuk hal yang sama. Saya membuat iTextView IBOutlet dan dikaitkan dengan UITextView di IB.

 -(void)keyboardWillShow:(NSNotification *)notification
    {
        NSLog(@"Keyboard");
        CGRect keyFrame = [[[notification userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];

        [UIView beginAnimations:@"resize view" context:nil];
        [UIView setAnimationCurve:1];
        [UIView setAnimationDuration:1.0];
        CGRect frame = iTableView.frame;
        frame.size.height = frame.size.height -  keyFrame.size.height;
        iTableView.frame = frame;
        [iTableView scrollRectToVisible:frame animated:YES];
        [UIView commitAnimations];

    }
musim panas ini
sumber
2

Jadi setelah berjam-jam pekerjaan yang melelahkan mencoba menggunakan solusi saat ini (dan benar-benar gagal) saya akhirnya berhasil dengan baik, dan memperbaruinya untuk menggunakan blok animasi baru. Jawaban saya sepenuhnya berdasarkan jawaban Ortwin di atas .

Jadi untuk alasan apa pun kode di atas tidak berfungsi untuk saya. Pengaturan saya sepertinya cukup mirip dengan yang lain, tapi mungkin karena saya menggunakan iPad atau 4.3 ... tidak tahu. Itu melakukan beberapa matematika aneh dan menembak tampilan meja saya dari layar.

Lihat hasil akhir dari solusi saya: http://screencast.com/t/hjBCuRrPC (Harap abaikan foto. :-P)

Jadi saya pergi dengan intisari dari apa yang dilakukan Ortwin, tetapi mengubah bagaimana melakukan beberapa matematika untuk menjumlahkan asalnya. Y & ukuran. Tinggi tampilan meja saya dengan ketinggian keyboard. Ketika saya mengurangi ketinggian jendela dari hasil itu, ia memberi tahu saya berapa banyak persimpangan yang telah saya lakukan. Jika lebih besar dari 0 (alias ada beberapa tumpang tindih) saya melakukan animasi ketinggian bingkai.

Selain itu ada beberapa masalah menggambar ulang yang diselesaikan dengan 1) Menunggu untuk menggulir ke sel sampai animasi selesai dan 2) menggunakan opsi UIViewAnimationOptionBeginFromCurrentState saat menyembunyikan keyboard.

Beberapa hal yang perlu diperhatikan.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame adalah variabel instan yang dideklarasikan di header.
  • self.guestEntryTableView adalah tableView saya (saya menggunakan file eksternal)
  • IASKCGRectSwap adalah metode Ortwin untuk membalik koordinat suatu bingkai
  • Saya hanya memperbarui ketinggian tableView jika setidaknya 50px akan ditampilkan
  • Karena saya tidak berada dalam UIViewController, saya tidak memiliki self.view, jadi saya hanya mengembalikan tableView ke bingkai aslinya

Sekali lagi, saya tidak akan mendekati jawaban ini jika saya Ortwin tidak memberikan inti dari itu. Ini kodenya:

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.activeTextField = textField;

    if ([self.guestEntryTableView indexPathsForVisibleRows].count) {
        _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[self.guestEntryTableView indexPathsForVisibleRows] objectAtIndex:0];
    } else {
        // this should never happen
        _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
        [textField resignFirstResponder];
    }
}

- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.activeTextField = nil;
}

- (void)keyboardWillShow:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];

    NSValue* keyboardFrameValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
    if (UIInterfaceOrientationLandscapeLeft == orientation ||UIInterfaceOrientationLandscapeRight == orientation ) {
        windowRect = IASKCGRectSwap(windowRect);
        viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        keyboardFrame = IASKCGRectSwap(keyboardFrame);
    }

    // fix the coordinates of our rect to have a top left origin 0,0
    viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height);

    CGRect frame = self.guestEntryTableView.frame;
    _originalFrame = self.guestEntryTableView.frame;

    int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height;

    if (remainder > 0 && !(remainder > frame.size.height + 50)) {
        frame.size.height = frame.size.height - remainder;
        float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
        [UIView animateWithDuration: duration
                        animations:^{
                            self.guestEntryTableView.frame = frame;
                        }
                        completion:^(BOOL finished){
                            UITableViewCell *textFieldCell = (UITableViewCell*) [[self.activeTextField superview] superview];
                            NSIndexPath *textFieldIndexPath = [self.guestEntryTableView indexPathForCell:textFieldCell];
                            [self.guestEntryTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
                        }];
    }

}

- (void)keyboardWillHide:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];
    float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration: duration
                          delay: 0.0
                        options: (UIViewAnimationOptionBeginFromCurrentState)
                     animations:^{
                         self.guestEntryTableView.frame = _originalFrame;
                     }
                     completion:^(BOOL finished){
                         [self.guestEntryTableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
                     }];

}   

#pragma mark CGRect Utility function
CGRect IASKCGRectSwap(CGRect rect) {
    CGRect newRect;
    newRect.origin.x = rect.origin.y;
    newRect.origin.y = rect.origin.x;
    newRect.size.width = rect.size.height;
    newRect.size.height = rect.size.width;
    return newRect;
}

CGRect FixOriginRotation(CGRect rect, UIInterfaceOrientation orientation, int parentWidth, int parentHeight) {
    CGRect newRect;
    switch(orientation)
    {
        case UIInterfaceOrientationLandscapeLeft:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), rect.origin.y, rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationLandscapeRight:
            newRect = CGRectMake(rect.origin.x, parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationPortrait:
            newRect = rect;
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
    }
    return newRect;
}
Bob Spryn
sumber
Menambahkan fungsi FixOriginRotation saya yang memperbaiki sistem koordinat tampilan sebelum Anda memperbarui bingkai dll. Saya pikir ini adalah bagian dari mengapa saya mengalami masalah pada awalnya. Tidak menyadari Sistem Koordinasi Jendela iOS Diputar dengan perangkat!
Bob Spryn
2

Soluton ini berfungsi untuk saya, HARAP perhatikan barisnya

[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];

Anda dapat mengubah nilai 160 agar cocok dengan itu bekerja dengan Anda

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
                        bkgndRect.size.height += kbSize.height;
     [activeField.superview setFrame:bkgndRect];
     [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
   activeField = textField;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
 {
     activeField = nil;
 }
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    tableView.contentInset = contentInsets;
    tableView.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
    //bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES];
}
hai
sumber
2

Diskusi thread yang sangat menarik, saya juga menghadapi masalah yang sama mungkin lebih buruk karena

  1. Saya menggunakan sel khusus dan bidang teks di dalamnya.
  2. Saya harus menggunakan UIViewController untuk memenuhi persyaratan saya sehingga tidak dapat memanfaatkan UITableViewController.
  3. Saya memiliki filter / semacam kriteria dalam sel tabel saya, yaitu sel-sel Anda terus berubah dan melacak indexpath dan semua tidak akan membantu.

Jadi baca utasnya di sini dan terapkan versi saya, yang membantu saya mendorong konten saya di iPad dalam mode lansekap . Berikut ini kode (ini bukan bukti bodoh dan semuanya, tetapi ini memperbaiki masalah saya) Pertama, Anda perlu memiliki delegasi di kelas sel khusus Anda, yang saat pengeditan dimulai, mengirimkan textfield ke ur viewcontroller Anda dan mengatur activefield = theTextField di sana

// DITERAPKAN HANYA UNTUK MENANGANI MODEL LANDSCAPE

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect aRect = myTable.frame;

    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);

    aRect.size.height -= kbSize.height+50;
// This will the exact rect in which your textfield is present
        CGRect rect =  [myTable convertRect:activeField.bounds fromView:activeField];
// Scroll up only if required
    if (!CGRectContainsPoint(aRect, rect.origin) ) {


            [myTable setContentOffset:CGPointMake(0.0, rect.origin.y) animated:YES];

    }


}

// Dipanggil ketika UIKeyboardWillHideNotification dikirim

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    myTable.contentInset = contentInsets;
    myTable.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);
    CGRect bkgndRect = activeField.superview.frame;
    bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [myTable setContentOffset:CGPointMake(0.0, 10.0) animated:YES];
}

-anoop4real

anoop4real
sumber
2

Saya baru saja menyelesaikan masalah seperti itu sendiri setelah saya merujuk sejumlah besar solusi yang ditemukan melalui Google dan Stack Overflow.

Pertama, harap pastikan bahwa Anda telah menyiapkan IBOutlet dari UIScrollView Anda, Kemudian silakan lihat lebih dekat di Apple Doc: Keyboard Management . Terakhir, jika Anda dapat menggulir latar belakang, tetapi keyboard masih menutupi Bidang Teks, silakan lihat bagian kode ini:

// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;

if (aRect.size.height < activeField.frame.origin.y+activeField.frame.size.height) {

    CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y+activeField.frame.size.height-aRect.size.height);

    [scrollView setContentOffset:scrollPoint animated:YES];

Perbedaan utama antara bagian ini dan Apple terletak pada kondisi if. Saya percaya perhitungan jarak gulir dan kondisi apel apakah bidang teks yang dicakup oleh keyboard tidak akurat, jadi saya membuat modifikasi seperti di atas.

Beri tahu saya jika berhasil

dumbfingers
sumber
2

Contoh di Swift, menggunakan titik persis bidang teks dari Dapatkan indexPath dari UITextField di UITableViewCell dengan Swift :

func textFieldDidBeginEditing(textField: UITextField) {
    let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView)
    let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable)
    accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true)
}
rapi
sumber
1

Metode mudah lainnya (hanya bekerja dengan satu bagian)

//cellForRowAtIndexPath
UItextField *tf;
[cell addSubview:tf];
tf.tag = indexPath.row;
tf.delegate = self;

//textFieldDidBeginEditing:(UITextField *)text
[[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] animated:YES];
JonasG
sumber
1

Jika UITableView Anda dikelola oleh subkelas dari UITableViewController dan bukan UITableView, dan delegasi bidang teks adalah UITableViewController, ia harus mengelola semua pengguliran secara otomatis - semua komentar lain ini sangat sulit diterapkan dalam praktiknya.

Untuk contoh yang baik, lihat proyek kode contoh apel: TaggedLocations.

Anda dapat melihatnya menggulir secara otomatis, tetapi sepertinya tidak ada kode yang melakukan ini. Proyek ini juga memiliki sel tampilan tabel khusus, jadi jika Anda membangun aplikasi Anda dengan itu sebagai panduan, Anda harus mendapatkan hasil yang diinginkan.

shim
sumber
1

Inilah cara saya membuat karya ini, yang merupakan campuran dari jawaban Sam Ho dan Marcel W, dan beberapa perbaikan bug saya dibuat untuk kode jelek saya. Saya menggunakan UITableViewController. Tabel sekarang mengubah ukuran dengan benar ketika keyboard ditampilkan.

1) Dalam viewDidLoadsaya menambahkan:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Saya lupa menyebut superpadanan dalam viewWillAppeardan awakeFromNib. Saya menambahkan ini kembali.

Danyal Aytekin
sumber
1

UITableViewControllerapakah Scrolling secara otomatis, memang. Perbedaannya dibandingkan dengan menggunakan a UIViewControlleradalah, bahwa Anda harus membuat Navbar-Buttonitems secara terprogram dengan menggunakan NavigationController, saat menggunakan a TableViewController.

Tom01
sumber