Как узнать, когда 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");
    }
}
8сухас
источник
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];

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

user1641587
источник
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!")
    }
}
footyapps27
источник
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
    }
Ануран Бармен
источник