Як дізнатися, коли UITableView прокрутився донизу в iPhone


100

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

Хтось це знає, будь ласка, допоможіть мені, заздалегідь дякую!

Відповіді:


18

Найкращий спосіб - перевірити точку в нижній частині екрана і використовувати цей метод виклику, коли користувач прокручує ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

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


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

247

у представника таблиці перегляду роблять щось подібне

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

Швидкий:

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 (наприклад, для відмов НІ).
Кріс Принс

4
Цей код у "scrollViewDidScroll" виконується весь час, коли користувач рухає пальцем вгору / вниз по екрану. Краще перемістити його під "scrollViewDidEndDecelerating". Таким чином він буде виконаний один раз, коли користувач перестане рухати пальцем.
Міша

хм .. цей код все ще працює? не отримавши для мене дно UITableView. Я хотів би, щоб я знав міркування за всім кодом вище, тому, можливо, я можу виправити
FlowUI. SimpleUITesting.com

зсув: 237,000000 зміст.висота: 855,000000 меж.висота: 667,000000 вставка.поверхня: 64,000000 вставка.низу: 49,000000 поз .: 855,000000 від 855,000000, якщо стає неправдивим
Абхішек Тапліял

62

Змінені неонеї трохи відповідають.

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

Підходить під час завантаження більше вмісту від деякого постачальника контенту (веб-сервіс, основні дані тощо). Зауважте, що такий підхід не враховує час відповіді вашого веб-сервісу.

- (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.
Декан

2
Дін, питання полягало у тому, щоб знати, коли подання таблиці прокручується донизу. Чи ви отримуєте дані з API чи ні, це не має значення, чи не так?
Очне яблуко

Але повідомлення не показує більше завантаження .., щоб користувач міг знати, що є більше результатів.
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
Мені подобається це рішення, крім однієї невеликої деталі. якщо (відстаньFromBottom <= висота)
Марк МакКоркл

Це допомогло мені вирішити мою проблему, дякую !! панель вкладок !! це було дурне вкладка !!
Дмитро

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

Дуже дякую!! Я вкладаю цей код у scrollViewWillBeginDecelerating, і він виконується лише один раз, коли прокручується до останнього рядка.
Маналі

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"?
анезофавр

це для перегляду прокрутки, а не для таблиці
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 ', ви застрягнете в нескінченному циклі.
Продаж Дільсона

14

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


Так, це те, що я шукаю, застосувавши цього делегата і перевіривши, чи позиція прокрутки є UITableViewScrollPositionBottom, я буду знати, коли таблиця прокручується донизу, дуже дякую
Son Nguyen,

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
Проблема полягає в тому, що у табличному перегляді є лише 3 комірки, наприклад, println("came to last row")цей код працює!
Mc.Lover

@ Mc.Lover, це була саме для мене проблема, для якої використання процесора збиралося до 92%. відповідь neoneye працювала на мене. Я також додав швидку версію для неї, якщо вам потрібно.
Ануран Барман

7

На основі відповіді @Jay Mayu, яку я вважав, було одним з кращих рішень:

Ціль-С

// 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!");
  }
}

Швидкий 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

Отримання неоні- чудових відповідей, але швидко та перейменування змінних.

В основному ми знаємо, що ми досягли нижнього перегляду таблиці, якщо значення 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

Ось швидкий код версії 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

Моє рішення полягає в тому, щоб додати комірки до того, як перегляд таблиці зменшиться за прогнозованим зміщенням. Це прогнозовано на швидкості.

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

Відповідь Неоні найкраще працювала для мене в «Цілі 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 повному Tableviewcell.

Отже, код посилається на:

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