Створення прокрутки UITableView, коли вибрано текстове поле


251

Після безлічі спроб і помилок я здаюсь і задаю питання. Я бачив багато людей з подібними проблемами, але не можу отримати всі відповіді, щоб правильно працювати.

Я маю UITableView який складається з користувацьких комірок. Осередки складаються з 5 текстових полів поруч (на зразок сітки).

Коли я намагаюся прокручувати та редагувати комірки внизу поля UITableView , я не можу отримати свої комірки належним чином розташовані над клавіатурою.

Я бачив багато відповідей, які говорять про зміну розмірів перегляду тощо ..., але жоден з них не працював чудово до цих пір.

Чи може хтось уточнити "правильний" спосіб зробити це на прикладі конкретного коду?


11
У цій документації на Applle викладені кроки щодо впровадження рішення цього питання. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Це посилання зазначає, що воно не оновлено для iOS 4.0
Bae

Цей код може бути корисним: gist.github.com/TimMedcalf/9505416
landonandrey

Дотримуйтесь нижче URL-адреси, вона працюватиме: stackoverflow.com/questions/48922266/…
Venkatesh G

Відповіді:


126

Якщо ви використовуєте UITableViewController замість UIViewController, це автоматично зробить це.


13
Ви спробували і виявили, що це не працює? Або рішення занадто просте для вас, щоб повірити? Просто розгорніть UITableViewController замість UIViewController, і комірка, що містить текстові поля, буде прокручуватися над клавіатурою, коли текстові поля стануть першим відповіддю. Не потрібен додатковий код.
Сем Хо

3
Так, але особливо на iPad нам потрібен спосіб зробити це, що не передбачає UITableViewController.
Боб Сприн

13
Для уточнення, не є розумною відповіддю сказати, що кожен раз, коли ви використовуєте перегляд таблиці, він повинен бути повноекранним, особливо на iPad. Є орди прикладів чудових додатків, які цього не роблять. Наприклад, багато власних Apple, включаючи додаток "Контакти" на iPad.
Боб Спрін

32
Він не працюватиме, якщо ви перекриєте [super viewWillAppear: YES]. Крім цього, це повинно працювати.
Рамбатіно

18
Якщо ви перекриєте анімований viewWillAppear: (BOOL), не забудьте зателефонувати [super viewWillAppear: анімація]; :)
Médéric Petit

93

Функція прокрутки може бути набагато простішою:

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

Це воно. Ніяких розрахунків взагалі немає.


2
А чому ні ?! Просто замініть UITableViewScrollPositionTop на UITableViewScrollPositionMiddle. Вам потрібно просто змінити масштаб UITableView, щоб, звичайно, відрегулювати видиму область.
Міхай Даміан

3
Схоже, це не працює, якщо UITableViewController подбав про розмір таблиці, коли розмір клавіатури відображається: контролер зменшує видимий розмір за допомогою а contentInset, що, мабуть, не враховується при запиті visibleRowsабо indexPathsForVisibleRows.
Джуліан Д.

16
Не працює останні кілька рядків подання таблиці. Клавіатура все одно затьмарить усі рядки, які неможливо прокрутити над клавіатурою.
Алекс Заватоне

3
Щоб поведінка автоматичної прокрутки працювала над останніми рядками таблиці, виявіть, коли ці рядки почнуть редагувати, та додайте нижній колонтитул до кінця огляду таблиці з порожнім видом певної висоти. Це дозволить перегляду таблиці прокручувати комірки в потрібне місце.
Самміо2

10
Дістатися до клітини через ланцюжок дзвінків на перегляд є недостовірним, якщо ви не переконаєтесь, що дійсно потрапляєте до клітини. Дивіться stackoverflow.com/a/17757851/1371070 та stackoverflow.com/a/17758021/1371070
Цезар

70

Я роблю щось дуже схоже, це загальне, не потрібно обчислювати щось конкретне для вашого коду. Просто перевірте зауваження щодо коду:

У 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

У 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

Версія 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()
        }
    }
}

використання сповіщень та отримання висоти клавіатури при включенні орієнтації пристрою було надзвичайним, спасибі за це! частина прокрутки для мене чомусь не працює, тому мені довелося скористатися цим:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
Taber

7
Це найкраща відповідь тут, я думаю. Дуже чисто. Лише дві речі: 1) your viewDidLoad не викликає [super viewDidLoad] і 2) мені довелося мати деякі математичні панелі вкладки на frame.size.height рядках. Інакше ідеально! Дякую.
toxaq

3
Ось опис модифікації toxaq: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Потім віднімайте tabBarHeight від висоти клавіатури, де б ви не використовували висоту клавіатури.
Стів N

1
Якщо користувач натискає текстове поле, його робота ідеально. але якщо користувач натискає інше текстове поле, не натискаючи клавіші повернення, його зменшення розміру перегляду таблиці.
Бхавін Рамані

1
@BhavinRamani погодився. Я додав просте буле властивість, щоб пам'ятати, чи клавіатура вже відображається чи ні, і пропустити повторне виконання коду, коли це не потрібно.
Брудний Генрі

46

Найпростіше рішення для Swift 3 на основі рішення 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)
    })
}

Невелика деталь ... Використання Notificationзамість цього NSNotificationбуде більше "Swift 3-y" :-)
Nicolas Miari

Це допоможе при перепозиціонуванні, якщо є навібар - оточити UIView.animate з цим, якщо дозволити - якщо нехай frame = self.navigationController? .NavigationBar.frame {хай y = frame.size.height + frame.origin.y}
Шон Дев

коли відбувається обертання, виникає збій у завантаженні, і деяка комірка зникає, коли подання таблиці прокручується мануально
jothikenpachi

Гарне рішення спасибі! Примітка - видаляти Observer більше не потрібно.
Нік МакКоннелл

44

У мене була така ж проблема, але я помітив, що вона з’являється лише в одному погляді. Тому я почав шукати відмінності в контролерах.

Я дізнався, що поведінка прокрутки встановлена - (void)viewWillAppear:(BOOL)animated супер екземпляром.

Тому не забудьте виконати так:

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

І це не має значення , якщо ви використовуєте UIViewControllerабо UITableViewController; перевірив це, поставивши UITableViewв якості підвідного перегляду self.view в UIViewController. Це була та сама поведінка. Перегляд не дозволяв прокручувати, якщо дзвінок [super viewWillAppear:animated];відсутній.


1
Це спрацювало чудово. Мені було цікаво, чому люди сказали, що UITableView зробить це для мене, і це вирішило це. Дякую!
olivaresF

5
У мене була і ця проблема, і ця відповідь повинна зробити її до початку!
Ам'єль Мартін

Я втратив стільки часу, намагаючись зрозуміти це самостійно ... спасибі;)
budidino

+1 почав трохи плакати, у мене був цей рядок, але він також потребував [tableViewController viewWillAppear: анімований]; тому що я додаю UITableViewController до UIViewController. більше не сльози :)
colin lamarre

41

я, можливо, пропустив це, оскільки я не прочитав тут всю публікацію, але те, що я придумав, здається оманливо простим. я не проніс це через викрутку, перевіряючи в будь-яких ситуаціях, але, схоже, це повинно працювати добре.

просто відрегулюйте contentInset табличного перегляду по висоті клавіатури, а потім прокрутіть клітинку донизу:

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

і звичайно

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

це занадто просто? я щось пропускаю? поки що це працює для мене чудово, але, як я вже сказав, я не провів це через прокрутку ...


ІМО, це найкраще рішення. Єдине, що я міняю - це ваша жорстко кодована тривалість[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Енді

Це дуже просто. Але я вважаю, що питання не буде оживляти зміни contentInsetта різко змінювати межі прокрутки.
Geek

Ця робота працювала найкраще для мене, проте, кілька питань. 1) Я не знаю, де ви могли б отримати "currentField.indexPath", тому мені довелося зберегти indexPath.row як тег поля та створити indexPath пізніше. 2) Не працює для рядків у верхній частині таблиці, вона прокручує їх із екрана. Довелося додати якийсь код, щоб прокручуватися лише у тому випадку, якщо indexPath поточного поля є більшим, ніж на екрані. 3) довелося використовувати kbSize.Width (замість висоти) на iPad, якщо це краєвид
Travis M.

вибачте, ми настільки звикли до власного коду, що іноді забуваємо, так? currentField - це поточне текстове поле, з яким я працюю, а indexPath - це розширення, яке я додав до класу, що просто додає NSIndexPath, тому я знаю, для чого це клітинка.
mickm

Це шлях, не переміщуючи кадри просто змінюючи властивості таблиці.
Nextorlg

35

Я думаю, що я придумав рішення, яке відповідає поведінці додатків Apple.

По-перше, у вашому viewWillAppear: підпишіться на сповіщення на клавіатурі, щоб ви знали, коли клавіатура відображатиметься та ховається, і система скаже вам розмір клавіатури, але не забудьте скасувати реєстрацію у вашому переглядіWillDisappear :.

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

Реалізуйте способи, подібні до наведеного нижче, щоб ви регулювали розмір таблиціView так, щоб він відповідав видимій області після показу клавіатури. Тут я окремо відстежую стан клавіатури, щоб я сам міг вибрати, коли повернути tableView на повний зріст, оскільки ви отримуєте ці сповіщення про кожну зміну поля. Не забудьте реалізувати клавіатуруWillHide: і виберіть потрібне місце, щоб виправити розмір таблиціView.

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

Тепер ось біт прокрутки, ми спочатку опрацьовуємо кілька розмірів, потім ми бачимо, де ми знаходимося на видимій ділянці, і встановлюємо пряму, яку ми хочемо прокрутити, щоб вона була або в половині виду над або під серединою текстового поля на основі про те, де воно знаходиться в полі зору. У цьому випадку у нас є масив UITextFields і enum, який відстежує їх, тому множення рядкаHeight на номер рядка дає нам фактичне зміщення кадру в цьому зовнішньому вікні.

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

Це, здається, працює досить непогано.


Приємне рішення. Дякуємо за публікацію
Алекс Рейнольдс

2
UIKeyboardBoundsUserInfoKeyзастаріло, як iOS 3.2. Дивіться моє рішення нижче, яке працює у всіх поточних версіях iOS ≥ 3.0. / @ iPhoneDev
Gentz

Це було складніше, ніж треба було. Відповідь @ user91083 була простою і працює.
Річард Брайтвелл

1
У цьому рішенні є невелика проблема. КлавіатураWillShow називається ПІСЛЯ textFieldDidBeginEditing, тому коли ми хочемо прокрутити до якоїсь комірки, кадр tableView ще не змінився, тому він не працюватиме
HiveHicks

35

Якщо ви можете використовувати UITableViewController, ви отримуєте функціонал безкоштовно. Однак іноді це не варіант, зокрема, якщо вам потрібно кілька переглядів, а не лишеUITableView .

Деякі з представлених тут рішень не працюють на iOS ≥4, деякі не працюють на iPad або в альбомному режимі, деякі не працюють на клавіатурах Bluetooth (де ми не хочемо прокрутки), деякі не робота при перемиканні між декількома текстовими полями. Тож якщо ви виберете будь-яке рішення, обов'язково протестуйте ці випадки. Це рішення , яке ми використовуємо використовується в 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];
    }
}   

Ось повний код класу в InAppSettingsKit. Щоб протестувати це, скористайтеся дочірньою панеллю "Повний список", де можна протестувати вищезазначені сценарії.


Я не знаю, чи корисно використовувати рядки замість констант, тому що якщо Apple приходить до ідеї змінити String внутрішньо з якихось причин, ваше рішення вже не працює. Так само ви не отримали попередження, коли воно застаріло. Я думаю

@iPortable: це не ідеально, я знаю. Чи можете ви запропонувати краще рішення, яке працює на всіх версіях ≥3.0?
Ортвін Генц

1
Діє як шарм, але не для UIInterfaceOrientationPor PortraitUpsideDown. Тоді обчислення зменшення висоти повинно здійснюватися також догори дном: CGFloat reduHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Клаас

Це дуже помітні візуальні глюки на моєму iPad та симуляторі (4.3). Занадто помітний у використанні. :(
Боб Сприн

Мені подобається, що це рішення буде враховувати панель інструментів у нижній частині екрана.
pdemarest

24

Найпростіше рішення для 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
    }

Працює ідеально, мінімальні розрахунки потрібні. Я додав код, який відновлює вставки таблиці назад, щоб завершити цю відповідь.
Віталій

Найкраще рішення спасибі Я опублікувати версію Swift 3 тут: stackoverflow.com/a/41040630/1064438
squall2022

Супер ідеальне рішення, яке я коли-небудь бачив, я намагався інших, але має деякі проблеми. Ви працюєте ідеально на ios 10.2.
Ванду Лін

8

Сподіваюся, ви, хлопці, вже отримали рішення, прочитавши все це. Але я знайшов своє рішення наступним чином. Я очікую, що у вас вже є клітина UITextField. Тому під час підготовки просто зберігайте індекс рядків у тезі текстового поля.

cell.textField.tag = IndexPath.row;

Створіть activeTextFieldпримірник UITextFieldіз глобальним діапазоном, як показано нижче:

@interface EditViewController (){

    UITextField *activeTextField;

}

Отже, тепер ви просто скопіюйте вставте мій код наприкінці. А також не забудьте додатиUITextFieldDelegate

#pragma mark - TextField Delegation

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

    activeTextField = textField;

    return YES;
}

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

    activeTextField = nil;

}

Реєструє клавіатуру 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];



}

Ручки клавіатури Notifications:

Викликається при UIKeyboardDidShowNotificationнадсиланні.

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

}

Викликається при UIKeyboardWillHideNotificationнадсиланні

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Тепер залишилося одне: Викличте registerForKeyboardNotificationsметод у ViewDidLoadтакий спосіб:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Ви закінчили, сподівайтеся, що ваша textFieldsволя більше не захована клавіатурою.


6

Поєднуючи та заповнюючи пробіли з кількох відповідей (зокрема, Ortwin Gentz, користувач 98013) та іншої публікації, це вийде з поля для SDK 4.3 на iPad в режимі портрет або ландшафт:

@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

Я використовував цей код в iOS 4.x просто чудово, але в iOS5 він виходить з ладу у scrollToOldPosition, тому що _topmostRowBeforeKeyboardWasShown вже звільнений. Не впевнений, яке рішення ще є. Напевно, пам’ятайте індекс замість об’єкта.
Томас Темпельман

5

Якщо ви використовуєте uitableview для розміщення своїх текстових полів ( від Джеффа Ламарше ), ви можете просто прокрутити табличний перегляд, використовуючи такий спосіб делегування.

(Примітка: мої текстові поля зберігаються в масиві з тим самим індексом, що і рядок у перегляді таблиці)

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

Ви не оновлюєте фрейм tableView. Потім знаки прокрутки та поведінка прокрутки помиляються, коли відображається клавіатура. Дивіться моє рішення.
Ортвін Генц

5

Повідомлення клавіатури працюють, але зразок коду Apple для цього передбачає, що вигляд прокрутки є кореневим видом вікна. Зазвичай це не так. Ви повинні компенсувати панелі вкладок тощо, щоб отримати правильне зміщення.

Це простіше, ніж це звучить. Ось код, який я використовую в UITableViewController. У нього є дві змінні екземпляра, прихованаReRe та клавіатура.

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

UIKeyboardCenterEndUserInfoKeyі UIKeyboardBoundsUserInfoKeyзастаріли на iOS 3.2. Дивіться моє рішення нижче, яке працює у всіх поточних версіях iOS ≥ 3.0.
Ортвін Генц

5

Якщо ви використовуєте Three20 , то використовуйте autoresizesForKeyboardвластивість. Просто встановіть у -initWithNibName:bundleспособі контролера перегляду

self.autoresizesForKeyboard = YES

Це турбується про:

  1. Прослуховування сповіщень на клавіатурі та налаштування кадру подання таблиці
  2. Прокрутка до першого відповіді

Зроблено і зроблено.


що тут Three20? Чи можете ви це вказати?
Mubin Mall

5

Мій підхід:

Я перший підклас UITextField і додаю властивість indexPath. У комірціДля методу я передаю властивість indexPath.

Потім я додаю наступний код:

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

до textFieldShould / WillBegin ... тощо.

Коли клавіатура зникає, вам потрібно повернути її назад:

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

4

Більш поточне рішення. Він прослизає до методів делегування UITextField, тому він не потребує псування без сповіщень UIKeyboard.

Примітки щодо реалізації:

kSettingsRowHeight - висота UITableViewCell.

offsetTarget і offsetThreshold відшаровується від kSettingsRowHeight. Якщо ви використовуєте іншу висоту рядка, встановіть ці значення на властивість y. [alt: обчислити зміщення рядків по-іншому.]

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

}


4

UITextField's delegateМетод використання :

Швидкий

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
}

Ціль-С

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

Привіт, у мене виникають проблеми змусити це працювати над Свіфтом. Мої поля UITextFi підключені до UITableViewCell. Якщо я реалізую цей код всередині свого UIViewController, я не маю доступу до полів UITextFields. Будь-які ідеї?
Ветука

4

Швидке 4.2 повне рішення

Я створив GIST з набором протоколів, який спрощує роботу з додаванням додаткового простору, коли клавіатура відображається, приховується або змінюється.

Особливості :

  • Правильно працює зі змінами кадру клавіатури (наприклад, зміна висоти клавіатури, як емоції → звичайна клавіатура).
  • Підтримка TabBar & ToolBar для прикладу UITableView (на інших прикладах ви отримуєте неправильні вставки).
  • Динамічна тривалість анімації (не жорстко закодована).
  • Підхід, орієнтований на протокол, який може бути легко модифікований для ваших цілей.

Використання

Основний приклад використання в контролері подання, який містить деякий вигляд прокрутки (звичайно також підтримується подання таблиці).

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

Ядро: спостерігач за зміною кадру

Протокол KeyboardChangeFrameObserverбуде запускати подію щоразу, коли змінюється рамка клавіатури (включаючи показ, приховування, зміну кадру).

  1. Зателефонуйте addKeyboardFrameChangesObserver()за viewWillAppear()подібним методом.
  2. Зателефонуйте removeKeyboardFrameChangesObserver()за viewWillDisappear()подібним методом.

Реалізація: прокрутка подання

ModifableInsetsOnKeyboardFrameChangesПротокол додає UIScrollViewпідтримку до основного протоколу. Це змінює вставки виду прокрутки при зміні рамки клавіатури.

Для вашого класу потрібно встановити подання прокрутки, при зміні кадру клавіатури чисті вставки будуть збільшені / зменшені.

var scrollViewToModify: UIScrollView { get }

3

Оскільки у таблиці є текстові поля, найкращий спосіб насправді - змінити розмір таблиці - потрібно встановити tableView.frame таким чином, щоб він був меншим за висотою за розміром клавіатури (я думаю, що становить близько 165 пікселів), а потім знову розгорнути її, коли клавіатура відхиляється.

Ви також можете також відключити взаємодію користувачів для tableView в той час, якщо ви не хочете, щоб користувач прокручував.


Я друге це і реєструюсь для UIKeyboardWillShowNotification, щоб динамічно знаходити розмір клавіатури.
бензадо

Число, повернене об’єктом сповіщення, не працює. Або, принаймні, це не було у 2.2, повернене число було неправильним, і мені довелося жорстко
зафіксувати

2

Це прекрасно працює, і на 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];
        }

Чому ви обмірковуєте і використовуєте спеціальні кейси для кожного текстового поля? Ідентифікуйте кожне текстове поле з NSIndexPath клітинки та змініть це неприємне значення, якщо заява на 2 рядки коду. Ви дійсно хочете зателефонувати CellForRowAtIndexPath, а потім отримати текстове поле з комірки.
Алекс Заватоне

Власне, враховуючи, наскільки неймовірно розгорнута ця ситуація на iOS, я думаю, що це нормально, щоб написати "повністю розкручений, смішно буквальний" код для цієї ситуації.
Fattie

Враховуючи цю відповідь, було дано понад 6 років тому.
WrightsCS

2

Я спробував майже той самий підхід і придумав простіший і менший код для того ж. Я створив IBOutlet iTextView і пов’язаний з UITextView в ІБ.

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

    }

2

Отож, після годинних виснажливих робіт, намагаючись використовувати ці поточні рішення (і зовсім не вдається), я нарешті змусив роботу добре працювати, і оновив їх, щоб використовувати нові анімаційні блоки. Моя відповідь повністю ґрунтується на відповіді Ортвіна вище .

Тому з будь-якої причини код вище для мене просто не працює. Моя установка здавалася досить схожою на інші, але, можливо, тому, що я був на iPad або 4.3 ... поняття не маю. Це робило якусь дурну математику і знімав мою таблицю з екрану.

Дивіться кінцевий результат мого рішення: http://screencast.com/t/hjBCuRrPC (Будь ласка, ігноруйте фотографію. :-P)

Тож я пішов із суттю того, що робив Ортвін, але змінив, як він робив певну математику, щоб додати origin.y & size.height мого подання таблиці з висотою клавіатури. Коли я віднімаю висоту вікна від цього результату, воно вказує мені, скільки перехрестя у мене відбувається. Якщо його більше 0 (він також має деяке перекриття), я виконую анімацію висоти кадру.

Крім того, були деякі проблеми з перемальовуванням, які були вирішені 1) Чекаючи прокрутки до комірки, поки не буде виконана анімація, і 2) використання параметра UIViewAnimationOptionBeginFromCurrentState при захованні клавіатури.

Пару речей, які слід зазначити.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame - це змінні екземпляри, оголошені у заголовку.
  • self.guestEntryTableView - це мій tableView (я перебуваю у зовнішньому файлі)
  • IASKCGRectSwap - метод Ортвіна для гортання координат кадру
  • Я оновлюю висоту tableView лише в тому випадку, якщо буде показано щонайменше 50 пікселів
  • Оскільки я не перебуваю у UIViewController, я не маю self.view, тому я просто повертаю tableView до його початкового кадру

Знову ж, я б не дійшов до цієї відповіді, якби я Ортвін не наголосив на цьому суть. Ось код:

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

Додана моя функція FixOriginRotation, яка виправляє систему координат подання перед оновленням її кадру тощо. Я думаю, що це частина того, чому у мене виникли проблеми спочатку. Не знали, що система координат вікон iOS повертається з пристроєм!
Боб Сприн

2

Цей солютон працює для мене

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

Ви можете змінити значення 160, щоб воно співпадало з вами

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

2

Дуже цікава тема обговорення, я також зіткнувся з тією ж проблемою, може бути і гіршою

  1. Я використовував власну клітинку, а текстове поле було всередині цього.
  2. Мені довелося використовувати UIViewController, щоб відповідати моїм вимогам, тому не можу скористатися UITableViewController.
  3. Я мав критерії фільтру / сортування у своїй комірці таблиці, тобто ур. Клітини продовжують змінюватись та відстежувати покажчик, і все не допоможе.

Тож прочитайте тут теми та застосуйте мою версію, яка допомогла мені висунути вміст iPad в альбомному режимі. Ось код (це не дурний доказ, і все, але це вирішило мою проблему) Спочатку вам потрібно мати делегата у вашому користувальницькому класі комірок, який при редагуванні починається, надсилає текстове поле на ur viewcontroller і встановлює там activefield = theTextField

// ВПРОВАДЖЕНО ДЛЯ РЕЖИМУ ТЕХНІЧНОГО РЕМОНТУ

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

    }


}

// Викликається, коли надсилається UIKeyboardWillHideNotification

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


2

Я щойно вирішив таку проблему сам після того, як я направив масу рішень, знайдених через Google та Stack Overflow.

По-перше, будь ласка, переконайтеся, що ви створили IBOutlet свого UIScrollView, а потім уважно подивіться на Apple Doc: Управління клавіатурою . Нарешті, якщо ви можете прокрутити фон, але клавіатура все ще покриває текстові поля, ознайомтеся з цим фрагментом коду:

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

Основна відмінність цього шматка від Apple полягає в умові if. Я вважаю, що яблучний розрахунок відстані прокрутки та умова того, чи не є точним текстове поле, яке охоплює клавіатура, я змінив, як було зазначено вище.

Дайте мені знати, чи працює він


2

Приклад у Swift, використовуючи точну точку текстового поля з Get indexPath of UITextField в UITableViewCell з 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)
}

1

Ще один простий метод (працює лише з одним розділом)

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

1

Якщо вашим UITableView керує підклас UITableViewController, а не UITableView, а делегатом текстового поля є UITableViewController, він повинен керувати всіма прокруткою - всі ці інші коментарі дуже важко реалізувати на практиці.

Для хорошого прикладу див проект кодового проекту яблука: TaggedLocations.

Ви можете бачити, що він прокручується автоматично, але, здається, не існує коду, який це робить. У цьому проекті також є власні клітинки подання таблиці, тому якщо ви будуєте свою програму з нею як керівництво, ви повинні отримати бажаний результат.


1

Ось як я зробив цю роботу, яка є сумішшю відповідей Сем Хо та Марселя У, а також деякі мої власні виправлення помилок, внесені до мого шаленого коду. Я використовував UITableViewController. Тепер таблиця правильно змінює розмір, коли відображається клавіатура.

1) У viewDidLoadдодав:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Я забув називати superеквіваленти в viewWillAppearі awakeFromNib. Я додав їх ще в.


1

UITableViewControllerчи справді відбувається Прокрутка автоматично. Різниця порівняно з використанням aUIViewController полягає тому, що вам доведеться програмно створювати Navbar-Buttonitems, використовуючи NavigationController, при використанні TableViewController.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.