iOS-通过偏移量实现上下拉刷新

2018-06-29  本文已影响0人  长衣貌

给UIView添加一个类目,用来快速获得控件的一些尺寸

//
//  UIView+Frame.h
//  PDBuDeJie
//
//  Created by 裴铎 on 2018/6/8.
//  Copyright © 2018年 裴铎. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIView (Frame)

/**
 宽
 */
@property CGFloat pd_width;

/**
 高
 */
@property CGFloat pd_height;

/**
 X轴
 */
@property CGFloat pd_x;

/**
 Y轴
 */
@property CGFloat pd_y;

/**
 中心X轴
 */
@property CGFloat pd_centerX;

/**
 中心Y轴
 */
@property CGFloat pd_centerY;

@end

//
//  UIView+Frame.m
//  PDBuDeJie
//
//  Created by 裴铎 on 2018/6/8.
//  Copyright © 2018年 裴铎. All rights reserved.
//

#import "UIView+Frame.h"

@implementation UIView (Frame)

- (void)setPd_width:(CGFloat)pd_width{
    
    CGRect rect = self.frame;
    rect.size.width = pd_width;
    self.frame = rect;
}

- (CGFloat)pd_width{
    return self.frame.size.width;
}

- (void)setPd_height:(CGFloat)pd_height{
    
    CGRect rect = self.frame;
    rect.size.height = pd_height;
    self.frame = rect;
}

- (CGFloat)pd_height{
    return self.frame.size.height;
}

- (void)setPd_x:(CGFloat)pd_x{
    
    CGRect rect = self.frame;
    rect.origin.x = pd_x;
    self.frame = rect;
}

- (CGFloat)pd_x{
    return self.frame.origin.x;
}

- (void)setPd_y:(CGFloat)pd_y{
    
    CGRect rect = self.frame;
    rect.origin.y = pd_y;
    self.frame = rect;
}

- (CGFloat)pd_y{
    return self.frame.origin.y;
}

- (void)setPd_centerX:(CGFloat)pd_centerX{
    CGPoint center = self.center;
    center.x = pd_centerX;
    self.center = center;
}

- (CGFloat)pd_centerX{
    return self.center.x;
}

- (void)setPd_centerY:(CGFloat)pd_centerY{
    CGPoint center = self.center;
    center.y = pd_centerY;
    self.center = center;
}

- (CGFloat)pd_centerY{
    return self.center.y;
}

@end

初始化上下拉刷新控件

- (void)setupRefresh{
    
    //加载头部广告视图
    //[self setupADView];
    
    //下啦刷新视图
    UIView * headerView = [[UIView alloc] init];
    
    //很多的应用tableView头部都会添加广告,所以 Y -50 让出广告位
    headerView.frame = CGRectMake(0, - 50, self.tableView.pd_width, 50);
    
    //赋值给全局属性
    self.headerView = headerView;
    
    UILabel * headerLabel = [[UILabel alloc] init];
    headerLabel.frame = headerView.bounds;
    headerLabel.backgroundColor = [UIColor grayColor];
    headerLabel.text = @"下拉加载更多";
    headerLabel.textAlignment = NSTextAlignmentCenter;
    self.headerLabel = headerLabel;
    
    [self.headerView addSubview:self.headerLabel];
    
    //把下啦刷新的视图添加到tableView的子视图,这样不会影响tableView的contentSize
    [self.tableView addSubview:self.headerView];
    
    //上拉刷新
    UIView * footerView = [[UIView alloc] init];
    footerView.frame = CGRectMake(0, 0, self.tableView.pd_width, 35);
    self.footerView = footerView;
    
    UILabel * footerLabel = [[UILabel alloc] init];
    footerLabel.frame = footerView.bounds;
    footerLabel.backgroundColor = [UIColor grayColor];
    footerLabel.text = @"上拉加载更多";
    footerLabel.textAlignment = NSTextAlignmentCenter;
    self.footerLabel = footerLabel;
    
    [self.footerView addSubview:self.footerLabel];
    
    self.tableView.tableFooterView = self.footerView;
}

当滚动视图结束减速时触发

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    
    CGFloat offsetY = - (self.tableView.contentInset.top + self.headerView.pd_height + 64);
    if (self.tableView.contentOffset.y <= offsetY) {
        //进入刷新状态
        [self headerBeginRefreshing];
    }
}

只要滚动了就调用头尾的刷新方法,在刷新方法内判断滚动到哪里了,是否开始刷新
因为应用内有全屏穿透效果,所以头部的下拉刷新控件默认时隐藏的,当向下拖拽到一定位置才会出现

//只要进行滚动就触发的代理方法,
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    //处理header下拉刷新
    [self dealHeader];
    
    //处理footer上拉刷新
    [self dealFooter];
    
    //当tableView向下拖拽的时候,显示提示标题
    if (self.tableView.contentOffset.y < -99) {
        //当视图滚动到一定的位置显示下拉刷新提示label
        self.headerLabel.hidden = NO;
    }else{
        self.headerLabel.hidden = YES;
    }
}

处理上下刷新的方法,内部判断滚动的位置,和是否应该刷新

/**
 处理header
 */
- (void)dealHeader{
    
    //正在进行下啦刷新
    //定义布尔型变量用来判断是否正在刷新
    if (self.isHeaderRefreshing) return;
    
    //当前滚动的偏移量 <= offsetY时,证明tableView下啦刷新控件已经完全出现了
    CGFloat offsetY = - (self.tableView.contentInset.top + self.headerView.pd_height + 64);
    if (self.tableView.contentOffset.y <= offsetY) {
        self.headerLabel.text = @"松开立即刷新";
    }else{
        self.headerLabel.text = @"下拉加载新鲜事.";
        self.headerLabel.backgroundColor = [UIColor grayColor];
    }
}

/**
 处理footer
 */
- (void)dealFooter{
    
    //如果当前tableview的内容没有加载完成就返回,防止出现tableView内没有内容,尾部的刷新直接触发了
    if (self.tableView.contentSize.height == 0) {
        return;
    }
    
    //判断如果正在刷新就返回,防止重复刷新
    if (self.isFooterRefreshing) return;
    
    //当前滚动的偏移量 > offsetY时,证明tableView的内容已经滚动完成了
    CGFloat offsetY = self.tableView.contentSize.height - self.tableView.pd_height + 49;
    
    if (self.tableView.contentOffset.y >= offsetY && self.tableView.contentOffset.y > - 90) {
        //进入刷新
        [self footerBeginRefreshing];
    }
}

用来给头部和尾部调用的请求数据的方法


#pragma mark - 数据处理
/**
 *  发送请求给服务器,下拉刷新数据
 */
- (void)loadNewTopics{
    
    //创建AFN管理器对象
    AFHTTPSessionManager * manager = [AFHTTPSessionManager manager];
    
    //设置AFN的解析器类型 默认是JSON的解析器,如果解析JSON可以不写这个
    manager.responseSerializer = [[AFJSONResponseSerializer alloc] init];
    
    //拼接参数
    NSMutableDictionary * parameters = [NSMutableDictionary dictionary];
    //给网络请求的每一个参数 赋值 parameters[@"key"] = @"value";
    parameters[@"key"] = @"value";
    
    /**
     参数
     1./URL
     2./请求参数
     3./获得下载进度
     4./请求成功执行的代码块 代码block块都是在主线程执行
     5./请求失败执行的代码块
     */
    [manager GET:PDPublicURL parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
        //下载进度
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //下载成功
        NSLog(@"%@",responseObject);        

        //回到主线程刷新UI界面
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });
        
        //结束刷新
        [self headerEndRefreshing];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //下载失败
        [SVProgressHUD showErrorWithStatus:@"网络繁忙,请稍后再试."];
        
        //结束刷新
        [self headerEndRefreshing];
    }];
}

/**
 *  发送请求给服务器,上拉加载更多数据
 */
- (void)loadMoreTopics{
    
    //创建AFN管理器对象
    AFHTTPSessionManager * manager = [AFHTTPSessionManager manager];
    
    //设置AFN的解析器类型 默认是JSON的解析器,如果解析JSON可以不写这个
    manager.responseSerializer = [[AFJSONResponseSerializer alloc] init];
    
    //拼接参数
    NSMutableDictionary * parameters = [NSMutableDictionary dictionary];
    //给网络请求的每一个参数 赋值 parameters[@"key"] = @"value";
    parameters[@"key"] = @"value";
    
    
    /**
     参数
     1./URL
     2./请求参数
     3./获得下载进度
     4./请求成功执行的代码块 代码block块都是在主线程执行
     5./请求失败执行的代码块
     */
    [manager GET:PDPublicURL parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
        //下载进度
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //下载成功
        
        NSLog(@"%@",responseObject);
        //回到主线程刷新UI界面
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });
        
        //结束刷新
        [self footerEndRefreshing];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //下载失败
        [SVProgressHUD showErrorWithStatus:@"网络繁忙,请稍后再试."];
        
        //结束刷新
        [self footerEndRefreshing];
    }];
}

头部和尾部开始刷新的方法,开始刷新内部必须调用结束刷新方法

#pragma mark - header开始刷新
- (void)headerBeginRefreshing
{
    // 如果正在下拉刷新,直接返回
    if (self.isHeaderRefreshing) return;
    
    // 如果正在上拉刷新,直接返回
    if (self.isFooterRefreshing) return;
    
    // 进入下拉刷新状态
    self.headerLabel.text = @"正在刷新数据...";
    self.headerLabel.backgroundColor = [UIColor blueColor];
    self.headerRefreshing = YES;
    // 增加内边距
    [UIView animateWithDuration:0.3 animations:^{
        UIEdgeInsets inset = self.tableView.contentInset;
        inset.top = 85;
        self.tableView.contentInset = inset;

    }];
    // 修改偏移量 让tableView回到顶部刷新所有数据
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x,  - 149);
    
    // 发送请求给服务器,下拉刷新数据
    [self loadNewTopics];
}

- (void)headerEndRefreshing
{
    self.headerRefreshing = NO;
    
    // 减小内边距
    [UIView animateWithDuration:0.2 animations:^{
        UIEdgeInsets inset = self.tableView.contentInset;
        inset.top = 35;
        self.tableView.contentInset = inset;
        self.headerLabel.hidden = YES;
    }];
}

#pragma mark - footer开始刷新
- (void)footerBeginRefreshing
{
    // 如果正在上拉刷新,直接返回
    if (self.isFooterRefreshing) return;
    
    // 如果正在下拉刷新,直接返回
    if (self.isHeaderRefreshing) return;
    
    // 进入刷新状态
    self.footerRefreshing = YES;
    self.footerLabel.text = @"正在加载更多数据...";
    
    // 发送请求给服务器,上拉加载更多数据
    [self loadMoreTopics];
}

- (void)footerEndRefreshing
{
    //结束刷新后将显示标签和bool变量变会初始状态
    self.footerRefreshing = NO;
    self.footerLabel.text = @"上拉可以加载更多";
}
上一篇下一篇

猜你喜欢

热点阅读