zoukankan      html  css  js  c++  java
  • UITableView个人使用总结【前篇-增量加载】

    UITableView现在边整边总结。

    预计分两个部分,第一个部分主要是对UITableView本身属性的学习。第二个部分可能会是加上一个编辑按钮以及对列表的操作。

    今天先学习第一部分。


    第一部分,我主要通过一个实例来学习:TableView的增量加载。

    什么是增量加载?大概就是下拉刷新,上拉加载更多这些功能。在这里我们的数据是本地模拟的数据。服务器数据请求类似。

    要实现增量加载,我的做法是数据源得分页(希望有更好做法的朋友能够下方留言交流)

    数据源分页,简单说,数据源会有两个属性,pageIndex和pageSize。pageSize是每页的大小,pageIndex是页数。在数据加载的时候,我会首先从数据源取得pageIndex=1的数据,保存在数组array中,然后每次需要增量加载更多的时候,便会去取得pageIndex++的数据,加到array里,然后reloadData;

    以上是大概思路,接下来是具体实现:

    在这里再复习一下基类和派生类的用法,因为增量加载的TableView会在很多地方用到,你不能每个用到的地方,都去写一次。所以应该抽个基类。

    【插一嘴先】:由于我直接截取的代码片段,有些没解释的东西读者能理解就理解,不能理解不影响本文主旨阅读的。

    @interface IncrementalTableView : UITableView<UITableViewDelegate,UITableViewDataSource>

    新手经常犯的问题,尤其是直接从故事版入手的新童鞋,这里没法连线,所以一定要手动设置TableView的Delegate和DataSource!(哈哈,我在说自己,找了好久才反应过来)

    self.delegate = self;
    self.dataSource = self;
    

    然后,实现dataSource最主要的三个方法

    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    -(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    

    其中头两个根据需要,进行异常判断和设置,在这里我分别返回的数组个数和1

    第三个方法由于本类只是基类,所以就留给派生类们各自去实现了。

    嗯,基本的TableView的架子就搭起了,然后只需要派生类的实现自己的时候,传入数据就可以了,类似

    [super.mArray addObjectsFromArray:nArray];//mArray就是我定义的数据源,是NSMutableArray类型
    [self reloadData];
    

    接下来就是实现增量加载了,(增量加载的方法,我也没有好办法,网上很多demo我看上去很费力,希望有好想法的朋友一样留言交流交流)

    增量加载的实现是通过实现UIScrollDelagate,计算拉动的位移来实现的。

    我在scrollViewDidScroll方法里,判断拉动的距离,然后在scrollViewDidEndDragging方法里调用加载数据的方法。

    - (void)scrollViewDidScroll:(UIScrollView *)sender{
        int marggin_Y = [[NSString stringWithFormat:@"%f",sender.contentOffset.y+sender.frame.size.height] intValue];
        if ([mArray count] == 0) {
            return;
        }
        if (marggin_Y < updateCriticalLine) {
            headerLabel.text = @"释放刷新";
            isNeedUpdate = YES;
        }else {
            headerLabel.text = @"下拉刷新列表";
            isNeedUpdate = NO;
        }
        if (marggin_Y > (cellHeight * pageSize)*pageIndex+160) {
            footerLabel.text = @"释放加载";
            isNeedLoad = YES;
        }else {
            footerLabel.text = @"下拉加载更多";
            isNeedLoad = NO;
        }
    }

    稍微解释一下,代码里这些数据是根据临界坐标加上了一些缓冲距离的结果,就是说不会刚刚拉到底部就执行加载更多,而会拉过了多少距离再加载,这个值大家可以自定。iOS7在下拉刷新的时候还会存在兼容性问题,会多20个像素(或者说之前的会少20个像素)。所以说没办法又做了一个版本判断……结果为代码中updateCriticalLine

    CellHeight是每个Cell的高度,pageSize和pageIndex如前文介绍,不多做解释。

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
    {
        if (isLoading == YES)  return; 
        if (isNeedUpdate == YES) {
            [self updateInfo];
            isLoading = YES;
        }
        if (isNeedLoad == YES) {
            [self getMoreInfo];
            [footerIndicator startAnimating];
            isLoading = YES;
        }
    }
    

    之所以放到scrollViewDidEndDragging方法里来加载数据,主要是为了方便用户取消,增强一下体验效果。isLoading是一个标志,不能重复发起请求。

    跟着代码走,接下来是updateInfo和getMoreInfo两个方法,updateInfo,就是下拉刷新操作。就是重新调用一遍第一次请求。

    这两段代码都应该由派生类实现,主要就是获取数据,我就不多说了,注意一下updateInfo是替换数组,getMoreInfo是追加数组就好了,实例代码上面已经贴过了。

    到现在为止,tableView的下拉刷新,上拉加载更多就已经基本实现了。

    接下来我们继续优化,增量加载一个必不可少的东西,就是表格的Header和Footer。为了不让用户感觉莫名奇妙的数据加载,友好的提示必不可少。

        //Header、Footer设置
        [self setContentInset:UIEdgeInsetsMake(-100, 0, -100, 0)];
        header = [[UIView alloc]initWithFrame:CGRectMake(0, 0, cellWidth, 100)];
        footer = [[UIView alloc]initWithFrame:CGRectMake(0, 0, cellWidth, 100)];
        [header setBackgroundColor:[UIColor lightGrayColor]];
        [footer setBackgroundColor:[UIColor lightGrayColor]];
        //Header、Footer文字信息设置
        headerLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, cellWidth, 40)];
        [headerLabel setTextAlignment:NSTextAlignmentCenter];
        [headerLabel setBackgroundColor:[UIColor clearColor]];
        headerLabel.center = header.center;
        [header addSubview:headerLabel];
        footerLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, cellWidth, 40)];
        [footerLabel setTextAlignment:NSTextAlignmentCenter];
        [footerLabel setBackgroundColor:[UIColor clearColor]];
        footerLabel.center = footer.center;
        [footer addSubview:footerLabel];
        //Header、Footer活动指示器设置
        /**
         *刷新(顶部)指示器不需要
         headerIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
         headerIndicator.center = CGPointMake(header.center.x-100, header.center.y);
         [headerIndicator setHidesWhenStopped:YES];
         [header addSubview:headerIndicator];
         */
        footerIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
        footerIndicator.center = CGPointMake(footer.center.x-100, footer.center.y);
        [footerIndicator setHidesWhenStopped:YES];
        [footer addSubview:footerIndicator];
        //初始化Header、Footer设置
        self.tableHeaderView = header;
        self.tableFooterView = footer;
        self.tableHeaderView.hidden = YES;
        self.tableFooterView.hidden = YES;
        //[headerIndicator stopAnimating];
        [footerIndicator stopAnimating];
    

    嗯,我也是用惯了故事版,纯代码用的不多,反正能解决问题就是。如上示代码初始化了一个Header和一个Footer,并且网上加了活动指示器和描述文字(1、当然这个可以做得更完善,2、后来想想更新的指示器改成了类似Android的Toast,感觉效果更好。)并且默认隐藏,以免数据加载失败之后仍然会出现很怪异的两坨不明物。

    当然,说到数据加载失败,我们还得加上数据加载失败的异常处理:

    我是这样做的,我在派生类中的updateInfo和getMoreInfo两个方法中,做的判断,如果加载失败,则会共同调用基类的实现方法,

    其中更新或者第一次(相同逻辑)失败,则会:

        self.separatorStyle = UITableViewCellSeparatorStyleNone;
        btnRetry = [UIButton buttonWithType:UIButtonTypeCustom];
        [btnRetry setFrame:CGRectMake(0, 0, 100, 50)];
        btnRetry.center = self.center;
        [btnRetry setTitle:@"重新加载" forState:UIControlStateNormal];
        [btnRetry setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
        [btnRetry setTitleColor:[UIColor clearColor] forState:UIControlStateHighlighted];
        [btnRetry addTarget:self action:@selector(btnRetry:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:btnRetry];
    

    取消分割线,提供重试按钮(重试的实现就是重新调用一边方法)

    加载更多失败的提示方式则是Toast一下(Toast这里就不讲了,自定义,不会的网上很多demo)

    There there,看上去一个自定义的增量在家tableView貌似大功告成了!其实我们还可以稍微优化一下……

    就是我们的上面加的Footer了,试想一下,如果列表数据内容不足填充列表区域,极端点,只有1个Cell,剩下很长的空白会怎么样呢?答案是骚丑骚丑的Footer会跟在Cell后面。(当然,如果你的Footer很美观,你也可以根据你的需要保留,做提示用。)

    怎么去掉?去掉的方式无非就是先判断装满没?如果没满,就去掉。怎么判断?我使用的这个方法:

    NSArray *visiblePaths = [self indexPathsForVisibleRows];
    if ([visiblePaths count] == [mArray count])  self.tableFooterView.hidden = YES;
    else self.tableFooterView.hidden = NO;
    

    利用系统api给的indexPathsForVisibleRows方法,得到可见区域的rows,如果数量等于我所有的rows的数量,说明已经显示完了,然后就让他hidden。

    如果你还需要他显示完了不能滚动之类的需求,也一并可以在这里操作。

    嗯,到这里真正差不多了,一个完全自定义的增量加载TableView新鲜出炉。技术水平有限,欢迎大神斧正。

  • 相关阅读:
    Oracle常用命令大全(很有用,做笔记)
    表格驱动编程在代码中的应用
    mac 利用svn下载远程代码出现Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo.
    FAILURE: Build failed with an exception.
    There is an internal error in the React performance measurement code.Did not expect componentDidMount timer to start while render timer is still in progress for another instance
    react native TypeError network request failed
    Android向系统相册中插入图片,相册中会出现两张 一样的图片(只是图片大小不一致)
    react-native Unrecognized font family ‘Lonicons’;
    react-native SyntaxError xxxxx/xx.js:Unexpected token (23:24)
    Application MyTest has not been registered. This is either due to a require() error during initialization or failure to call AppRegistry.registerComponent.
  • 原文地址:https://www.cnblogs.com/anjohnlv/p/3406748.html
Copyright © 2011-2022 走看看