Создание прокрутки UITableView при выделении текстового поля - PullRequest
245 голосов
/ 27 февраля 2009

После долгих проб и ошибок я сдаюсь и задаю вопрос. Я видел много людей с похожими проблемами, но не мог правильно ответить на все ответы.

У меня есть UITableView, который состоит из пользовательских ячеек. Ячейки состоят из 5 текстовых полей, расположенных рядом друг с другом (вроде сетки).

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

Я видел много ответов, говорящих об изменении размеров вида и т. Д., Но ни один из них до сих пор не работал должным образом.

Может ли кто-нибудь уточнить «правильный» способ сделать это на конкретном примере кода?

Ответы [ 45 ]

2 голосов
/ 21 мая 2015

Пример в Swift, использующий точную точку текстового поля из Получить 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)
}
2 голосов
/ 18 августа 2012

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

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

2 голосов
/ 23 октября 2010

Это отлично работает, и на 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];
        }
2 голосов
/ 13 мая 2011

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

    }
2 голосов
/ 18 июля 2011

Итак, после нескольких часов изнурительной работы, пытаясь использовать эти текущие решения (и полностью терпя неудачу), я наконец-то все заработало, и обновил их, чтобы использовать новые блоки анимации. Мой ответ полностью основан на ответе Ортвина выше .

Так что по какой-то причине приведенный выше код просто не работал для меня. Мои настройки казались довольно похожими на других, но, может быть, потому что я был на iPad или 4.3 ... не знаю. Он занимался какой-то дурацкой математикой и снимал мой стол с экрана.

См. Конечный результат моего решения: http://screencast.com/t/hjBCuRrPC (Пожалуйста, игнорируйте фотографию. :-P)

Итак, я понял суть того, что делал Ортвин, но изменил то, как он делал некоторые математические операции, чтобы сложить origin.y & size.height моего вида таблицы с высотой клавиатуры. Когда я вычитаю высоту окна из этого результата, он говорит мне, сколько пересечений у меня происходит. Если его значение больше 0 (иначе есть некоторое перекрытие), я выполняю анимацию высоты кадра.

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

Пара вещей, на которые стоит обратить внимание.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame являются переменными экземпляра, объявленными в заголовке.
  • self.guestEntryTableView - это мой tableView (я во внешнем файле)
  • IASKCGRectSwap - это метод Ортвина для переключения координат кадра
  • Я обновляю высоту tableView, только если она будет отображаться как минимум в 50px
  • Поскольку я не в 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;
}
2 голосов
/ 17 июля 2012

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

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

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

Этот солутон работает для меня, ПОЖАЛУЙСТА, обратите внимание на строку

[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];
}
1 голос
/ 13 февраля 2014

Я использую их, и они работают как шарм:

BSKeyboardControls - BSKeyboardControls github

TPKeyboardAvoiding - TPKeyboardAvoiding github

1 голос
/ 13 февраля 2014

Я часто использую это в своих проектах. Это решение работает с видами прокрутки, таблицами или коллекциями, и его легко настроить. Он также автоматически подключает кнопки «Далее» на клавиатуре для переключения между текстовыми полями.

Проверьте это здесь

1 голос
/ 28 января 2014

Я брошу свое решение (или QuickDialog's, что есть) в шапку. В основном ждите, чтобы оживить прокрутку. Было бы неплохо получить клавиатуру с анимацией JIT вместо магического числа.

-(void)textFieldDidBeginEditing:(UITextField *)textField
{
    if (textField == self.emailTextField) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 50 * USEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
        });
    }
}
...