UITableView下拉刷新的实现原理是自定义的下拉刷新控件KVO监听UITableView(UIScrollView)的 contentOffset 属性。
#import <UIKit/UIKit.h>
typedef void (^RefreshHeaderBlock)(void);
@interface RefreshHeader : UIView
- (instancetype)initWithTarget:(id)target beginRefreshBlock:(RefreshHeaderBlock)refreshHeaderBlock;
- (void)beginRefreshing;
- (void)endRefreshing;
@end
把UITableView(UIScrollView)传进来作为目标,添加监听
@implementation RefreshHeader
- (instancetype)initWithTarget:(id)target beginRefreshBlock:(RefreshHeaderBlock)refreshHeaderBlock{
self = [super init];
if (self) {
self.frame = CGRectMake(0, 0, 0, HeaderHeight);
_refreshHeaderBlock = refreshHeaderBlock;
_scrollView = (UIScrollView *)target;
[_scrollView addSubview:self];
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[_scrollView addObserver:self forKeyPath:RefreshHeaderKeyPathContentOffset options:option context:nil];
}
return self;
}
- (void)layoutSubviews{
[super layoutSubviews];
CGFloat superWidth = self.scrollView.frame.size.width;
self.frame = CGRectMake(0, - HeaderHeight, superWidth, HeaderHeight);
......
}
#pragma mark public
- (void)beginRefreshing{
if (!_isRefresh) {
_isRefresh = YES;
//设置偏移量,衔接加载的更多数据
[UIView animateWithDuration:0.3 animations:^{
//刷新控件停留
[_scrollView setContentInset:UIEdgeInsetsMake(HeaderHeight, 0, 0, 0)];
[self.activityView startAnimating];
self.arrowView.hidden = YES;
self.stateLable.text = @"加载中";
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_refreshHeaderBlock();
});
}
}
- (void)endRefreshing{
_isRefresh = NO;
[UIView animateWithDuration:0.3 animations:^{
[self.activityView stopAnimating];
self.arrowView.hidden = YES;
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI*2);
//还原scrollView
[self.scrollView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}];
}
#pragma mark KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:RefreshHeaderKeyPathContentOffset]) {
[self scrollViewContentOffsetDidChange:change];
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark logicol
//判断contentOffset<0代表手势为往下拖动,根据isDragging属性判断是否松开,如果松开且偏移量大于(HeaderHeight),启动刷新动作
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{
self.stateLable.hidden = NO;
CGFloat offset = self.scrollView.contentOffset.y;
if (offset < 0) {
//正在拖拽
if(self.scrollView.isDragging){
//还原scrollView
[self.scrollView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[UIView animateWithDuration:0.3 animations:^{
self.arrowView.hidden = NO;
if (offset < -HeaderHeight*1.5) {
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI);
self.stateLable.text = @"释放更新";
}else{
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI*2);
self.stateLable.text = @"载入更多";
}
}];
return;
}
//松开且偏移量大于头部高度,启动刷新
if (offset < -HeaderHeight) {
[self beginRefreshing];
}
}
}
-(void)dealloc{
[self.scrollView removeObserver:self forKeyPath:RefreshHeaderKeyPathContentOffset];
}