123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- //
- // RefreshControl.m
- //
- // Copyright (c) 2014 YDJ ( https://github.com/ydj/RefreshControl )
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "FKRefreshControl.h"
- #import "FKRefreshBottomView.h"
- #import "FKRefreshViewDelegate.h"
- #import "FKRefreshTopView.h"
- @interface FKRefreshControl ()
- @property (nonatomic,weak)id<RefreshControlDelegate>delegate;
- @property (nonatomic,strong)UIView <FKRefreshViewDelegate> * topView;
- @property (nonatomic,strong)UIView <FKRefreshViewDelegate> * bottomView;
- @property (nonatomic,copy)NSString * topClass;
- @property (nonatomic,copy)NSString * bottomClass;
- @end
- @implementation FKRefreshControl
- - (void)registerClassForTopView:(Class)topClass
- {
- if ([topClass conformsToProtocol:@protocol(FKRefreshViewDelegate)]) {
- self.topClass=NSStringFromClass([topClass class]);
- }
- else{
- self.topClass=NSStringFromClass([FKRefreshTopView class]);
- }
- }
- - (void)registerClassForBottomView:(Class)bottomClass
- {
- if ([bottomClass conformsToProtocol:@protocol(FKRefreshViewDelegate)]) {
- self.bottomClass=NSStringFromClass([bottomClass class]);
- }
- else{
- self.bottomClass=NSStringFromClass([FKRefreshBottomView class]);
- }
-
-
- }
- - (instancetype)initWithScrollView:(UIScrollView *)scrollView delegate:(id<RefreshControlDelegate>)delegate
- {
- self=[super init];
- if (self)
- {
- _scrollView=scrollView;
- _delegate=delegate;
-
- _topClass=NSStringFromClass([FKRefreshTopView class]);
- _bottomClass=NSStringFromClass([FKRefreshBottomView class]);
-
- self.enableInsetTop=65.0;
- self.enableInsetBottom=65.0;
- self.enableTopEngage = YES;
- self.enableBottomEngage = YES;
- self.bottomRefreshOffsetY = 0;
-
- [_scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
- [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionPrior context:NULL];
-
-
-
- }
-
- return self;
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
- if([keyPath isEqual:@"contentSize"])
- {
- if (self.topEnabled)
- {
- [self initTopView];
- }
-
- if (self.bottomEnabled)
- {
- [self initBottonView];
- }
- }
- else if([keyPath isEqualToString:@"contentOffset"])
- {
- if (_refreshingDirection==RefreshingDirectionNone) {
- [self _drogForChange:change];
- }
- }
-
-
- }
- - (void)_drogForChange:(NSDictionary *)change
- {
-
- if ( self.topEnabled && self.scrollView.contentOffset.y<0)
- {
- if(self.scrollView.contentOffset.y<-self.enableInsetTop)
- {
- if (self.autoRefreshTop || ( self.scrollView.decelerating && self.scrollView.dragging==NO)) {
- [self _engageRefreshDirection:RefreshDirectionTop];
- }
- else {
- [self _canEngageRefreshDirection:RefreshDirectionTop];
- }
- }
- else
- {
- [self _didDisengageRefreshDirection:RefreshDirectionTop];
- }
- }
-
- if ( self.bottomEnabled && self.scrollView.contentOffset.y>0 )
- {
-
- if(self.scrollView.contentOffset.y>(self.scrollView.contentSize.height+self.enableInsetBottom-self.scrollView.bounds.size.height) )
- {
- if(self.autoRefreshBottom || (self.scrollView.decelerating && self.scrollView.dragging==NO)){
- [self _engageRefreshDirection:RefreshDirectionBottom];
- }
- else{
- [self _canEngageRefreshDirection:RefreshDirectionBottom];
- }
- }
- else {
- [self _didDisengageRefreshDirection:RefreshDirectionBottom];
- }
-
- }
-
-
-
- }
- - (void)_canEngageRefreshDirection:(RefreshDirection) direction
- {
-
-
- if (direction==RefreshDirectionTop)
- {
- if ([self.topView respondsToSelector:@selector(canEngageRefreshWithOffsetY:)]) {
- [self.topView canEngageRefreshWithOffsetY:self.scrollView.contentOffset.y];
- }
- }
- else if (direction==RefreshDirectionBottom)
- {
- if ([self.bottomView respondsToSelector:@selector(canEngageRefreshWithOffsetY:)]){
- [self.bottomView canEngageRefreshWithOffsetY:self.scrollView.contentOffset.y];
- }
- }
- }
- - (void)_didDisengageRefreshDirection:(RefreshDirection) direction
- {
-
- if (direction==RefreshDirectionTop)
- {
- if ([self.topView respondsToSelector:@selector(didDisengageRefreshWithOffsetY:)]){
- [self.topView didDisengageRefreshWithOffsetY:self.scrollView.contentOffset.y];
- }
- }
- else if (direction==RefreshDirectionBottom)
- {
- if ([self.bottomView respondsToSelector:@selector(didDisengageRefreshWithOffsetY:)]){
- [self.bottomView didDisengageRefreshWithOffsetY:self.scrollView.contentOffset.y];
- }
- }
- }
- - (void)_engageRefreshDirection:(RefreshDirection) direction
- {
-
- UIEdgeInsets edge = UIEdgeInsetsZero;
-
- if (direction==RefreshDirectionTop)
- {
- _refreshingDirection=RefreshingDirectionTop;
- float topH=self.enableInsetTop<45?45:self.enableInsetTop;
- edge=UIEdgeInsetsMake(topH, 0, 0, 0);///enableInsetTop
-
- if (_enableTopEngage) _scrollView.contentInset=edge;
-
- }
- else if (direction==RefreshDirectionBottom)
- {
- float botomH=self.enableInsetBottom<50?50:self.enableInsetBottom;
- edge=UIEdgeInsetsMake(0, 0, botomH, 0);///self.enableInsetBottom
- _refreshingDirection=RefreshingDirectionBottom;
-
- if (_enableBottomEngage) _scrollView.contentInset=edge;
- }
-
- // _scrollView.contentInset=edge;
-
- [self _didEngageRefreshDirection:direction];
-
- }
- - (void)_didEngageRefreshDirection:(RefreshDirection) direction
- {
-
- if (direction==RefreshDirectionTop)
- {
- [self.topView performSelector:@selector(startRefreshing)];
- //[self.topView startRefreshing];
- }
- else if (direction==RefreshDirectionBottom)
- {
- [self.bottomView performSelector:@selector(startRefreshing)];
- // [self.bottomView startRefreshing];
- }
-
- if ([self.delegate respondsToSelector:@selector(refreshControl:didEngageRefreshDirection:)])
- {
- [self.delegate refreshControl:self didEngageRefreshDirection:direction];
- }
-
-
- }
- - (void)_startRefreshingDirection:(RefreshDirection)direction animation:(BOOL)animation
- {
- CGPoint point =CGPointZero;
-
- if (direction==RefreshDirectionTop)
- {
- float topH=self.enableInsetTop<45?45:self.enableInsetTop;
- point=CGPointMake(0, -topH);//enableInsetTop
- }
- else if (direction==RefreshDirectionBottom)
- {
- float height=MAX(self.scrollView.contentSize.height, self.scrollView.frame.size.height);
- float bottomH=self.enableInsetBottom<45?45:self.enableInsetBottom;
- point=CGPointMake(0, height-self.scrollView.bounds.size.height+bottomH);///enableInsetBottom
- }
- __weak typeof(self)weakSelf=self;
-
- [_scrollView setContentOffset:point animated:YES];
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- __strong typeof(self)strongSelf=weakSelf;
- [strongSelf _engageRefreshDirection:direction];
- });
-
-
- }
- - (void)_finishRefreshingDirection1:(RefreshDirection)direction animation:(BOOL)animation
- {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [UIView animateWithDuration:0.25 animations:^{
-
- _scrollView.contentInset=UIEdgeInsetsZero;
-
- } completion:^(BOOL finished) {
-
- }];
-
- _refreshingDirection=RefreshingDirectionNone;
-
- if (direction==RefreshDirectionTop)
- {
- [self.topView performSelector:@selector(finishRefreshing)];
- //[self.topView finishRefreshing];
- }
- else if(direction==RefreshDirectionBottom)
- {
- [self.bottomView performSelector:@selector(finishRefreshing)];
- //[self.bottomView finishRefreshing];
- }
- });
- }
- - (void)dealloc
- {
- [_scrollView removeObserver:self forKeyPath:@"contentSize"];
- [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
- }
- - (void)initTopView
- {
-
- if (!CGRectIsEmpty(self.scrollView.frame))
- {
- float topOffsetY=self.enableInsetTop+45*6;
-
- if (self.topView==nil)
- {
- Class className=NSClassFromString(self.topClass);
-
- _topView=[[className alloc] initWithFrame:CGRectMake(0, -topOffsetY, self.scrollView.frame.size.width, topOffsetY)];
- _topView.backgroundColor = UIColorFromRGB(0xf4f4f4);
- [self.scrollView addSubview:self.topView];
- [self.topView resetLayoutSubViews];
-
- }
- else{
- _topView.frame=CGRectMake(0, -topOffsetY, self.scrollView.frame.size.width, topOffsetY);
-
- [_topView performSelector:@selector(resetLayoutSubViews)];
- //[_topView resetLayoutSubViews];
- }
-
- }
-
- }
- - (void)initBottonView
- {
-
-
- if (!CGRectIsNull(self.scrollView.frame))
- {
- float y=MAX(self.scrollView.bounds.size.height, self.scrollView.contentSize.height);
- if (self.bottomView==nil)
- {
- Class className=NSClassFromString(self.bottomClass);
-
- _bottomView=[[className alloc] initWithFrame:CGRectMake(0,y + self.bottomRefreshOffsetY, self.scrollView.bounds.size.width, self.enableInsetBottom+45)];
- [self.scrollView addSubview:_bottomView];
- }
- else{
- _bottomView.frame=CGRectMake(0,y + self.bottomRefreshOffsetY, self.scrollView.bounds.size.width, self.enableInsetBottom+45*6);
-
- [self.bottomView performSelector:@selector(resetLayoutSubViews)];
- // [self.bottomView resetLayoutSubViews];
- }
-
- }
-
-
- }
- - (void)setTopEnabled:(BOOL)topEnabled
- {
- _topEnabled=topEnabled;
-
- if (_topEnabled)
- {
- if (self.topView==nil)
- {
- [self initTopView];
- }
-
- }
- else{
- [self.topView removeFromSuperview];
- self.topView=nil;
- }
-
- }
- - (void)setBottomEnabled:(BOOL)bottomEnabled
- {
- _bottomEnabled=bottomEnabled;
-
- if (_bottomEnabled)
- {
- if (_bottomView==nil)
- {
- [self initBottonView];
- }
- }
- else{
- [_bottomView removeFromSuperview];
- _bottomView=nil;
- }
-
- }
- - (void)startRefreshingDirection:(RefreshDirection)direction
- {
-
- [self _startRefreshingDirection:direction animation:YES];
-
- }
- - (void)finishRefreshingDirection:(RefreshDirection)direction
- {
-
- [self _finishRefreshingDirection1:direction animation:YES];
-
-
- }
- @end
|