From a80d38200e1a20d36aad7829b64749c34f00e41e Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 9 Jun 2014 21:22:58 +0000 Subject: [PATCH] Add pull down refresh and pull up load more function. fix https://github.com/xhzengAIB/MessageDisplayKit/issues/100 --- .../XHDemoWeChatMessageTableViewController.m | 2 +- .../XHMessageRootViewController.h | 4 +- .../XHMessageRootViewController.m | 27 +- .../XHPullRefreshTableViewController.h | 8 +- .../XHPullRefreshTableViewController.m | 25 +- .../Views/RefreshViews/XHLoadMoreView.h | 6 + .../Views/RefreshViews/XHLoadMoreView.m | 43 +++ .../Views/RefreshViews/XHRefreshCircleView.h | 6 + .../Views/RefreshViews/XHRefreshCircleView.m | 2 +- .../Views/RefreshViews/XHRefreshControl.h | 22 +- .../Views/RefreshViews/XHRefreshControl.m | 309 +++++++++++++++++- .../Views/RefreshViews/XHRefreshView.h | 6 + .../Views/RefreshViews/XHRefreshView.m | 41 ++- 13 files changed, 461 insertions(+), 40 deletions(-) diff --git a/Example/MessageDisplayExample/MessageDisplayExample/XHDemoWeChatMessageTableViewController.m b/Example/MessageDisplayExample/MessageDisplayExample/XHDemoWeChatMessageTableViewController.m index 3a28a55..2c241d5 100644 --- a/Example/MessageDisplayExample/MessageDisplayExample/XHDemoWeChatMessageTableViewController.m +++ b/Example/MessageDisplayExample/MessageDisplayExample/XHDemoWeChatMessageTableViewController.m @@ -149,7 +149,7 @@ - (void)viewDidLoad for (NSInteger j = 0; j < 18; j ++) { XHEmotion *emotion = [[XHEmotion alloc] init]; NSString *imageName = [NSString stringWithFormat:@"section%ld_emotion%ld", (long)i , (long)j % 16]; - emotion.emotionPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"emotion%d@2x.gif", j] ofType:@""]; + emotion.emotionPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"emotion%ld@2x.gif", (long)j] ofType:@""]; emotion.emotionConverPhoto = [UIImage imageNamed:imageName]; [emotions addObject:emotion]; } diff --git a/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.h b/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.h index 4706c65..28ffe7e 100644 --- a/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.h +++ b/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.h @@ -7,8 +7,8 @@ // #import -#import "XHBaseTableViewController.h" +#import "XHPullRefreshTableViewController.h" -@interface XHMessageRootViewController : XHBaseTableViewController +@interface XHMessageRootViewController : XHPullRefreshTableViewController @end diff --git a/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.m b/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.m index ca1427b..e8177c2 100644 --- a/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.m +++ b/Example/MessageDisplayExample/MessageDisplayExample/XHMessageRootViewController.m @@ -104,9 +104,7 @@ - (XHPopMenu *)popMenu { return _popMenu; } - -- (void)addContactForGroup -{ +- (void)addContactForGroup { //建立100个测试数据 NSMutableArray *items = [NSMutableArray array]; //网上拉的昵称列表,请忽视非主流。 @@ -188,18 +186,35 @@ - (void)addContactForGroup [self.navigationController presentViewController:navVC animated:YES completion:nil]; } +#pragma mark - DataSource + +- (void)loadDataSource { + self.isDataLoading = YES; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSMutableArray *dataSource = [[NSMutableArray alloc] initWithObjects:@"华捷新闻,点击查看美女新闻呢!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点进入聊天页面,这里有多种显示样式", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", nil]; + sleep(2); + dispatch_async(dispatch_get_main_queue(), ^{ + self.isDataLoading = NO; + self.dataSource = dataSource; + [self.tableView reloadData]; + [self endPullDownRefreshing]; + }); + }); +} #pragma mark - Life Cycle +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + [self startPullDownRefreshing]; +} + - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showMenuOnView:)]; - NSMutableArray *dataSource = [[NSMutableArray alloc] initWithObjects:@"华捷新闻,点击查看美女新闻呢!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点进入聊天页面,这里有多种显示样式", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"请问你现在在哪里啊?我在广州天河", @"点击我查看最新消息,里面有惊喜哦!", @"点击我查看最新消息,里面有惊喜哦!", nil]; - self.dataSource = dataSource; - [self.view addSubview:self.tableView]; } diff --git a/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.h b/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.h index 4b24f10..78ab4bc 100644 --- a/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.h +++ b/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.h @@ -10,8 +10,14 @@ @interface XHPullRefreshTableViewController : XHBaseTableViewController -@property (nonatomic, assign) BOOL isLoading; +@property (nonatomic, assign) BOOL isDataLoading; @property (nonatomic, assign) NSInteger requestCurrentPage; +- (void)startPullDownRefreshing; + +- (void)endPullDownRefreshing; + +- (void)endLoadMoreRefreshing; + @end diff --git a/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.m b/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.m index face41b..1f811d3 100644 --- a/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.m +++ b/MessageDisplayKit/Classes/Controllers/XHBaseTableViewController/XHPullRefreshTableViewController.m @@ -18,6 +18,18 @@ @interface XHPullRefreshTableViewController () @implementation XHPullRefreshTableViewController +- (void)startPullDownRefreshing { + [self.refreshControl startPullDownRefreshing]; +} + +- (void)endPullDownRefreshing { + [self.refreshControl endPullDownRefreshing]; +} + +- (void)endLoadMoreRefreshing { + [self.refreshControl endLoadMoreRefresing]; +} + #pragma mark - Propertys - (XHRefreshControl *)refreshControl { @@ -43,15 +55,26 @@ - (void)didReceiveMemoryWarning { #pragma mark - XHRefreshControl Delegate - (BOOL)isLoading { - return self.isLoading; + return self.isDataLoading; } - (void)beginPullDownRefreshing { self.requestCurrentPage = 0; + [self loadDataSource]; } - (void)beginLoadMoreRefreshing { self.requestCurrentPage ++; + [self loadDataSource]; +} + +- (NSDate *)lastUpdateTime { + return [NSDate date]; +} + +- (BOOL)keepiOS7NewApiCharacter { + BOOL keeped = [[[UIDevice currentDevice] systemVersion] integerValue] >= 7.0; + return keeped; } @end diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.h b/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.h index a6937a6..e058288 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.h +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.h @@ -8,8 +8,14 @@ #import +#define kXHLoadMoreViewHeight 50 + @interface XHLoadMoreView : UIView @property (nonatomic, strong) UIButton *loadMoreButton; +- (void)startLoading; + +- (void)endLoading; + @end diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.m b/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.m index aa86ce6..627c8ca 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.m +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHLoadMoreView.m @@ -10,18 +10,61 @@ @interface XHLoadMoreView () +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView; + @end @implementation XHLoadMoreView +- (void)startLoading { + self.hidden = NO; + [self.loadMoreButton setTitle:@"正在载入" forState:UIControlStateNormal]; + [self.activityIndicatorView startAnimating]; +} + +- (void)endLoading { + self.hidden = YES; + [self.loadMoreButton setTitle:@"显示下20条" forState:UIControlStateNormal]; + [self.activityIndicatorView stopAnimating]; +} + #pragma mark - Propertys +- (UIButton *)loadMoreButton { + if (!_loadMoreButton) { + _loadMoreButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, CGRectGetWidth(self.bounds) - 20, CGRectGetHeight(self.bounds) - 10)]; + _loadMoreButton.titleLabel.font = [UIFont systemFontOfSize:16]; + [_loadMoreButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [_loadMoreButton setBackgroundColor:[UIColor colorWithWhite:0.922 alpha:1.000]]; + // _loadMoreButton.layer.borderColor = [UIColor colorWithWhite:0.867 alpha:1.000].CGColor; + // _loadMoreButton.layer.borderWidth = 1; + // _loadMoreButton.layer.shadowColor = [UIColor colorWithWhite:0.867 alpha:1.000].CGColor; + // _loadMoreButton.layer.shadowOffset = CGSizeMake(1, 1); + // _loadMoreButton.layer.shadowRadius = 1; + // _loadMoreButton.layer.shadowOpacity = 1; + } + return _loadMoreButton; +} + +- (UIActivityIndicatorView *)activityIndicatorView { + if (!_activityIndicatorView) { + _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityIndicatorView.hidesWhenStopped = YES; + _activityIndicatorView.center = CGPointMake(CGRectGetWidth(self.bounds) / 3, CGRectGetHeight(self.bounds) / 2.0); + } + return _activityIndicatorView; +} + #pragma mark - Life Cycle - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code + self.backgroundColor = [UIColor whiteColor]; + [self addSubview:self.loadMoreButton]; + [self addSubview:self.activityIndicatorView]; + [self endLoading]; } return self; } diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.h b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.h index dc8a5ce..748f238 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.h +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.h @@ -8,6 +8,12 @@ #import +//开始画圆圈时的offset +#define kXHRefreshCircleViewHeight 20 + +//圆圈开始旋转时的offset (即开始刷新数据时) +//#define HEIGHT_BEGIN_TO_REFRESH (50 + HEIGHT_BEGIN_TO_DRAW_CIRCLE) + @interface XHRefreshCircleView : UIView //圆圈开始旋转时的offset (即开始刷新数据时) diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.m b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.m index 30fbd60..3779d5c 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.m +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshCircleView.m @@ -47,7 +47,7 @@ - (void)drawRect:(CGRect)rect { CGFloat endAngle = (ABS(_offsetY) / _heightBeginToRefresh) * (M_PI * 19 / 10) + startAngle; CGContextAddArc(context, CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2, radius, startAngle, endAngle, 0); } else { - static CGFloat startAngle = 3 * M_PI / 2; + static CGFloat startAngle = M_PI / 2; CGFloat endAngle = (ABS(_offsetY) / _heightBeginToRefresh) * (M_PI * 19 / 10) + startAngle; CGContextAddArc(context, CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2, radius, startAngle, endAngle, 0); } diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.h b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.h index a35421e..23784c0 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.h +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.h @@ -13,6 +13,7 @@ // 1、是否加载中 // 2、将要开始下拉刷新的方法 // 3、将要开始上提加载更多的方法 +// 4、最后更新数据的时间 // 可选实现的delegate // 1、是否支持下拉刷新 @@ -20,7 +21,6 @@ // 3、标识下拉刷新是UIScrollView的子view,还是UIScrollView父view的子view // 4、是否保留iOS7的新特性,只对滚动视图有效 // 5、多少次上提加载后,启用点击按钮进行加载一页数据 -// 6、需要多长的下拉UIScrollView最大高度限定值 typedef NS_ENUM(NSInteger, XHRefreshViewLayerType) { XHRefreshViewLayerTypeOnScrollView = 0, @@ -47,18 +47,25 @@ typedef NS_ENUM(NSInteger, XHRefreshViewLayerType) { */ - (void)beginLoadMoreRefreshing; +/** + * 4、最后更新数据的时间 + * + * @return 返回缓存最后更新某个页面的时间 + */ +- (NSDate *)lastUpdateTime; + @optional /** * 1、是否支持下拉刷新 * - * @return 如果没有实现该delegate方法,默认是不支持下拉的,为NO + * @return 如果没有实现该delegate方法,默认是支持下拉的,为YES */ - (BOOL)isPullDownRefreshed; /** * 2、是否支持上提加载更多 * - * @return 如果没有实现该delegate方法,默认是不支持上提加载更多的,为NO + * @return 如果没有实现该delegate方法,默认是支持上提加载更多的,为YES */ - (BOOL)isLoadMoreRefreshed; @@ -72,7 +79,7 @@ typedef NS_ENUM(NSInteger, XHRefreshViewLayerType) { /** * 4、UIScrollView的控制器是否保留iOS7新的特性,意思是:tablView的内容是否可以穿透过导航条 * - * @return 如果不是先该delegate方法,默认是支持的 + * @return 如果不是先该delegate方法,默认是不支持的 */ - (BOOL)keepiOS7NewApiCharacter; @@ -83,13 +90,6 @@ typedef NS_ENUM(NSInteger, XHRefreshViewLayerType) { */ - (NSInteger)autoLoadMoreRefreshedCountConverManual; -/** - * 6、需要多长的下拉UIScrollView最大高度限定值 - * - * @return 如果不实现该delegate方法,默认是64个像素高度 - */ -- (CGFloat)pullDownRefreshTotalPixels; - @end @interface XHRefreshControl : NSObject diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.m b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.m index dfa8967..4a4b7cd 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.m +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshControl.m @@ -11,7 +11,20 @@ #import "XHRefreshView.h" #import "XHLoadMoreView.h" -#define kXHDefaultRefreshTotalPixels 64 +#define fequal(a,b) (fabs((a) - (b)) < FLT_EPSILON) +#define fequalzero(a) (fabs(a) < FLT_EPSILON) + +#define kXHDefaultRefreshTotalPixels 60 + +#define kXHAutoLoadMoreRefreshedCount 5 + + +typedef NS_ENUM(NSInteger, XHRefreshState) { + XHRefreshStatePulling = 0, + XHRefreshStateNormal = 1, + XHRefreshStateLoading = 2, + XHRefreshStateStopped = 3, +}; @interface XHRefreshControl () @@ -25,26 +38,162 @@ @interface XHRefreshControl () @property (nonatomic, assign) CGFloat refreshTotalPixels; -@property (nonatomic, weak) UIScrollView *scrollView; +@property (nonatomic, assign) NSInteger autoLoadMoreRefreshedCount; + +@property (nonatomic, readwrite) CGFloat originalTopInset; + +@property (nonatomic, strong) UIScrollView *scrollView; + +@property (nonatomic, assign) XHRefreshState refreshState; + +@property (nonatomic, assign) NSInteger loadMoreRefreshedCount; + +@property (nonatomic, assign) BOOL pullDownRefreshing; + +@property (nonatomic, assign) BOOL loadMoreRefreshing; @end @implementation XHRefreshControl -#pragma mark - Public Method +#pragma mark - Pull Down Refreshing Method - (void)startPullDownRefreshing { + self.pullDownRefreshing = YES; + + NSDate *date = [self.delegate lastUpdateTime]; + if (date || [date isKindOfClass:[NSDate class]]) { + self.refreshView.timeLabel.text = [NSString stringWithFormat:@"上次刷新:%@", @"10小时前"]; + } + + self.refreshState = XHRefreshStatePulling; + + self.refreshState = XHRefreshStateLoading; +} + +- (void)animationRefreshCircleView { + if (self.refreshView.refreshCircleView.offsetY != kXHDefaultRefreshTotalPixels - kXHRefreshCircleViewHeight) { + self.refreshView.refreshCircleView.offsetY = kXHDefaultRefreshTotalPixels - kXHRefreshCircleViewHeight; + [self.refreshView.refreshCircleView setNeedsDisplay]; + } + // 先去除所有动画 + [self.refreshView.refreshCircleView.layer removeAllAnimations]; + // 添加旋转的动画 + [self.refreshView.refreshCircleView.layer addAnimation:[XHRefreshCircleView repeatRotateAnimation] forKey:@"rotateAnimation"]; + [self callBeginPullDownRefreshing]; +} + +- (void)callBeginPullDownRefreshing { + [self.delegate beginPullDownRefreshing]; } - (void)endPullDownRefreshing { + self.pullDownRefreshing = NO; + self.refreshState = XHRefreshStateStopped; + [self resetScrollViewContentInset]; +} + +#pragma mark - Load More Refreshing Method + +- (void)startLoadMoreRefreshing { + NSLog(@"进入上提多少次"); + // if (self.loadMoreRefreshedCount < self.autoLoadMoreRefreshedCount) { + [self callBeginLoadMoreRefreshing]; + // } +} + +- (void)callBeginLoadMoreRefreshing { + if (self.loadMoreRefreshing) + return; + self.loadMoreRefreshing = YES; + self.loadMoreRefreshedCount ++; + self.refreshState = XHRefreshStateLoading; + [self.loadMoreView startLoading]; + [self.delegate beginLoadMoreRefreshing]; } - (void)endLoadMoreRefresing { - + self.loadMoreRefreshing = NO; + self.refreshState = XHRefreshStateNormal; + UIEdgeInsets contentInset = self.scrollView.contentInset; + contentInset.bottom -= CGRectGetHeight(self.loadMoreView.bounds); + self.scrollView.contentInset = contentInset; + [self.loadMoreView endLoading]; } +- (void)loadMoreButtonClciked:(UIButton *)sender { + [self callBeginLoadMoreRefreshing]; +} + +#pragma mark - Scroll View + +- (void)resetScrollViewContentInset { + UIEdgeInsets contentInset = self.scrollView.contentInset; + contentInset.top = self.originalTopInset; + [UIView animateWithDuration:0.3f animations:^{ + [self.scrollView setContentInset:contentInset]; + } completion:^(BOOL finished) { + + self.refreshState = XHRefreshStateNormal; + + if (self.refreshView.refreshCircleView) { + [self.refreshView.refreshCircleView.layer removeAllAnimations]; + } + }]; +} + +- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset { + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState + animations:^{ + self.scrollView.contentInset = contentInset; + } + completion:^(BOOL finished) { + if (self.refreshState == XHRefreshStateStopped) { + self.refreshState = XHRefreshStateNormal; + + if (self.refreshView.refreshCircleView) { + [self.refreshView.refreshCircleView.layer removeAllAnimations]; + } + } + }]; +} + +- (void)setScrollViewContentInsetForLoading { + UIEdgeInsets currentInsets = self.scrollView.contentInset; + currentInsets.top = self.refreshTotalPixels; + [self setScrollViewContentInset:currentInsets]; +} + +- (void)setScrollViewContentInsetForLoadMore { + if (self.pullDownRefreshing) + return; + UIEdgeInsets currentInsets = self.scrollView.contentInset; + currentInsets.bottom = kXHLoadMoreViewHeight; + [self setScrollViewContentInset:currentInsets]; +} + +#pragma mark - Propertys + +- (XHRefreshView *)refreshView { + if (!_refreshView) { + _refreshView = [[XHRefreshView alloc] initWithFrame:CGRectMake(0, -kXHDefaultRefreshTotalPixels, CGRectGetWidth([[UIScreen mainScreen] bounds]), kXHDefaultRefreshTotalPixels)]; + _refreshView.refreshCircleView.heightBeginToRefresh = kXHDefaultRefreshTotalPixels - kXHRefreshCircleViewHeight; + _refreshView.refreshCircleView.offsetY = 0; + } + return _refreshView; +} + +- (XHLoadMoreView *)loadMoreView { + if (!_loadMoreView) { + _loadMoreView = [[XHLoadMoreView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([[UIScreen mainScreen] bounds]), kXHLoadMoreViewHeight)]; + [_loadMoreView.loadMoreButton addTarget:self action:@selector(loadMoreButtonClciked:) forControlEvents:UIControlEventTouchUpInside]; + } + return _loadMoreView; +} #pragma mark - Getter Method @@ -56,28 +205,89 @@ - (BOOL)isPullDownRefreshed { if ([self.delegate respondsToSelector:@selector(isPullDownRefreshed)]) { return [self.delegate isPullDownRefreshed]; } - return NO; + return YES; } - (BOOL)isLoadMoreRefreshed { if ([self.delegate respondsToSelector:@selector(isLoadMoreRefreshed)]) { return [self.delegate isLoadMoreRefreshed]; } - return NO; + return YES; } - (CGFloat)refreshTotalPixels { - if ([self.delegate respondsToSelector:@selector(pullDownRefreshTotalPixels)]) { - return [self.delegate pullDownRefreshTotalPixels]; + return kXHDefaultRefreshTotalPixels + [self getAdaptorHeight]; +} + +- (CGFloat)getAdaptorHeight { + if ([self.delegate respondsToSelector:@selector(keepiOS7NewApiCharacter)]) { + return ([self.delegate keepiOS7NewApiCharacter] ? 64 : 0); + } else { + return 0; + } +} + +- (NSInteger)autoLoadMoreRefreshedCount { + if ([self.delegate respondsToSelector:@selector(autoLoadMoreRefreshedCountConverManual)]) { + return [self.delegate autoLoadMoreRefreshedCountConverManual]; + } + return kXHAutoLoadMoreRefreshedCount; +} + +#pragma mark - Setter Method + +- (void)setRefreshState:(XHRefreshState)refreshState { + switch (refreshState) { + case XHRefreshStateStopped: + case XHRefreshStateNormal: { + self.refreshView.stateLabel.text = @"下拉刷新"; + break; + } + case XHRefreshStateLoading: { + + if (self.pullDownRefreshing) { + self.refreshView.stateLabel.text = @"正在加载"; + [self setScrollViewContentInsetForLoading]; + + if(_refreshState == XHRefreshStatePulling) { + [self animationRefreshCircleView]; + } + } + break; + } + case XHRefreshStatePulling: + self.refreshView.stateLabel.text = @"释放立即刷新"; + break; + default: + break; } - return kXHDefaultRefreshTotalPixels; + + _refreshState = refreshState; } #pragma mark - Life Cycle +- (void)configuraObserverWithScrollView:(UIScrollView *)scrollView { + [scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; + [scrollView addObserver:self forKeyPath:@"contentInset" options:NSKeyValueObservingOptionNew context:nil]; + [scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil]; +} + +- (void)removeObserverWithScrollView:(UIScrollView *)scrollView { + [scrollView removeObserver:self forKeyPath:@"contentOffset" context:nil]; + [scrollView removeObserver:self forKeyPath:@"contentInset" context:nil]; + [scrollView removeObserver:self forKeyPath:@"contentSize" context:nil]; +} + - (void)setup { + self.refreshState = XHRefreshStateNormal; + + [self configuraObserverWithScrollView:self.scrollView]; + [self.scrollView addSubview:self.refreshView]; + [self.scrollView addSubview:self.loadMoreView]; + self.originalTopInset = self.scrollView.contentInset.top; } - (id)initWithScrollView:(UIScrollView *)scrollView { @@ -89,4 +299,85 @@ - (id)initWithScrollView:(UIScrollView *)scrollView { return self; } +- (void)dealloc { + self.delegate = nil; + [self removeObserverWithScrollView:self.scrollView]; + self.scrollView = nil; + + self.refreshView = nil; + + self.loadMoreView = nil; +} + +#pragma mark - KVO + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqualToString:@"contentOffset"]) { + CGPoint contentOffset = [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]; + + + // 上提加载更多的逻辑方法 + int currentPostion = contentOffset.y; + + if (currentPostion > 0) { + + CGRect bounds = self.scrollView.bounds;//边界 + + CGSize size = self.scrollView.contentSize;//滚动视图内容区域size + + UIEdgeInsets inset = self.scrollView.contentInset;//视图周围额外的滚动视图区域 + + float y = currentPostion + bounds.size.height + inset. bottom; + + //判断是否滚动到底部 + if((y - size.height) > kXHLoadMoreViewHeight && self.refreshState != XHRefreshStateLoading && self.isLoadMoreRefreshed && !self.loadMoreRefreshing) { + [self startLoadMoreRefreshing]; + } + } + + if (!self.loadMoreRefreshing) { + // 下拉刷新的逻辑方法 + if(self.refreshState != XHRefreshStateLoading) { + // 如果不是加载状态的时候 + + if (ABS(self.scrollView.contentOffset.y + [self getAdaptorHeight]) >= kXHRefreshCircleViewHeight) { + self.refreshView.refreshCircleView.offsetY = MIN(ABS(self.scrollView.contentOffset.y + [self getAdaptorHeight]), kXHDefaultRefreshTotalPixels) - kXHRefreshCircleViewHeight; + [self.refreshView.refreshCircleView setNeedsDisplay]; + } + + CGFloat scrollOffsetThreshold; + scrollOffsetThreshold = -(kXHDefaultRefreshTotalPixels + self.originalTopInset); + + if(!self.scrollView.isDragging && self.refreshState == XHRefreshStatePulling) { + self.pullDownRefreshing = YES; + self.refreshState = XHRefreshStateLoading; + } else if(contentOffset.y < scrollOffsetThreshold && self.scrollView.isDragging && self.refreshState == XHRefreshStateStopped) { + self.refreshState = XHRefreshStatePulling; + } else if(contentOffset.y >= scrollOffsetThreshold && self.refreshState != XHRefreshStateStopped) { + self.refreshState = XHRefreshStateStopped; + } + } else { + CGFloat offset; + UIEdgeInsets contentInset; + offset = MAX(self.scrollView.contentOffset.y * -1, 0.0f); + offset = MIN(offset, self.refreshTotalPixels); + contentInset = self.scrollView.contentInset; + self.scrollView.contentInset = UIEdgeInsetsMake(offset, contentInset.left, contentInset.bottom, contentInset.right); + } + } + } else if ([keyPath isEqualToString:@"contentInset"]) { + UIEdgeInsets contentInset = [[change valueForKey:NSKeyValueChangeNewKey] UIEdgeInsetsValue]; + NSLog(@"contentInset : %@", NSStringFromUIEdgeInsets(contentInset)); + } else if ([keyPath isEqualToString:@"contentSize"]) { + CGSize contentSize = [[change valueForKey:NSKeyValueChangeNewKey] CGSizeValue]; + NSLog(@"contentSize : %@", NSStringFromCGSize(contentSize)); + if (contentSize.height > CGRectGetHeight(self.scrollView.frame)) { + CGRect loadMoreViewFrame = self.loadMoreView.frame; + loadMoreViewFrame.origin.y = contentSize.height; + self.loadMoreView.frame = loadMoreViewFrame; + [self setScrollViewContentInsetForLoadMore]; + } + } +} + @end diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.h b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.h index 8038344..3c625b6 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.h +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.h @@ -8,8 +8,14 @@ #import +#import "XHRefreshCircleView.h" @interface XHRefreshView : UIView +@property (nonatomic, strong) XHRefreshCircleView *refreshCircleView; + +@property (nonatomic, strong) UILabel *stateLabel; + +@property (nonatomic, strong) UILabel *timeLabel; @end diff --git a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.m b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.m index d90b84c..101b258 100644 --- a/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.m +++ b/MessageDisplayKit/Classes/Views/RefreshViews/XHRefreshView.m @@ -8,28 +8,53 @@ #import "XHRefreshView.h" -#import "XHRefreshCircleView.h" - @interface XHRefreshView () -@property (nonatomic, strong) XHRefreshCircleView *refreshCircleView; - -@property (nonatomic, strong) UILabel *stateLabel; - -@property (nonatomic, strong) UILabel *timeLabel; - @end @implementation XHRefreshView #pragma mark - Propertys +- (XHRefreshCircleView *)refreshCircleView { + if (!_refreshCircleView) { + _refreshCircleView = [[XHRefreshCircleView alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.bounds) - kXHRefreshCircleViewHeight) / 2 - 40, (CGRectGetHeight(self.bounds) - kXHRefreshCircleViewHeight) / 2 - 5, kXHRefreshCircleViewHeight, kXHRefreshCircleViewHeight)]; + } + return _refreshCircleView; +} + +- (UILabel *)stateLabel { + if (!_stateLabel) { + _stateLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.refreshCircleView.frame) + 5, CGRectGetMinY(self.refreshCircleView.frame), 160, 14)]; + _stateLabel.backgroundColor = [UIColor clearColor]; + _stateLabel.font = [UIFont systemFontOfSize:14.f]; + _stateLabel.textColor = [UIColor blackColor]; + } + return _stateLabel; +} + +- (UILabel *)timeLabel { + if (!_timeLabel) { + CGRect timeLabelFrame = self.stateLabel.frame; + timeLabelFrame.origin.y += CGRectGetHeight(timeLabelFrame) + 6; + _timeLabel = [[UILabel alloc] initWithFrame:timeLabelFrame]; + _timeLabel.backgroundColor = [UIColor clearColor]; + _timeLabel.font = [UIFont systemFontOfSize:11.f]; + _timeLabel.textColor = [UIColor colorWithWhite:0.659 alpha:1.000]; + } + return _timeLabel; +} + #pragma mark - Life Cycle - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code + self.backgroundColor = [UIColor whiteColor]; + [self addSubview:self.refreshCircleView]; + [self addSubview:self.stateLabel]; + [self addSubview:self.timeLabel]; } return self; }