Как узнать, когда UITableView прокрутил вниз в iPhone


101

Я хотел бы знать, когда UITableViewпрокручивался вниз, чтобы загрузить и показать больше контента, что-то вроде делегата или чего-то еще, чтобы контроллер знал, когда таблица действительно прокручивается вниз.

Кто-нибудь знает это, помогите пожалуйста, заранее спасибо!

Ответы:


19

Лучший способ - проверить точку внизу экрана и использовать этот вызов метода всякий раз, когда пользователь scrolls ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

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


К сожалению, мое приложение будет обновлять данные в реальном времени для существующих строк в этой таблице, поэтому таким образом можно получить больше контента, когда таблица вообще не прокручивается.
— Сон Нгуен

247

в делегате tableview сделайте что-то вроде этого

ObjC:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;
    // NSLog(@"offset: %f", offset.y);   
    // NSLog(@"content.height: %f", size.height);   
    // NSLog(@"bounds.height: %f", bounds.size.height);   
    // NSLog(@"inset.top: %f", inset.top);   
    // NSLog(@"inset.bottom: %f", inset.bottom);   
    // NSLog(@"pos: %f of %f", y, h);

    float reload_distance = 10;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Swift:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let inset = scrollView.contentInset
    let y = offset.y + bounds.size.height - inset.bottom
    let h = size.height
    let reload_distance:CGFloat = 10.0
    if y > (h + reload_distance) {
        print("load more rows")
    }
}

Хорошо! Просто используйте: y> = h + reload_distance, что действительно имеет значение только при reload_distance = 0 (например, для отскоков НЕТ).
— Chris Prince

4
Этот код в "scrollViewDidScroll" выполняется все время, когда пользователь перемещает палец вверх / вниз по экрану. Лучше переместить его в раздел scrollViewDidEndDecelerating. Таким образом, он будет выполнен один раз, когда пользователь перестанет двигать пальцем.
— Миша

хм .. этот код все еще работает? не разбираясь в UITableView для меня. Хотел бы я знать
— причину

смещение: 237.000000 content.height: 855.000000 bounds.height: 667.000000 inset.top: 64.000000 inset.bottom: 49.000000 pos: 855.000000 из 855.000000 если ложно
— Abhishek Thapliyal

62

Немного доработанный neoneyes отвечает.

Этот ответ нацелен на тех из вас, кто хочет, чтобы событие запускалось только один раз при отпускании пальца .

Подходит при загрузке большего количества контента от некоторых поставщиков контента (веб-сервис, основные данные и т. Д.). Обратите внимание, что этот подход не учитывает время ответа от вашего веб-сервиса.

- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
                  willDecelerate:(BOOL)decelerate
{
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;

    float reload_distance = 50;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Это не работает. Всякий раз, когда пользователь прокручивает страницу вниз, вызывается «загрузить больше строк». Это происходит даже тогда, когда пользователь уже прокрутил страницу вниз и ожидает, пока API вернет данные.
— Dean

2
Дин, вопрос был в том, чтобы узнать, когда таблица прокручивается вниз. Независимо от того, получаете ли вы данные из API или нет, не так ли?
— Eyeball

Но нет сообщения, показывающего загрузить больше .., чтобы пользователь мог знать, что есть еще результаты, которые нужно показать.
— iPhone 7,

Хороший ответ, в моем случае он работает, если: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "загрузить больше строк"); } потому что y = 565 и h тоже 565
— Абхишек Таплиял

1
Я попробовал ответить как Eyeball, так и @neoneye. Поверьте мне или нет, «scrollViewDidScroll» вызывается несколько раз по сравнению с «scrollViewDidEndDragging». Так что было эффективно использовать scrollViewDidEndDragging, поскольку у меня есть вызов API.
— Винод Супнекар

39

добавьте этот метод в UITableViewDelegate:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{   
    CGFloat height = scrollView.frame.size.height;

    CGFloat contentYoffset = scrollView.contentOffset.y;

    CGFloat distanceFromBottom = scrollView.contentSize.height - contentYoffset;

    if(distanceFromBottom < height) 
    {
        NSLog(@"end of the table");
    }
}

3
Мне нравится это решение, кроме одной маленькой детали. if (distanceFromBottom <= height)
— Марк МакКоркл,

Это помогло мне решить мою проблему, спасибо !! панель вкладок !! это была дурацкая панель вкладок !!
— Дмитрий

это работает как шарм, и это тоже просто, но я столкнулся с одной небольшой проблемой, например, он выполняется два или три раза в последней строке. как заставить его выполняться только один раз, когда он достигает последней строки tableview?
— Р. Мохан

Огромное спасибо!! Я поместил этот код в scrollViewWillBeginDecelerating, и он выполняется только один раз при прокрутке до последней строки.
— Manali

20

Ни один из приведенных выше ответов мне не помог, поэтому я придумал следующее:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{   
    NSArray *visibleRows = [self.tableView visibleCells];
    UITableViewCell *lastVisibleCell = [visibleRows lastObject];
    NSIndexPath *path = [self.tableView indexPathForCell:lastVisibleCell];
    if(path.section == lastSection && path.row == lastRow)
    {
        // Do something here
    }
}

Не могли бы вы уточнить, откуда взялось "lastSection"?
— ainesophaur

это для просмотра прокрутки, а не для таблицы
— iosMentalist

19

Использовать – tableView:willDisplayCell:forRowAtIndexPath: ( UITableViewDelegateметод)

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

Документы: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath :


Затем, если вы перезагрузите данные tableView, вы застрянете в бесконечном цикле.
— Dielson Sales

14

UITableViewявляется подклассом UIScrollViewи UITableViewDelegateсоответствует UIScrollViewDelegate. Таким образом, делегат, который вы присоединяете к табличному представлению, будет получать такие события, как scrollViewDidScroll:, и вы можете вызывать методы, например, contentOffsetв табличном представлении, чтобы найти положение прокрутки.


Да, это то, что я ищу, реализуя этого делегата и проверяя, является ли позиция прокрутки UITableViewScrollPositionBottom, я буду знать, когда таблица прокручивается вниз, большое спасибо
— Сон Нгуен

8
NSLog(@"%f / %f",tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);

if (tableView.contentOffset.y == tableView.contentSize.height - tableView.frame.size.height)
        [self doSomething];

Красиво и просто


8

в Swiftвы можете сделать что-то вроде этого. Следующее условие будет выполняться каждый раз, когда вы достигнете конца просмотра таблицы.

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}

1
проблема в том, что когда tableview имеет только 3 ячейки, например, println("came to last row")этот код запускается!
— Mc.Lover 04

@ Mc.Lover, это была точная проблема для меня, при которой загрузка процессора составляла 92%. Ответ neoneye сработал для меня. Я также добавил для него быструю версию, если вам нужно.
— Ануран Барман

7

Основываясь на ответе @Jay Mayu, который, по моему мнению, был одним из лучших решений:

Цель-C

// UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

// Need to call the service & update the array
if(indexPath.row + 1 == self.sourceArray.count) {
    DLog(@"Displayed the last row!");
  }
}

Swift 2.x

// UITableViewDelegate
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if (indexPath.row + 1) == sourceArray.count {
        print("Displayed the last row!")
    }
}

5

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

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row ==  myDataArray.count-1) {
        NSLog(@"load more");
    }
}

1
это не верно. Каждый раз, когда вы вызываете reloadData, будет выполняться запрос. А иногда нужно просто перезагрузить стол.
— Патрик Бассут

Вы хотели сказать, что если вы находитесь в последней ячейке, а затем выполните перезагрузку данных, то это произойдет?
— Шайк Рияз

5

Принимая neoneye отличных ответов , но в стриже, и переименование переменных ..

В основном мы знаем, что достигли нижней части представления таблицы, если yOffsetAtBottomона превышает высоту содержимого таблицы.

func isTableViewScrolledToBottom() -> Bool {
    let tableHeight = tableView.bounds.size.height
    let contentHeight = tableView.contentSize.height
    let insetHeight = tableView.contentInset.bottom

    let yOffset = tableView.contentOffset.y
    let yOffsetAtBottom = yOffset + tableHeight - insetHeight

    return yOffsetAtBottom > contentHeight
}

5

Вот код версии Swift 3.0.

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let offset = scrollView.contentOffset
        let bounds = scrollView.bounds
        let size = scrollView.contentSize
        let inset = scrollView.contentInset
        let y: Float = Float(offset.y) + Float(bounds.size.height) + Float(inset.bottom)
        let height: Float = Float(size.height)
        let distance: Float = 10

        if y > height + distance {
            // load more data
        }
    }

3

Мое решение - добавить ячейки до того, как tableview замедлится при расчетном смещении. Это предсказуемо по скорости.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)offset {

    NSLog(@"offset: %f", offset->y+scrollView.frame.size.height);
    NSLog(@"Scroll view content size: %f", scrollView.contentSize.height);

    if (offset->y+scrollView.frame.size.height > scrollView.contentSize.height - 300) {
        NSLog(@"Load new rows when reaches 300px before end of content");
        [[DataManager shared] fetchRecordsNextPage];
    }
}

3

Обновление для Swift 3

Ответ Neoneye лучше всего сработал для меня в Objective C, это эквивалент ответа в Swift 3:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let offset: CGPoint = scrollView.contentOffset
    let bounds: CGRect = scrollView.bounds
    let size: CGSize = scrollView.contentSize
    let inset: UIEdgeInsets = scrollView.contentInset
    let y: CGFloat = offset.y + bounds.size.height - inset.bottom
    let h: CGFloat = size.height
//        print("offset: %f", offset.y)
//        print("content.height: %f", size.height)
//        print("bounds.height: %f", bounds.size.height)
//        print("inset.top: %f", inset.top)
//        print("inset.bottom: %f", inset.bottom)
//        print("position: %f of %f", y, h)

    let reloadDistance: CGFloat = 10

    if (y > h + reloadDistance) {
            print("load more rows")
    }
}

2

Я хочу выполнить какое-то действие над любой 1 полной ячейкой Tableview.

Итак, код связан с:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSArray* cells = self.tableView.visibleCells;
    NSIndexPath *indexPath = nil ;

    for (int aIntCount = 0; aIntCount < [cells count]; aIntCount++)
    {

        UITableViewCell *cell = [cells objectAtIndex:aIntCount];
CGRect cellRect = [self.tableView convertRect:cell.frame toView:self.tableView.superview];

        if (CGRectContainsRect(self.tableView.frame, cellRect))
        {
            indexPath = [self.tableView indexPathForCell:cell];

    //  remain logic
        }
     }
}

Пусть это кому-нибудь поможет.


0

Ответ @neoneye сработал для меня. Вот его версия для Swift 4

    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let insets = scrollView.contentInset
    let y = offset.y + bounds.size.height - insets.bottom
    let h = size.height
    let reloadDistance = CGFloat(10)
    if y > h + reloadDistance {
        //load more rows
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.