美文网首页
MJRefresh的底层实现原理

MJRefresh的底层实现原理

作者: ScaryMonsterLyn | 来源:发表于2016-05-11 14:43 被阅读251次
    #import "ViewController.h"
    #import "MJExtension.h"
    #import "AFNetworking.h"
    #import "UIView+Frame.h"
    #import "TopicItem.h"
    
    @interface ViewController ()
    
    //* 请求管理者 
    @property (nonatomic, weak) AFHTTPSessionManager *mgr;
    /** 全部的帖子数据 */
    @property (nonatomic, strong) NSMutableArray *topics;
    /** 用来加载下一页数据 */
    @property (nonatomic, copy) NSString *maxtime;
    
    /******** 下拉刷新-header ********/
    /** 下拉刷新控件 */
    @property (nonatomic, weak) UIView *header;
    /** 下拉刷新控件里面的文字 */
    @property (nonatomic, weak) UILabel *headerLabel;
    /** 是否为"松开立即刷新" */
    @property(nonatomic, assign, getter=isWillLoadingNewData) BOOL willLoadingNewData;
    /** 是否为"正在刷新" */
    @property(nonatomic, assign, getter=isLoadingNewData) BOOL loadingNewData;
    /******** 下拉刷新-header ********/
    
    /******** 上拉刷新-footer ********/
    /** 上拉刷新控件 */
    @property (nonatomic, weak) UIView *footer;
    /** 上拉刷新控件里面的文字 */
    @property (nonatomic, weak) UILabel *footerLabel;
    /** 是否正在加载更多数据 */
    @property(nonatomic, assign, getter=isLoadingMoreData) BOOL loadingMoreData;
    /******** 上拉刷新-footer ********/
    
    @end
    
    @implementation ViewController
    
    #pragma mark - 懒加载
    - (AFHTTPSessionManager *)mgr
    {
        if (!_mgr) {
            _mgr = [AFHTTPSessionManager manager];
        }
        return _mgr;
    }
    
    #pragma mark - 初始化
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.tableView.contentInset = UIEdgeInsetsMake(61, 0, 0, 0);
    
        // 集成刷新控件
        [self setUpRefresh];
    
        [self loadNewTopics];
    }
    
    /**
     * 集成刷新控件
     */
    - (void)setUpRefresh
    {
        // 下拉刷新:加载最新的数据
        UIView *header = [[UIView alloc] init];
        header.backgroundColor = [UIColor yellowColor];
        header.height = 60;
        header.width = self.tableView.width;
        header.y = - header.height;
        [self.tableView addSubview:header];
        self.header = header;
    
        UILabel *headerLabel = [[UILabel alloc] init];
        headerLabel.text = @"下拉可以刷新";
        headerLabel.width = self.tableView.width;
        headerLabel.height = header.height;
        headerLabel.textAlignment = NSTextAlignmentCenter;
        [header addSubview:headerLabel];
        self.headerLabel = headerLabel;
    
        // 上拉刷新:加载更多的数据
        UIView *footer = [[UIView alloc] init];
        footer.backgroundColor = [UIColor orangeColor];
        footer.height = 35;
        footer.hidden = YES;
        self.tableView.tableFooterView = footer;
        self.footer = footer;
    
        UILabel *footerLabel = [[UILabel alloc] init];
        footerLabel.text = @"上拉可以加载更多";
        footerLabel.width = self.tableView.width;
        footerLabel.height = footer.height;
        footerLabel.textAlignment = NSTextAlignmentCenter;
        [footer addSubview:footerLabel];
        self.footerLabel = footerLabel;
    }
    
    #pragma mark - 数据处理
    /**
     * 加载最新的帖子数据
     */
    - (void)loadNewTopics
    {
        // 拼接请求参数
        NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
        parameters[@"a"] = @"list";
        parameters[@"c"] = @"data";
        parameters[@"type"] = @"1";
    
        // 发送请求
        [self.mgr GET:@"http://api.budejie.com/api/api_open.php" parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            // 存储maxtime
            self.maxtime = responseObject[@"info"][@"maxtime"];
    
            // 字典数组 -> 模型数组
            self.topics = [TopicItem mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
    
            // 刷新表格
            [self.tableView reloadData];
    
            // 结束刷新
            self.loadingNewData = NO;
            // 恢复顶部的内边距
            [UIView animateWithDuration:0.25 animations:^{
                UIEdgeInsets inset = self.tableView.contentInset;
                inset.top -= self.header.height;
                self.tableView.contentInset = inset;
            }];
    
            // 有数据了
            self.footer.hidden = NO;
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"请求失败 - %@", error);
            // 结束刷新
            self.loadingNewData = NO;
            // 恢复顶部的内边距
            [UIView animateWithDuration:0.25 animations:^{
                UIEdgeInsets inset = self.tableView.contentInset;
                inset.top -= self.header.height;
                self.tableView.contentInset = inset;
            }];
        }];
    }
    
    /**
     * 加载更多的帖子数据
     */
    - (void)loadMoreTopics
    {
        // 拼接请求参数
        NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
        parameters[@"a"] = @"list";
        parameters[@"c"] = @"data";
        parameters[@"type"] = @"1";
        parameters[@"maxtime"] = self.maxtime;
    
        // 发送请求
        [self.mgr GET:@"http://api.budejie.com/api/api_open.php" parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            // 存储maxtime
            self.maxtime = responseObject[@"info"][@"maxtime"];
    
            // 字典数组 -> 模型数组
            NSArray *moreTopics = [TopicItem mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
            [self.topics addObjectsFromArray:moreTopics];
    
            // 刷新表格
            [self.tableView reloadData];
    
            // 结束刷新
            self.loadingMoreData = NO;
            self.footerLabel.text = @"上拉可以加载更多";
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"请求失败 - %@", error);
    
            // 结束刷新
            self.loadingMoreData = NO;
            self.footerLabel.text = @"上拉可以加载更多";
        }];
    }
    
    #pragma mark - 代理方法
    /**
     * 当scrollView在滚动,就会调用这个代理方法
     */
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        // 处理下拉刷新
        [self dealLoadNewData];
    
        // 处理上拉加载更多
        [self dealLoadMoreData];
    }
    
    /**
     * 当用户手松开(停止拖拽),就会调用这个代理方法
     */
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        if (self.willLoadingNewData == NO || self.loadingNewData) return;
    
        // 增加顶部的内边距
        [UIView animateWithDuration:0.25 animations:^{
            UIEdgeInsets inset = self.tableView.contentInset;
            inset.top += self.header.height;
            self.tableView.contentInset = inset;
        }];
    
        // 修改文字
        self.headerLabel.text = @"正在刷新数据...";
    
        // 正在刷新
        self.loadingNewData = YES;
    
        // 发送请求
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
            [self loadNewTopics];
        });
    }
    
    /**
     * 处理下拉刷新
     */
    - (void)dealLoadNewData
    {
        if (self.loadingNewData) return;
    
        CGFloat offsetY =  - (64 + self.header.height);
        if (self.tableView.contentOffset.y <= offsetY) {
            self.headerLabel.text = @"松开立即刷新";
            self.willLoadingNewData = YES;
        } else {
            self.headerLabel.text = @"下拉可以刷新";
            self.willLoadingNewData = NO;
        }
    }
    
    /**
     * 处理上拉加载更多
     */
    - (void)dealLoadMoreData
    {
        // 如果没有数据 或者 正在上拉刷新, 直接返回
        if (self.topics.count == 0 || self.loadingMoreData) return;
    
        CGFloat offsetY = self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.height;
        if (self.tableView.contentOffset.y >= offsetY) {
            self.loadingMoreData = YES;
    
            // 更改文字
            self.footerLabel.text = @"正在加载更多的数据...";
    
            // 加载更多的帖子数据
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self loadMoreTopics];
            });
        }
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.topics.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
        }
    
        TopicItem *item = self.topics[indexPath.row];
        cell.textLabel.text = item.name;
        cell.detailTextLabel.text = item.text;
    
        return cell;
    }
    @end
    **效果**
    

    实现原理
    在tableView上加上一个View,注意不是headerView,而是一个Y值为负数的普通View,下拉时候监听偏移量,改变View的内容显示。在tableView下面加上一个footerView,监听偏移量,当footerView完全显示的时候加载更多数据。基本代码已贴,有不清楚的可以讨论交流。

    相关文章

      网友评论

          本文标题:MJRefresh的底层实现原理

          本文链接:https://www.haomeiwen.com/subject/iflurttx.html