Создание прокрутки 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) animated, не забудьте вызвать [super viewWillAppear: animated]; :)
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];
}

Вот и все. Никаких расчетов вообще.

user91083
источник
2
И почему бы нет?! Просто замените UITableViewScrollPositionTop на UITableViewScrollPositionMiddle. Конечно, вам просто нужно изменить масштаб UITableView, чтобы настроить видимую область.
Михай Дамиан
3
Кажется, не работает, если UITableViewController позаботился об изменении размера табличного представления при отображении клавиатуры: контроллер уменьшает видимый размер с помощью a contentInset, который, очевидно, не учитывается при запросе visibleRowsили indexPathsForVisibleRows.
Джулиан Д.
16
Не работает для последних нескольких строк табличного представления. Клавиатура будет по-прежнему скрывать все строки, которые нельзя прокручивать над клавиатурой.
Алекс Заватоне
3
Чтобы заставить режим автоматической прокрутки работать с последними несколькими строками таблицы, определите, когда эти строки начинают редактировать, и добавьте нижний колонтитул в конец табличного представления с пустым представлением определенной высоты. Это позволит табличному представлению прокручивать ячейки в правильное место.
Sammio2
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()
        }
    }
}
ZeLegolas
источник
использование уведомлений и получение высоты клавиатуры при включении ориентации устройства было потрясающим, спасибо за это! часть прокрутки у меня почему-то не сработала, поэтому мне пришлось использовать это:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
Табер
7
Это лучший ответ здесь, я думаю. Очень чистый. Только две вещи: 1) ваш 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)
    })
}
squall2022
источник
Небольшая деталь ... Использовать Notificationвместо NSNotificationбыло бы более "Свифт 3-й" :-)
Николас Миари
Это поможет с перемещением, если есть панель навигации - окружите UIView.animate с этим, если let - если let frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Шон Дев
когда происходит вращение, происходит сбой в загрузке, и некоторая ячейка исчезает, когда просмотр
таблицы
Хорошее решение, спасибо! Примечание - больше не нужно выполнять removeObserver.
Ник Макконнелл
44

У меня была такая же проблема, но я заметил, что она появляется только в одном представлении. Поэтому я начал искать различия в контроллерах.

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

Так что не забудьте реализовать так:

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

И не важно, используете ли вы UIViewControllerили UITableViewController; проверил это, поместив UITableViewв качестве подпредставления self.view в UIViewController. Это было то же самое поведение. Представление не позволяло прокрутить, если звонок [super viewWillAppear:animated];отсутствовал.

phse
источник
1
Это сработало превосходно. Мне было интересно, почему люди сказали, что UITableView сделает это для меня, и это решило это. Спасибо!
olivaresF
5
У меня тоже была эта проблема, этот ответ должен быть верным!
Амиэль Мартин
Я потерял так много времени, пытаясь понять это самостоятельно ... спасибо;)
budidino
+1 начинал немного плакать, у меня была эта строка, но мне также нужно было [tableViewController viewWillAppear: animated]; потому что я добавляю UITableViewController к UIViewController. слез больше нет :)
Колин Ламарр
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;
    }];
}

это слишком просто? я что-то упускаю? до сих пор это работает для меня хорошо, но, как я уже сказал, я не положил это через отжимной ...

mickm
источник
ИМО, это лучшее решение. Единственное, что я бы изменил, это твое жестко запрограммированное время на[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Энди
Это очень просто. Но одна проблема, которую я нахожу, заключается в том, что она не оживит изменение contentInsetи резко изменит границы прокрутки.
Компьютерщик
Этот работал лучше всего для меня, однако, несколько вопросов. 1) Я не знаю, где вы можете получить «currentField.indexPath», поэтому мне пришлось сохранить indexPath.row как тег поля и создать indexPath позже. 2) Не работает для строк в верхней части таблицы, он прокручивает их за пределы экрана. Пришлось добавить код для прокрутки, только если indexPath currentField больше, чем умещается на экране. 3) должен был использовать kbSize.Width (вместо высоты) на iPad, если это пейзаж
Трэвис М.
извините, мы настолько привыкли к нашему собственному коду, что иногда забываем, а? currentField - это текущее текстовое поле, с которым я работаю, а indexPath - это расширение, которое я добавил в класс, который просто добавляет NSIndexPath, чтобы я знал, в какой ячейке он находится.
mickm
Это путь, а не перемещение кадров вокруг простого изменения свойств таблицы.
Nextorlg
35

Я думаю, что нашел решение, соответствующее поведению приложений Apple.

Во-первых, в вашем viewWillAppear: подпишитесь на уведомления клавиатуры, чтобы вы знали, когда клавиатура будет отображаться и скрываться, и система сообщит вам размер клавиатуры, но не забудьте отменить регистрацию в вашем viewWillDisappear :.

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

Реализуйте методы, аналогичные приведенным ниже, чтобы настроить размер вашего tableView так, чтобы он соответствовал видимой области, как только появится клавиатура. Здесь я отслеживаю состояние клавиатуры отдельно, поэтому я могу сам выбирать, когда установить tableView обратно на полную высоту, поскольку вы получаете эти уведомления при каждом изменении поля. Не забудьте реализовать keyboardWillHide: и выбрать подходящее место, чтобы зафиксировать размер таблицы.

-(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 и перечисление, которое отслеживает их, поэтому умножение rowHeight на номер строки дает нам фактическое смещение кадра в этом внешнем виде.

- (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
Ортвин Генц
Это было сложнее, чем нужно. Ответ @ user91083 был прост и работает.
Ричард Брайтвелл
1
В этом решении есть небольшая проблема. keyboardWillShow называется AFTER 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 придет к мысли изменить внутреннюю строку по некоторым причинам, ваше решение больше не работает. Точно так же вы не получили предупреждение, когда оно устарело.
@iPortable: это не идеально, я знаю. Можете ли вы предложить лучшее решение, которое работает на всех версиях ≥3.0?
Ортвин Генц
1
Работает как шарм, но не для UIInterfaceOrientationPortraitUpsideDown. Тогда вычисление уменьшения высоты также должно быть основано вверх ногами: CGFloat reduHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Клаас
Это имеет очень заметные визуальные глюки на моем iPad и симуляторе (4.3). Слишком заметно для использования. :(
Боб Сприн
Мне нравится, что это решение будет учитывать панель инструментов в нижней части экрана.
Пдемарест
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
    }
Bartłomiej Semańczyk
источник
Работает отлично, минимум расчетов необходим. Я добавил код, который восстанавливает вставки таблиц обратно, чтобы завершить этот ответ.
Виталий
Спасибо за лучшее решение. Я разместил версию 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клавиатура больше не будет скрыта.

Hussain KMR Behestee
источник
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
Colin
источник
Я использовал этот код в 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. У него есть две переменные экземпляра: hiddenRect и 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;
}
Донован Восс
источник
UIKeyboardCenterEndUserInfoKeyи UIKeyboardBoundsUserInfoKeyустарели с iOS 3.2. Посмотрите мое решение ниже, которое работает во всех текущих версиях iOS ≥ 3.0.
Ортвин Генц
5

Если вы используете Three20, то используйте autoresizesForKeyboardсвойство. Просто установите в -initWithNibName:bundleметоде контроллера вашего представления

self.autoresizesForKeyboard = YES

Это заботится о:

  1. Прослушивание уведомлений клавиатуры и настройка рамки табличного представления
  2. Прокрутка до первого респондента

Сделано и сделано.

Думай сверху вниз
источник
что такое Three20 здесь? Можете ли вы указать это?
Mubin Mall
5

Мой подход:

Сначала я создаю подкласс UITextField и добавляю свойство indexPath. В методе cellFor ... я передаю свойство 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);}];
Sven
источник
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
}

Objective-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;
}
оборота Пареш Навадия
источник
Привет, у меня проблема, заставь это работать на Swift. Мои UITextFields подключены к UITableViewCell. Если я реализую этот код внутри моего UIViewController, у меня нет доступа к UITextFields. Любые идеи?
Ветука
4

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

Core: наблюдатель смены кадров

Протокол KeyboardChangeFrameObserverбудет запускать событие каждый раз при смене кадра клавиатуры (включая отображение, скрытие, смену кадра).

  1. Позвоните addKeyboardFrameChangesObserver()в viewWillAppear()или подобный метод.
  2. Позвоните removeKeyboardFrameChangesObserver()в viewWillDisappear()или подобный метод.

Реализация: прокрутка

ModifableInsetsOnKeyboardFrameChangesПротокол добавляет UIScrollViewподдержку основного протокола. Изменяет вставки вида прокрутки при смене рамки клавиатуры.

Вашему классу нужно установить вид прокрутки, вставки будут увеличиваться / уменьшаться при смене кадров клавиатуры.

var scrollViewToModify: UIScrollView { get }
Василий
источник
3

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

При желании вы также можете отключить взаимодействие с пользователем для tableView в это время, если вы не хотите, чтобы пользователь прокручивал.

Кендалл Хельмштеттер Гелнер
источник
Я второй это, и зарегистрируйтесь для UIKeyboardWillShowNotification, чтобы найти размер клавиатуры динамически.
Бензадо
Число, возвращаемое объектом уведомления, не работает. Или, по крайней мере, в 2.2 это не так, возвращаемое число было неправильным, и мне пришлось жестко закодировать значение 165, чтобы правильно отрегулировать высоту (оно было отключено на пять-десять пикселей)
Кендалл Хельмштеттер Гелнер,
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];
        }
WrightsCS
источник
Почему вы вводите и используете специальные случаи для каждого текстового поля? Идентифицируйте каждое текстовое поле из NSIndexPath ячейки и измените этот неприятный оператор if на 2 строки кода. Вы действительно хотите вызвать cellForRowAtIndexPath, а затем получить textField из ячейки.
Алекс Заватоне
На самом деле, учитывая, насколько невероятно сложная ситуация на iOS, я думаю, что можно написать «полностью раскрученный, смехотворно буквальный» код для этой ситуации.
Толстяк
Учитывая этот ответ был дан более 6 лет назад.
WrightsCS
2

Я попробовал почти такой же подход и придумал более простой и меньший код для того же самого. Я создал IBOutlet iTextView и связал его с UITextView в 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];

    }
thesummersign
источник
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];
}
hiennt
источник
2

Очень интересная ветка обсуждения, я также столкнулся с той же проблемой, может быть хуже, потому что

  1. Я использовал пользовательскую ячейку, и текстовое поле было внутри этого.
  2. Мне пришлось использовать UIViewController для удовлетворения моих требований, поэтому я не могу воспользоваться UITableViewController.
  3. У меня в ячейке таблицы были критерии фильтра / сортировки, т.е. ур-ячейки постоянно меняются и отслеживают путь индекса, и все это не поможет.

Так что читайте темы здесь и внедрите мою версию, которая помогла мне перенести содержимое в iPad в альбомном режиме. Вот код (это не дурак и все, но это исправило мою проблему) Сначала вам нужно иметь делегата в вашем пользовательском классе ячеек, который при редактировании начинает, отправляет текстовое поле в ваш 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

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. Я считаю, что Apple рассчитывает расстояние прокрутки и условие, является ли текстовое поле, покрытое клавиатурой, неточным, поэтому я сделал свою модификацию, как указано выше.

Дайте мне знать, если это работает

dumbfingers
источник
2

Пример в Swift, использующий точную точку текстового поля из Get indexPath 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];
JonasG
источник
1

Если ваш UITableView управляется подклассом UITableViewController, а не UITableView, а делегатом текстового поля является UITableViewController, он должен автоматически управлять всей прокруткой - все эти другие комментарии очень трудно реализовать на практике.

Для хорошего примера см. Пример проекта кода Apple: TaggedLocations.

Вы можете видеть, что он прокручивается автоматически, но, похоже, нет кода, который это делает. В этом проекте также есть настраиваемые ячейки табличного представления, поэтому, если вы создадите свое приложение, руководствуясь им, вы должны получить желаемый результат.

шайба
источник
1

Вот как я сделал эту работу, которая представляет собой смесь ответов Сэма Хо и Марселя В. и некоторых моих собственных исправлений ошибок, внесенных в мой дерьмовый код. Я использовал UITableViewController. Таблица теперь изменяет размеры правильно, когда отображается клавиатура.

1) В viewDidLoadдобавлено:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Я забыл назвать superэквиваленты в viewWillAppearи awakeFromNib. Я добавил их обратно.

Данял айтекин
источник
1

UITableViewControllerделает прокрутку автоматически, действительно. Разница по сравнению с использованием a UIViewControllerзаключается в том, что вы должны создавать элементы Navbar-Buttonitems программно, используя NavigationController, при использовании a TableViewController.

Tom01
источник