iOS下拉、上拉刷新控件的封裝
iOS 封裝下拉、上拉刷新控件,首先看下效果圖:

簡(jiǎn)單闡述一下:自定義頭部、尾部刷新視圖,繼承UIView,通過KVO監(jiān)聽scrollView的滑動(dòng),通過偏移量設(shè)置刷新狀態(tài),通過修改狀態(tài)修改scrollView的滾動(dòng)位置。建一個(gè)UIScrollView的分類,添加上拉、下拉刷新及回調(diào)的方法,可以讓UITableView、UICollectionView直接調(diào)用?,F(xiàn)在很多應(yīng)用是在滑動(dòng)到底部自動(dòng)進(jìn)行上拉加載超做,可以在scrollViewDidScroll這個(gè)代理方法中手動(dòng)調(diào)用尾部刷新。
下面貼上主要相關(guān)代碼:
控制器ViewController:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
/*** ---------------分割線--------------- ***/
#import "ViewController.h"
#import "HWRefresh.h"
@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) NSMutableArray *array;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, assign) NSInteger page;
@end
@implementation ViewController
- (NSMutableArray *)array
{
if (!_array) {
_array = [NSMutableArray array];
}
return _array;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.page = 1;
//模擬獲取信息
[self getInfo];
//創(chuàng)建控件
[self creatControl];
//添加頭部刷新
[self addHeaderRefresh];
//添加尾部刷新
[self addFooterRefresh];
}
- (void)getInfo
{
NSArray *array = @[@"iOS HERO博客", @"iOS HERO博客", @"iOS HERO博客", @"iOS HERO博客", @"http://blog.csdn.net/hero_wqb"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.page == 1) {
self.array = [NSMutableArray arrayWithArray:array];
}else{
[self.array addObjectsFromArray:array];
}
[_tableView reloadData];
[_tableView headerEndRefreshing];
[_tableView footerEndRefreshing];
NSLog(@"已經(jīng)刷新好了");
});
}
- (void)creatControl
{
//列表視圖
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(20, 64, [[UIScreen mainScreen] bounds].size.width - 100, [[UIScreen mainScreen] bounds].size.height - 164) style:UITableViewStylePlain];
_tableView.dataSource = self;
_tableView.delegate = self;
[self.view addSubview:_tableView];
}
- (void)addHeaderRefresh
{
__weak typeof(self) weakSelf = self;
[_tableView addHeaderRefreshWithCallback:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.page = 1;
[strongSelf getInfo];
}];
}
- (void)addFooterRefresh
{
__weak typeof(self) weakSelf = self;
[_tableView addFooterRefreshWithCallback:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.page ++;
[strongSelf getInfo];
}];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.array.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"refreshTest";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
cell.textLabel.text = [_array[indexPath.row] stringByAppendingString:[NSString stringWithFormat:@"_%ld", indexPath.row]];
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//滑動(dòng)到底部自動(dòng)刷新
if (_tableView.contentSize.height > _tableView.frame.size.height && _tableView.contentOffset.y + _tableView.frame.size.height > _tableView.contentSize.height - 40 && _page < 50) {
[_tableView footerBeginRefreshing];
}
}
@end
刷新基類HWRefreshBaseView:
#import <UIKit/UIKit.h>
#define HWRefreshContentOffset @"contentOffset"
typedef enum {
HWRefreshStateNormal = 0, //普通狀態(tài)
HWRefreshStatePulling, //釋放即可刷新的狀態(tài)
HWRefreshStateRefreshing, //正在刷新中的狀態(tài)
} HWRefreshState;
@interface HWRefreshBaseView : UIView
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, copy) NSString *pullToRefreshText;
@property (nonatomic, copy) NSString *releaseToRefreshText;
@property (nonatomic, copy) NSString *refreshingText;
@property (nonatomic, copy) void (^refreshingCallback)();
@property (nonatomic, assign) HWRefreshState state;
@property (nonatomic, assign) UIEdgeInsets scrollViewOriginalInset;
- (void)beginRefreshing;
- (void)endRefreshing;
@end
/*** ---------------分割線--------------- ***/
#import "HWRefreshBaseView.h"
#define KHWRefreshViewHeight 44.0f
#define KImageW 30.0f
#define KLabelW 100.0f
@interface HWRefreshBaseView ()
@property (nonatomic, weak) UILabel *rLabel;
@property (nonatomic, weak) UIImageView *rImageView;
@end
@implementation HWRefreshBaseView
- (instancetype)initWithFrame:(CGRect)frame
{
frame.size.height = KHWRefreshViewHeight;
if (self = [super initWithFrame:frame]) {
CGFloat imageH = 30.f;
CGFloat labelH = 20.f;
CGFloat imageX = ([UIScreen mainScreen].bounds.size.width - KImageW - KLabelW) * 0.5;
CGFloat imageY = (KHWRefreshViewHeight - imageH) * 0.5;
CGFloat labelY = (KHWRefreshViewHeight - labelH) * 0.5;
//圖片
UIImageView *rImageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, KImageW, imageH)];
rImageView.image = [UIImage imageNamed:@"refreshing.jpg"];
[self addSubview:rImageView];
self.rImageView = rImageView;
//標(biāo)簽
UILabel *rLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(rImageView.frame), labelY, KLabelW, labelH)];
rLabel.text = self.pullToRefreshText;
rLabel.font = [UIFont systemFontOfSize:14.0f];
rLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:rLabel];
self.rLabel = rLabel;
}
return self;
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
//舊的父控件
[self.superview removeObserver:self forKeyPath:HWRefreshContentOffset context:nil];
//新的父控件
if (newSuperview) {
[newSuperview addObserver:self forKeyPath:HWRefreshContentOffset options:NSKeyValueObservingOptionNew context:nil];
//記錄UIScrollView
_scrollView = (UIScrollView *)newSuperview;
//記錄UIScrollView最開始的contentInset
_scrollViewOriginalInset = _scrollView.contentInset;
}
//居中顯示圖片、提示信息
CGRect temFrame = _rImageView.frame;
temFrame.origin.x = (newSuperview.frame.size.width - KImageW - KLabelW) * 0.5;
_rImageView.frame = temFrame;
CGRect tf = _rLabel.frame;
tf.origin.x = CGRectGetMaxX(_rImageView.frame);
_rLabel.frame = tf;
}
- (void)setPullToRefreshText:(NSString *)pullToRefreshText
{
_pullToRefreshText = pullToRefreshText;
self.rLabel.text = pullToRefreshText;
}
- (void)setState:(HWRefreshState)state
{
if (_state == state) return;
switch (state) {
case HWRefreshStateNormal: {
[self stopAnimating];
self.rLabel.text = self.pullToRefreshText;
break;
}
case HWRefreshStatePulling: {
self.rLabel.text = self.releaseToRefreshText;
break;
}
case HWRefreshStateRefreshing: {
[self startAnimating];
self.rLabel.text = self.refreshingText;
if (self.refreshingCallback) self.refreshingCallback();
break;
}
default:
break;
}
_state = state;
}
//開始刷新
- (void)beginRefreshing
{
self.state = HWRefreshStateRefreshing;
}
//結(jié)束刷新
- (void)endRefreshing
{
self.state = HWRefreshStateNormal;
}
//開始動(dòng)畫
- (void)startAnimating
{
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 2; i++) {
NSString *imageName = [NSString stringWithFormat:@"refreshing%02d.jpg", i + 1];
UIImage *image = [UIImage imageNamed:imageName];
[array addObject:image];
}
[_rImageView setAnimationImages:array];
[_rImageView setAnimationDuration:0.3f];
[_rImageView startAnimating];
}
//結(jié)束動(dòng)畫
- (void)stopAnimating
{
if (_rImageView.isAnimating) {
[_rImageView stopAnimating];
[_rImageView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:0];
}
}
@end
頭部刷新HWRefreshHeader:
#import "HWRefreshBaseView.h"
@interface HWRefreshHeader : HWRefreshBaseView
+ (instancetype)header;
@end
/*** ---------------分割線--------------- ***/
#import "HWRefreshHeader.h"
@implementation HWRefreshHeader
+ (instancetype)header
{
return [[HWRefreshHeader alloc] init];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.pullToRefreshText = @"下拉即可刷新";
self.releaseToRefreshText = @"釋放即可刷新";
self.refreshingText = @"刷新中...";
}
return self;
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
//設(shè)置自己的位置和尺寸
CGRect frame = self.frame;
frame.origin.y = - self.frame.size.height;
self.frame = frame;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//不能跟用戶交互或正在刷新就直接返回
if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden || self.state == HWRefreshStateRefreshing) return;
//根據(jù)偏移量設(shè)置相應(yīng)狀態(tài)
if ([keyPath isEqualToString:HWRefreshContentOffset]) {
[self setStateWithContentOffset];
}
}
- (void)setStateWithContentOffset
{
//當(dāng)前的contentOffset
CGFloat currentOffsetY = self.scrollView.contentOffset.y;
//頭部控件剛好出現(xiàn)的offsetY
CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
//如果是向上滾動(dòng)到看不見頭部控件,直接返回
if (currentOffsetY >= happenOffsetY) return;
//滑動(dòng)時(shí)
if (self.scrollView.isDragging) {
//普通狀態(tài)和即將刷新狀態(tài)的臨界點(diǎn)
CGFloat normalTopullingOffsetY = happenOffsetY - self.frame.size.height;
//轉(zhuǎn)為即將刷新狀態(tài)
if (self.state == HWRefreshStateNormal && currentOffsetY < normalTopullingOffsetY) {
self.state = HWRefreshStatePulling;
//轉(zhuǎn)為普通狀態(tài)
}else if (self.state == HWRefreshStatePulling && currentOffsetY >= normalTopullingOffsetY) {
self.state = HWRefreshStateNormal;
}
//松手時(shí),如果是松開就可以進(jìn)行刷新的狀態(tài),則進(jìn)行刷新
}else if (self.state == HWRefreshStatePulling) {
self.state = HWRefreshStateRefreshing;
}
}
- (void)setState:(HWRefreshState)state
{
//若狀態(tài)未改變,直接返回
if (self.state == state) return;
//保存舊狀態(tài)
HWRefreshState oldState = self.state;
//調(diào)用父類方法
[super setState:state];
switch (state) {
case HWRefreshStateNormal: {
//如果由刷新狀態(tài)返回到普通狀態(tài)
if (oldState == HWRefreshStateRefreshing) {
[UIView animateWithDuration:0.25f animations:^{
UIEdgeInsets inset = self.scrollView.contentInset;
inset.top -= self.frame.size.height;
self.scrollView.contentInset = inset;
}];
}
break;
}
case HWRefreshStatePulling: {
break;
}
case HWRefreshStateRefreshing: {
//執(zhí)行動(dòng)畫
[UIView animateWithDuration:0.25f animations:^{
CGFloat top = self.scrollViewOriginalInset.top + self.frame.size.height;
//增加滾動(dòng)區(qū)域
UIEdgeInsets inset = self.scrollView.contentInset;
inset.top = top;
self.scrollView.contentInset = inset;
//設(shè)置滾動(dòng)位置
CGPoint offset = self.scrollView.contentOffset;
offset.y = - top;
self.scrollView.contentOffset = offset;
}];
break;
}
default:
break;
}
self.state = state;
}
@end
分類UIScrollView+HWRefresh:
#import <UIKit/UIKit.h>
@interface UIScrollView (HWRefresh)
//添加下拉刷新回調(diào)
- (void)addHeaderRefreshWithCallback:(void (^)())callback;
//讓下拉刷新控件停止刷新
- (void)headerEndRefreshing;
//添加上拉刷新回調(diào)
- (void)addFooterRefreshWithCallback:(void (^)())callback;
//讓上拉刷新控件開始刷新
- (void)footerBeginRefreshing;
//讓上拉刷新控件停止刷新
- (void)footerEndRefreshing;
@end
/*** ---------------分割線--------------- ***/
#import "UIScrollView+HWRefresh.h"
#import "HWRefreshHeader.h"
#import "HWRefreshFooter.h"
#import <objc/runtime.h>
@interface UIScrollView ()
@property (nonatomic, weak) HWRefreshHeader *header;
@property (weak, nonatomic) HWRefreshFooter *footer;
@end
@implementation UIScrollView (HWRefresh)
static char HWRefreshHeaderKey;
static char HWRefreshFooterKey;
- (void)setHeader:(HWRefreshHeader *)header
{
[self willChangeValueForKey:@"HWRefreshHeaderKey"];
objc_setAssociatedObject(self, &HWRefreshHeaderKey, header, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"HWRefreshHeaderKey"];
}
- (HWRefreshHeader *)header
{
return objc_getAssociatedObject(self, &HWRefreshHeaderKey);
}
- (void)setFooter:(HWRefreshFooter *)footer
{
[self willChangeValueForKey:@"HWRefreshFooterKey"];
objc_setAssociatedObject(self, &HWRefreshFooterKey, footer, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"HWRefreshFooterKey"];
}
- (HWRefreshFooter *)footer
{
return objc_getAssociatedObject(self, &HWRefreshFooterKey);
}
- (void)addHeaderRefreshWithCallback:(void (^)())callback
{
if (!self.header) {
HWRefreshHeader *header = [HWRefreshHeader header];
[self addSubview:header];
self.header = header;
}
self.header.refreshingCallback = callback;
}
- (void)headerEndRefreshing
{
[self.header endRefreshing];
}
- (void)addFooterRefreshWithCallback:(void (^)())callback
{
if (!self.footer) {
HWRefreshFooter *footer = [HWRefreshFooter footer];
[self addSubview:footer];
self.footer = footer;
}
self.footer.refreshingCallback = callback;
}
- (void)footerBeginRefreshing
{
[self.footer beginRefreshing];
}
- (void)footerEndRefreshing
{
[self.footer endRefreshing];
}
@end
寫博客的初心是希望大家共同交流成長(zhǎng),博主水平有限難免有偏頗之處,歡迎批評(píng)指正。
相關(guān)文章
iOS驗(yàn)證手機(jī)號(hào)的正則表達(dá)式
這篇文章主要為大家詳細(xì)介紹了iOS驗(yàn)證手機(jī)號(hào)的正則表達(dá)式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
解析Objective-C?中?`+load`?方法的執(zhí)行順序
在?Objective-C?中,+load?方法是在類或分類被加載到內(nèi)存時(shí)調(diào)用的,它在程序啟動(dòng)過程中非常早的階段執(zhí)行,用于在類或分類被加載時(shí)進(jìn)行一些初始化工作,這篇文章主要介紹了?Objective-C?中?`+load`?方法的執(zhí)行順序,需要的朋友可以參考下2024-07-07
在iOS App中實(shí)現(xiàn)地理位置定位的基本方法解析
這篇文章主要介紹了在iOS App中實(shí)現(xiàn)地理位置定位的基本方法解析,包括獲取當(dāng)前位置和計(jì)算兩點(diǎn)間距離等基本功能的實(shí)現(xiàn),需要的朋友可以參考下2016-05-05
iOS App開發(fā)中使用及自定義UITableViewCell的教程
這篇文章主要介紹了iOS App開發(fā)中使用及自定義UITableViewCell的教程,自定義TableViewCell文中使用Objective-C演示而非ib,需要的朋友可以參考下2016-04-04

