Làm thế nào để biết khi nào UITableView đã cuộn xuống dưới cùng trong iPhone


100

Tôi muốn biết khi nào một bảng UITableViewđã cuộn xuống dưới cùng để tải và hiển thị thêm nội dung, giống như một đại biểu hoặc một cái gì đó khác để cho người điều khiển biết khi nào bảng cuộn xuống dưới cùng.

Có ai biết cái này thì giúp mình với, cảm ơn trước!

Câu trả lời:


18

Cách tốt nhất là kiểm tra một điểm ở cuối màn hình và sử dụng phương thức này khi người dùng cuộn ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Kiểm tra một điểm gần cuối màn hình, sau đó sử dụng indexPath, nó trả về kiểm tra xem indexPath đó có phải là hàng cuối cùng hay không, sau đó thêm các hàng vào.


Rất tiếc là Ứng dụng của tôi sẽ cập nhật dữ liệu theo thời gian thực cho các hàng hiện có trong bảng này, vì vậy, cách này có thể nhận được nhiều nội dung hơn khi bảng không cuộn.
Son Nguyen

247

trong tableview ủy nhiệm làm một cái gì đó như thế này

Mục tiêuC:

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

Nhanh:

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

Tốt! Chỉ cần sử dụng: y> = h + reload_distance, điều này thực sự chỉ là mối quan tâm khi reload_distance = 0 (ví dụ: đối với số lần trả lại KHÔNG).
Chris Prince

4
Mã này trong "scrollViewDidScroll" đang được thực thi mọi lúc người dùng di chuyển ngón tay lên / xuống trên màn hình. Tốt hơn là di chuyển nó trong "scrollViewDidEndDecelerating". Bằng cách này, nó sẽ được thực thi một lần khi người dùng ngừng di chuyển ngón tay của mình.
Misha

hmm .. mã này vẫn hoạt động? không nhận được cuối của UITableView cho tôi. Tôi ước gì tôi biết lý do đằng sau tất cả các đoạn mã trên để có thể sửa chữa
FlowUI. SimpleUITesting.com

offset: 237.000000 content.height: 855.000000 bounds.height: 667.000000 inset.top: 64.000000 inset.bottom: 49.000000 pos: 855.000000 of 855.000000 if is false
Abhishek Thapliyal 20/05

62

Neoneyes sửa đổi trả lời một chút.

Câu trả lời này nhắm mục tiêu đến những người trong số các bạn chỉ muốn sự kiện được kích hoạt một lần mỗi lần thả ngón tay .

Thích hợp khi tải thêm nội dung từ một số nhà cung cấp nội dung (dịch vụ web, dữ liệu cốt lõi, v.v.). Lưu ý rằng cách tiếp cận này không tôn trọng thời gian phản hồi từ dịch vụ web của bạn.

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

Điều này không hoạt động. Bất cứ khi nào người dùng cuộn xuống dưới cùng, "tải thêm hàng" sẽ được gọi. Điều này xảy ra ngay cả khi người dùng đã cuộn xuống cuối và đang đợi API trả lại dữ liệu.
Dean

2
Dean, câu hỏi là về việc biết khi nào chế độ xem bảng cuộn xuống dưới cùng. Việc bạn đang tìm nạp dữ liệu từ một API hay không đều không liên quan, phải không?
Nhãn cầu

Nhưng không có thông báo nào hiển thị tải thêm .. để người dùng có thể biết rằng có nhiều kết quả hơn để hiển thị.
iPhone 7

Câu trả lời tuyệt vời trong trường hợp của tôi là nó đang hoạt động nếu: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "tải thêm hàng"); } bởi vì y = 565 và h cũng là 565
Abhishek Thapliyal

1
Tôi đã thử cả câu trả lời của Eyeball và @neoneye. Hãy cho tôi biết hay không 'scrollViewDidScroll' được gọi nhiều lần so với 'scrollViewDidEndDragging'. Vì vậy, thật hiệu quả khi sử dụng 'scrollViewDidEndDragging' vì tôi có lệnh gọi API trên đó.
Vinod Supnekar

39

thêm phương thức này vào 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
Tôi thích giải pháp này ngoại trừ một chi tiết nhỏ. if (distanceFromBottom <= height)
Mark McCorkle

Điều này đã giúp tôi khắc phục sự cố của mình, cảm ơn bạn !! một thanh tab !! nó là một thanh tab ngu ngốc !!
Dmytro

công việc này giống như một sự quyến rũ và nó cũng đơn giản, nhưng tôi đang gặp phải một vấn đề nhỏ như nó đang thực hiện hai hoặc ba lần ở hàng cuối cùng. làm thế nào để nó chỉ thực thi một lần khi đến hàng cuối cùng của tableview?
R. Mohan

Cảm ơn bạn rất nhiều!! Tôi đặt mã này trong scrollViewWillBeginDecelerating và nó chỉ thực thi một lần khi được cuộn đến hàng cuối cùng.
Manali

20

Không có câu trả lời nào ở trên giúp ích cho tôi, vì vậy tôi đã nghĩ ra điều này:

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

Bạn có thể nói rõ "lastSection" đến từ đâu không?
ainesophaur

này là dành cho xem di chuyển và không bảng
iosMentalist

19

Sử dụng – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegatephương pháp)

Đơn giản chỉ cần so sánh indexPathvới các mục trong mảng dữ liệu của bạn (hoặc bất kỳ nguồn dữ liệu nào bạn sử dụng cho chế độ xem bảng của mình) để tìm hiểu xem phần tử cuối cùng có được hiển thị hay không.

Tài liệu: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath :


Sau đó, nếu bạn tải lại dữ liệu của tableView, bạn sẽ bị mắc kẹt trong một vòng lặp vô hạn.
Bán hàng Dielson

14

UITableViewlà một lớp con của UIScrollViewUITableViewDelegatetuân theo UIScrollViewDelegate. Vì vậy, đại biểu mà bạn đính kèm vào dạng xem bảng sẽ nhận được các sự kiện chẳng hạn như scrollViewDidScroll:và bạn có thể gọi các phương thức như contentOffsettrên dạng xem bảng để tìm vị trí cuộn.


Vâng, đó là những gì tôi đang tìm kiếm, bằng cách thực hiện ủy nhiệm và kiểm tra xem vị trí cuộn là UITableViewScrollPositionBottom, tôi sẽ biết khi nào bảng là cuộn xuống dưới, cảm ơn rất nhiều
Sơn Nguyễn

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];

Đẹp và đơn giản


8

trong Swiftbạn có thể làm một cái gì đó như thế này. Điều kiện sau sẽ đúng mỗi khi bạn đến cuối chế độ xem bảng

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

1
vấn đề là khi chế độ xem bảng chỉ có 3 ô, ví dụ println("came to last row")mã này chạy!
Mc.Lover

@ Mc.Lover, đó là vấn đề chính xác đối với tôi mà việc sử dụng CPU sẽ lên tới 92%. câu trả lời của neoneye đã làm việc cho tôi. Tôi cũng đã thêm phiên bản nhanh chóng cho nó nếu bạn cần.
Anuran Barman

7

Dựa trên câu trả lời của @Jay Mayu, mà tôi cảm thấy là một trong những giải pháp tốt hơn:

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

Tôi thường sử dụng điều này để tải thêm dữ liệu, khi ô cuối cùng bắt đầu hiển thị

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

1
cái này sai. Mỗi khi bạn gọi reloadData, một yêu cầu sẽ được thực hiện. Và đôi khi bạn chỉ cần tải lại bảng.
Patrick Bassut

bạn muốn nói, nếu bạn ở ô cuối cùng và sau đó thực hiện tải lại dữ liệu thì điều này sẽ xảy ra?
Shaik Riyaz

5

Đưa ra các câu trả lời xuất sắc bằng mắt thường nhưng nhanh chóng và đổi tên các biến ..

Về cơ bản, chúng tôi biết rằng chúng tôi đã đến cuối chế độ xem bảng nếu yOffsetAtBottomnó vượt quá chiều cao nội dung bảng.

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

Đây là mã phiên bản 3.0 nhanh chóng.

   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

Giải pháp của tôi là thêm các ô trước khi chế độ xem bảng sẽ giảm tốc trên độ lệch ước tính. Nó có thể dự đoán được trên vận tốc.

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

Cập nhật cho Swift 3

Câu trả lời của Neoneye phù hợp nhất với tôi trong Objective C, đây là câu trả lời tương đương với câu trả lời trong 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

Tôi muốn thực hiện một số hành động trên 1 Tableviewcell đầy đủ bất kỳ của mình.

Vì vậy, mã là liên kết:

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

Mong điều này giúp ích cho một số người.


0

Câu trả lời của @ neoneye phù hợp với tôi. Đây là phiên bản Swift 4 của nó

    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
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.