ios UITableView 无限滚动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10404116/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:12:33  来源:igfitidea点击:

UITableView infinite scrolling

iphoneobjective-ciosipad

提问by adit

How do I do an infinite scrolling in a UITableView? I know how to do it using a UIScrollView, in which apple has demonstrated in one of the WWDC's video. I tried doing the following in tableView:cellForRowAtIndexPath::

如何在 a 中无限滚动UITableView?我知道如何使用UIScrollView,其中苹果在 WWDC 的视频之一中进行了演示。我尝试在以下内容中执行以下操作tableView:cellForRowAtIndexPath:

if (indexPath.row == [self.newsFeedData_ count] - 1)
{
    [self.newsFeedData_ addObjectsFromArray:self.newsFeedData_];
    [self.tableView reloadData];
}

but this fails. Any other idea?

但这失败了。还有其他想法吗?

回答by CodaFi

If you need to know when you hit the bottom of the UITableView, become it's delegate (because it is a subclass of UIScrollView), and use the -scrollViewDidScroll: delegate method to compare the table's content height and it's actual scroll position.

如果您需要知道何时到达 UITableView 底部,则成为它的委托(因为它是 UIScrollView 的子类),并使用 -scrollViewDidScroll: 委托方法来比较表格的内容高度和实际滚动位置。

EDIT (something like this):

编辑(类似这样):

- (void)scrollViewDidScroll:(UIScrollView *)scrollView_ 
{   
    CGFloat actualPosition = scrollView_.contentOffset.y;
    CGFloat contentHeight = scrollView_.contentSize.height - (someArbitraryNumber);
    if (actualPosition >= contentHeight) {
        [self.newsFeedData_ addObjectsFromArray:self.newsFeedData_];
        [self.tableView reloadData];
     }
}

回答by Richard H Fung

You can support infinite scroll with pull to refresh at the top and/or scroll continuously at the bottom with a spinner wheel using:

您可以使用以下方法支持无限滚动以在顶部刷新和/或使用微调轮在底部连续滚动:

https://github.com/samvermette/SVPullToRefresh

https://github.com/samvermette/SVPullToRefresh

SVPullToRefreshhandles the logic when UITableViewreaches the bottom. A spinner is shown automatically and a callback block is fired. You add in your business logic to the callback block.

SVPullToRefreshUITableView到达底部时处理逻辑。自动显示微调器并触发回调块。您将业务逻辑添加到回调块中。

Here's an example:

下面是一个例子:

#import "UIScrollView+SVInfiniteScrolling.h"

// ...

[tableView addInfiniteScrollingWithActionHandler:^{
    // append data to data source, insert new cells at the end of table view
    // call [tableView.infiniteScrollingView stopAnimating] when done
}];

This project can be added to your project using CocoaPodsor directly compiled into your project.

这个项目可以使用CocoaPods添加到你的项目中,也可以直接编译到你的项目中。

回答by Oliver Pearmain

Here's a very quick and complete demo of an infinite scrolling UITableView I put together...

这是我放在一起的无限滚动 UITableView 的一个非常快速和完整的演示......

@interface InfiniteScrollViewController ()

@property (nonatomic) NSMutableArray *tableViewData;
@property (nonatomic) BOOL loadingMoreTableViewData;

@end

@implementation InfiniteScrollViewController

- (void)viewDidLoad {
    self.tableViewData = [[NSMutableArray alloc] init];
    [self addSomeMoreEntriesToTableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.tableViewData.count + 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    if (indexPath.row < self.tableViewData.count) {
        cell.textLabel.text = [self.tableViewData objectAtIndex:indexPath.row];
    } else {
        cell.textLabel.text = @"Loading more data...";

        // User has scrolled to the bottom of the list of available data so simulate loading some more if we aren't already
        if (!self.loadingMoreTableViewData) {
            self.loadingMoreTableViewData = YES;
            [self performSelector:@selector(addSomeMoreEntriesToTableView) withObject:nil afterDelay:5.0f];
        }
    }

    return cell;
}

- (void)addSomeMoreEntriesToTableView {
    int loopTill = self.tableViewData.count + 20;
    while (self.tableViewData.count < loopTill) {
        [self.tableViewData addObject:[NSString stringWithFormat:@"%i", self.tableViewData.count]];
    };
    self.loadingMoreTableViewData = NO;
    [self.tableView reloadData];
}

@end

回答by Anup Kattel

'UITableView' is same as 'UIScrollView' in 'scrollViewDidScroll' method.

'UITableView' 与 'scrollViewDidScroll' 方法中的 'UIScrollView' 相同。

So, its easy to emulate infinite scrolling.

因此,很容易模拟无限滚动。

  1. double the array so that head and tail are joined together to emulate circular table

  2. use my following code to make user switch between 1st part of doubled table and 2nd part of doubled table when they tend to reach the start or the end of the table.

  1. 将数组加倍,以便将头部和尾部连接在一起以模拟圆形表

  2. 使用我的以下代码让用户在双表的第一部分和双表的第二部分之间切换,当他们倾向于到达表格的开头或结尾时。

:

/* To emulate infinite scrolling...

The table data was doubled to join the head and tail: (suppose table had 1,2,3,4)
1 2 3 4|1 2 3 4 (actual data doubled)
---------------
1 2 3 4 5 6 7 8 (visualising joined table in eight parts)

When the user scrolls backwards to 1/8th of the joined table, user is actually at the 1/4th of actual data, so we scroll instantly (we take user) to the 5/8th of the joined table where the cells are exactly the same.

Similarly, when user scrolls to 6/8th of the table, we will scroll back to 2/8th where the cells are same. (I'm using 6/8th when 7/8th sound more logical because 6/8th is good for small tables.)

In simple words, when user reaches 1/4th of the first half of table, we scroll to 1/4th of the second half, when he reaches 2/4th of the second half of table, we scroll to the 2/4 of first half. This is done simply by subtracting OR adding half the length of the new/joined table.
*/


-(void)scrollViewDidScroll:(UIScrollView *)scrollView_ 
{  

    CGFloat currentOffsetX = scrollView_.contentOffset.x;
    CGFloat currentOffSetY = scrollView_.contentOffset.y;
    CGFloat contentHeight = scrollView_.contentSize.height;

    if (currentOffSetY < (contentHeight / 8.0)) {
    scrollView_.contentOffset = CGPointMake(currentOffsetX,(currentOffSetY + (contentHeight/2)));
    }
   if (currentOffSetY > ((contentHeight * 6)/ 8.0)) {
       scrollView_.contentOffset = CGPointMake(currentOffsetX,(currentOffSetY - (contentHeight/2)));
    }

}

P.S. - I've used this code on one of my apps called NT Time Table (Lite). If you want the preview, you can check out the app: https://itunes.apple.com/au/app/nt-time-table-lite/id528213278?mt=8

PS - 我在我的一个名为 NT Time Table (Lite) 的应用程序中使用了此代码。如果您想要预览,可以查看应用程序:https: //itunes.apple.com/au/app/nt-time-table-lite/id528213278?mt=8

If your table can sometimes be too short, at the beginning of the above method you can add a if logic to exit the method when data count is say for example less than 9.

如果您的表有时太短,在上述方法的开头,您可以添加一个 if 逻辑以在数据计数小于 9 时退出该方法。

回答by zevarito

For me worked better scrollViewDidEndDragging:than scrollViewDidScroll:.

对我来说,scrollViewDidEndDragging:scrollViewDidScroll:工作得更好。

The second approach will send you each position during scroll and cause, if you are fetching remote resources you will hit your endpoint several times, which is not good.

第二种方法会在滚动期间向您发送每个位置并导致,如果您正在获取远程资源,您将多次访问您的端点,这并不好。

Complete example based on @codafisolution with comments from @danielgomezricoabout how to calculate contentHeight:

基于@codafi解决方案的完整示例以及@danielgomezrico关于如何计算contentHeight 的评论

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
                  willDecelerate:(BOOL)decelerate {
    CGFloat actualPosition = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height - (self.tableView.frame.size.height);
    if (actualPosition >= contentHeight) {
        // fetch resources
        [self.tableView reloadData];
    }
}

回答by Domenico Vacchiano

Generally I override scrollViewDidEndDeceleratingand inside it I put my code to request more data.
Example:

通常我会覆盖scrollViewDidEndDecelerating并在其中放置我的代码来请求更多数据。
例子:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

    float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;

    if (endScrolling >= scrollView.contentSize.height){
        //put here your code

    }
}

Recently I uploaded on GitHub a subclass of UITableView, that implements the infinite scroll.
You can download it here:
https://github.com/alchimya/iOS-LazyTableView

最近在GitHub上上传了一个UITableView的子类,实现了无限滚动。
你可以在这里下载:https:
//github.com/alchimya/iOS-LazyTableView

回答by Shardul

rather than overriding we can do this optimally in layoutSubviews. Here's how I got it implemented. You can get to know more about the implementation here

我们可以在 layoutSubviews 中以最佳方式执行此操作,而不是覆盖。这是我如何实现它的。您可以在此处了解有关实施的更多信息

- (void)layoutSubviews{
[super layoutSubviews];

if(self.delegateForViews){

    CGPoint contentOffset = self.contentOffset;

    if([self.delegateForViews noOfViews]>numOfReusableViews){
        NSUInteger centerIndex=visibleViews.count/2;
        NSUInteger noOfViews=[self.delegateForViews noOfViews];
        UIView *centerView=[visibleViews objectAtIndex:centerIndex];

        CGPoint centerViewOrigin=centerView.frame.origin;
        CGSize centerViewSize=centerView.frame.size;
        CGFloat offsetDifference=contentOffset.x-centerViewOrigin.x;
        CGFloat offsetDifferenceAbs=fabs(contentOffset.x-centerViewOrigin.x);

        if(offsetDifferenceAbs>=centerViewSize.width){

            if(offsetDifference<0){
                currentPosition--;
            }else{
                currentPosition++;
            }

            self.contentOffset=centerViewOrigin;

            currentPosition=[self getPosition:currentPosition noOfViews:noOfViews];

            [self.delegateForViews clearView:centerView];
            [self.delegateForViews setupView:centerView forPosition:currentPosition];

            for (int i=centerIndex-1; i>=0; i--) {
                UIView* prevView=[visibleViews objectAtIndex:i];
                [self.delegateForViews clearView:prevView];
                [self.delegateForViews setupView:prevView forPosition:
                        [self getPosition:currentPosition-1 noOfViews:noOfViews]];
            }

            for (int i=centerIndex+1; i<visibleViews.count; i++) {
                UIView* nextView=[visibleViews objectAtIndex:i];
                [self.delegateForViews clearView:nextView];
                [self.delegateForViews setupView:nextView forPosition:
                        [self getPosition:currentPosition+1 noOfViews:noOfViews]];
            }

        }
    }

}

}

回答by Yethin Dias

scrollviewDidScroll will call when you move through the rows in tableview

当您在 tableview 中的行中移动时,scrollviewDidScroll 将调用

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    //check for the visible rows
    let indexpath = self.tableView.indexPathsForVisibleRows?.last
    //check if the visible row last is equal to the total number of counts
    if(indexpath?.last == self.listCount){
      //code for adding data to the tableview and reload the table view.
    }
}

look in the link for more details about indexPathForVisibleRows https://developer.apple.com/documentation/uikit/uitableview/1614885-indexpathsforvisiblerows

查看链接以了解有关 indexPathForVisibleRows https://developer.apple.com/documentation/uikit/uitableview/1614885-indexpathsforvisiblerows的更多详细信息

回答by Cornel Damian

One of the simple and that offered me everything i need is this class:

一个简单的并且为我提供了我需要的一切的是这个类:

https://github.com/jakemarsh/JMStatefulTableViewController

https://github.com/jakemarsh/JMStatefulTableViewController

You just need to subclass JMStatefulTableViewController and the it has 3 methods that you need to overwrite:

你只需要继承 JMStatefulTableViewController 并且它有 3 个你需要覆盖的方法:

  • one that is called on init, to get the initial data
    • statefulTableViewControllerWillBeginInitialLoading
  • one when the user pull to refresh
    • statefulTableViewControllerWillBeginLoadingFromPullToRefresh
  • one when is called for the infinite scroll (next page)
    • statefulTableViewControllerWillBeginLoadingNextPage
  • 在 init 上调用的一个,以获取初始数据
    • statefulTableViewControllerWillBeginInitialLoading
  • 用户拉动刷新时的一种
    • statefulTableViewControllerWillBeginLoadingFromPullToRefresh
  • 无限滚动时调用(下一页)
    • statefulTableViewControllerWillBeginLoadingNextPage

This can be used from Cocoapodstoo.

这也可以从Cocoapods 中使用。