效果:
源码:
// // SRSlimeView // @author SR // Modified by JunHan on 13-9-18. // #import <UIKit/UIKit.h> #define kStartTo 0.7f #define kEndTo 0.15f #define kAnimationInterval (1.0f / 50.0f) NS_INLINE CGFloat distansBetween(CGPoint p1 , CGPoint p2) { return sqrtf((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y)); } typedef enum { SRSlimeStateNormal, SRSlimeStateShortening, SRSlimeStateMiss } SRSlimeState; @class SRSlimeView; @interface SRSlimeView : UIView @property (nonatomic, assign) CGPoint startPoint, toPoint; @property (nonatomic, assign) CGFloat viscous; //default 55 @property (nonatomic, assign) CGFloat radius; //default 13 @property (nonatomic, assign) CGFloat lineWith; @property (nonatomic, assign) CGFloat shadowBlur; @property (nonatomic, strong) UIColor *bodyColor, *skinColor; @property (nonatomic, strong) UIColor *shadowColor; @property (nonatomic, assign) SRSlimeState state; - (void)setPullApartTarget:(id)target action:(SEL)action; @end
SRSlimeView.h
// // SRSlimeView // @author SR // Modified by JunHan on 13-9-18. // #import "SRSlimeView.h" #import <QuartzCore/QuartzCore.h> NS_INLINE CGPoint pointLineToArc(CGPoint center, CGPoint p2, float angle, CGFloat radius) { float angleS = atan2f(p2.y - center.y, p2.x - center.x); float angleT = angleS + angle; float x = radius * cosf(angleT); float y = radius * sinf(angleT); return CGPointMake(x + center.x, y + center.y); } @implementation SRSlimeView { __weak id _target; SEL _action; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor clearColor]; _toPoint = _startPoint = CGPointMake(frame.size.width / 2, frame.size.height / 2); _viscous = 45.0f; // 拉扯长度 _radius = 17.0f; // 拉扯物体半径 _bodyColor = [[UIColor grayColor] colorWithAlphaComponent:0.45f]; _skinColor = [[UIColor blackColor] colorWithAlphaComponent:0.12f]; _lineWith = 1.0; _shadowBlur = 4; _shadowColor = [[UIColor grayColor] colorWithAlphaComponent:0.7f]; } return self; } - (void)setLineWith:(CGFloat)lineWith { _lineWith = lineWith; [self setNeedsDisplay]; } - (void)setStartPoint:(CGPoint)startPoint { if (CGPointEqualToPoint(_startPoint, startPoint)) return; if (_state == SRSlimeStateNormal) { _startPoint = startPoint; [self setNeedsDisplay]; } } - (void)setToPoint:(CGPoint)toPoint { if (CGPointEqualToPoint(_toPoint, toPoint)) return; if (_state == SRSlimeStateNormal) { _toPoint = toPoint; [self setNeedsDisplay]; } } - (UIBezierPath*)bodyPath:(CGFloat)startRadius end:(CGFloat)endRadius percent:(float)percent { UIBezierPath *path = [[UIBezierPath alloc] init]; float angle1 = M_PI/3 + (M_PI/6) * percent; CGPoint sp1 = pointLineToArc(_startPoint, _toPoint, angle1, startRadius), sp2 = pointLineToArc(_startPoint, _toPoint, -angle1, startRadius), ep1 = pointLineToArc(_toPoint, _startPoint, M_PI/2, endRadius), ep2 = pointLineToArc(_toPoint, _startPoint, -M_PI/2, endRadius); #define kMiddleP 0.6 CGPoint mp1 = CGPointMake(sp2.x*kMiddleP + ep1.x*(1-kMiddleP), sp2.y*kMiddleP + ep1.y*(1-kMiddleP)), mp2 = CGPointMake(sp1.x*kMiddleP + ep2.x*(1-kMiddleP), sp1.y*kMiddleP + ep2.y*(1-kMiddleP)), mm = CGPointMake((mp1.x + mp2.x)/2, (mp1.y + mp2.y)/2); float p = distansBetween(mp1, mp2) / 2 / endRadius * (0.9 + percent/10); mp1 = CGPointMake((mp1.x - mm.x)/p + mm.x, (mp1.y - mm.y)/p + mm.y); mp2 = CGPointMake((mp2.x - mm.x)/p + mm.x, (mp2.y - mm.y)/p + mm.y); [path moveToPoint:sp1]; float angleS = atan2f(_toPoint.y - _startPoint.y, _toPoint.x - _startPoint.x); [path addArcWithCenter:_startPoint radius:startRadius startAngle:angleS + angle1 endAngle:angleS + M_PI*2 - angle1 clockwise:YES]; [path addQuadCurveToPoint:ep1 controlPoint:mp1]; angleS = atan2f(_startPoint.y - _toPoint.y, _startPoint.x - _toPoint.x); [path addArcWithCenter:_toPoint radius:endRadius startAngle:angleS + M_PI/2 endAngle:angleS + M_PI*3/2 clockwise:YES]; [path addQuadCurveToPoint:sp1 controlPoint:mp2]; return path; } - (void)drawRect:(CGRect)rect { switch (_state) { case SRSlimeStateNormal: { float percent = 1 - distansBetween(_startPoint , _toPoint) / _viscous; if (percent == 1) { CGContextRef context = UIGraphicsGetCurrentContext(); UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(_startPoint.x - _radius, _startPoint.y - _radius, 2*_radius, 2*_radius) cornerRadius:_radius]; [self setContext:context path:path]; CGContextDrawPath(context, kCGPathFillStroke); }else { CGFloat startRadius = _radius * (kStartTo + (1 - kStartTo) * percent); CGFloat endRadius = _radius * (kEndTo + (1 - kEndTo) * percent); CGContextRef context = UIGraphicsGetCurrentContext(); // build curve path UIBezierPath *path = [self bodyPath:startRadius end:endRadius percent:percent]; [self setContext:context path:path]; CGContextDrawPath(context, kCGPathFillStroke); if (percent <= 0) { _state = SRSlimeStateShortening; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [_target performSelector:_action withObject:self]; #pragma clang diagnostic pop [self performSelector:@selector(scaling) withObject:nil afterDelay:kAnimationInterval inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; } } } break; case SRSlimeStateShortening: { // back shortneing _toPoint = CGPointMake((_toPoint.x - _startPoint.x) * 0.8 + _startPoint.x, (_toPoint.y - _startPoint.y) * 0.8 + _startPoint.y); float p = distansBetween(_startPoint, _toPoint) / _viscous; float percent = 1 - p; float r = _radius * p; if (p > 0.01) { CGFloat startRadius = r * (kStartTo + (1 - kStartTo) * percent); CGContextRef context = UIGraphicsGetCurrentContext(); CGFloat endRadius = r * (kEndTo + (1 - kEndTo) * percent) * (1 + percent / 2); UIBezierPath *path = [self bodyPath:startRadius end:endRadius percent:percent]; [self setContext:context path:path]; CGContextDrawPath(context, kCGPathFillStroke); }else { self.hidden = YES; _state = SRSlimeStateMiss; } } break; default: break; } } - (void)setContext:(CGContextRef)context path:(UIBezierPath *)path { if (self.shadowColor) { CGContextSetShadowWithColor(context, CGSizeZero, self.shadowBlur, self.shadowColor.CGColor); } CGContextSetFillColorWithColor(context, self.bodyColor.CGColor); CGContextSetLineWidth(context, self.lineWith); CGContextSetStrokeColorWithColor(context, self.skinColor.CGColor); CGContextAddPath(context, path.CGPath); } - (void)scaling { if (_state == SRSlimeStateShortening) { [self setNeedsDisplay]; [self performSelector:@selector(scaling) withObject:nil afterDelay:kAnimationInterval inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; } } - (void)setPullApartTarget:(id)target action:(SEL)action { _target = target; _action = action; } - (void)setHidden:(BOOL)hidden { [super setHidden:hidden]; if (!hidden) { self.layer.transform = CATransform3DIdentity; [self setNeedsDisplay]; } } - (void)setState:(SRSlimeState)state { _state = state; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(scaling) object:nil]; } @end
SRSlimeView.m
/** * WaterRefreshLoadMoreView.h * A refresh view looks like UIRefreshControl and have loadMore function * (水滴下拉刷新,上拉加载) * * 使用方法:(可设置type类型,默认是下拉上拉都有) * 1. 直接initWithType创建,然后将它的scrollView赋值,别忘了在使用它的ScrollView 或者 VC的dealloc * 中调用这个 deallocWithCloseConnect 方法 * * Created by 覃俊涵 on 13-9-18. All rights reserved. **/ #import <UIKit/UIKit.h> #import "SRSlimeView.h" typedef enum{ WaterRefreshTypeRefreshAndLoadMore,//含下拉上拉(default) WaterRefreshTypeOnlyRefresh, //仅下拉刷新 WaterRefreshTypeOnlyLoadMore, //仅加载更多 WaterRefreshTypeNone }WaterViewType; @protocol WaterRefreshDelegate; @protocol WaterLoadMoreDelegate; @interface WaterRefreshLoadMoreView : UIView @property (nonatomic, weak) UIScrollView *scrollView; @property (nonatomic, assign) WaterViewType type; // 水下拉的view @property (nonatomic, weak) UIView *waterRefreshView; // 上拉的view @property (nonatomic, strong) UIView *loadMoreView; // 水滴中央的图片样式 @property (nonatomic, strong) UIImageView *refleshView; // 刷新提示字时 前方显示的 图片样式 @property (nonatomic, strong) UIImageView *refreshRemindPicView; // refresh上的提示label @property (nonatomic, strong) UILabel *refreshRemind; // loadMore上的提示label @property (nonatomic, strong) UILabel *loadMoreRemind; // loadMore转圈时显示的文字,比如 "加载中..",没有设置则转动图样居中显示 @property (nonatomic, strong) NSString *loadMoreIndicatorWord; @property (nonatomic, weak) id <WaterRefreshDelegate> refreshDelegate; @property (nonatomic, weak) id <WaterLoadMoreDelegate> loadMoreDelegate; @property (nonatomic, assign, readonly) BOOL isRefreshing; @property (nonatomic, assign, readonly) BOOL isLoadingMore; // initial method - (id)initWithWithType:(WaterViewType)type; // !你需要在使用它的ScrollView 或者 VC的dealloc中调用这个方法 - (void)deallocWithCloseConnect; /*w start: 自动开始水滴下拉刷新自动开始,另一种是靠你手去拖动触发 end : 结束水滴下拉刷新并显示提示动画,请传入提示语,你可能会想提示:"刷新成功", 可指定提示图片 */ - (void)startRefreshWithExpansion; - (void)startRefreshWithExpansionWithoutDelegate; - (void)endRefreshWithRemindsWords:(NSString *)remind remindImage:(UIImage *)remindImage; /* start: 自动开始上拉加载,另一种是靠手去上拉 end : 停止上拉加载,可给予提示 */ - (void)startLoadingMoreExpansion; - (void)startLoadingMoreExpansionWithOutDelegate; - (void)endLoadingMoreWithRemind:(NSString *)remind; // 禁掉loadMore的上拉加载功能,ban为YES时提示语默认为“没有更多内容” - (void)banFunctionOfStartLoadMore:(BOOL)ban remind:(NSString *)remind; - (void)setWaterRefreshColor:(UIColor*)color; @end @protocol WaterRefreshDelegate <NSObject> // 水滴下拉刷新的代理方法 - (void)slimeRefreshStartRefresh:(WaterRefreshLoadMoreView*)refreshView; - (void)slimeRefreshEndRefresh:(WaterRefreshLoadMoreView *)refreshView; @end @protocol WaterLoadMoreDelegate <NSObject> @optional // 加载更多的代理方法 - (void)loadMoreViewStartLoad:(WaterRefreshLoadMoreView*)refreshView; - (void)loadMoreViewEndLoad:(WaterRefreshLoadMoreView *)refreshView; @end
WaterRefreshLoadMoreView.h
// // WaterRefreshLoadMoreView.m // shiyanforwaterfresh // // Created by 俊涵 on 13-9-19. // Copyright (c) 2013年 俊涵. All rights reserved. // #import "WaterRefreshLoadMoreView.h" #import "SRSlimeView.h" #import <QuartzCore/QuartzCore.h> #define KEYPATH_CONTENTOFFSET @"contentOffset" #define KEYPATH_CONTENTSIZE @"contentSize" #define STRING_LOADMORE @"加载更多" #define STRING_NOMORE @"没有更多内容" #define HEIGHT_DEFAULT 40.0f #define DURIATION_ANIMATE 0.2f #define DURIATION_SHOWNOMORE 1.5f #define TAG_LOADMORE_INDICATOR 200 #define kRefreshImageWidth 20.0f #define kDistanceFromRemind 10.0f static int scrollViewObservanceContext; static int scrollViewContentSizeContext; typedef enum { FooterViewStateNormal, FooterViewStateNormalPullling, FooterViewStateLoadingMore }FooterViewState; @interface WaterRefreshLoadMoreView() { UIImageView *_refleshView; SRSlimeView *_slime; } @property (nonatomic, assign) FooterViewState footerState; @property (nonatomic, strong) SRSlimeView *slime; @property (nonatomic, strong) UIActivityIndicatorView *loadMoreActivityView; @property (nonatomic, assign) BOOL closeLoadMore; @property (nonatomic, assign) BOOL endAnimation; @property (nonatomic, assign) BOOL endRefreshWithRemind; @property (nonatomic, assign) BOOL broken; @property (nonatomic, assign) BOOL slimeMissWhenGoingBack; @property (nonatomic, assign) BOOL lastDragScroll; @property (nonatomic, assign) BOOL isHasMoreContent; @property (nonatomic, assign) CGFloat upInset; @property (nonatomic, strong) NSString *refreshRemindWord; @property (nonatomic, strong) NSString *loadMoreRemindWord; @property (nonatomic, strong) UITapGestureRecognizer *gesture; @end @implementation WaterRefreshLoadMoreView { UIActivityIndicatorView *_activityIndicatorView; CGFloat _oldLength; BOOL _unmissSlime; CGFloat _dragingHeight; } - (id)initWithFrame:(CGRect)frame { self = [self initWithHeight:HEIGHT_DEFAULT]; return self; } - (id)initWithWithType:(WaterViewType)type { self = [self initWithHeight:HEIGHT_DEFAULT]; self.type = type; return self; } - (id)initWithHeight:(CGFloat)height { CGRect frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, height); self = [super initWithFrame:frame]; if (self) { // slime _slime = [[SRSlimeView alloc] initWithFrame:self.bounds]; _slime.startPoint = CGPointMake(frame.size.width / 2, height / 2); [self addSubview:_slime]; [_slime setPullApartTarget:self action:@selector(pullApart:)]; // 刷新图样 _refleshView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"waterRefreshLoadMore_refresh"]]; _refleshView.center = _slime.startPoint; _refleshView.bounds = CGRectMake(0.0f, 0.0f, kRefreshImageWidth, kRefreshImageWidth); [self addSubview:_refleshView]; // indicator _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray]; _activityIndicatorView.center = _slime.startPoint; [self addSubview:_activityIndicatorView]; // refreshRemind self.refreshRemind = [[UILabel alloc] initWithFrame:_slime.frame]; [self addSubview:self.refreshRemind]; self.refreshRemind.backgroundColor = [UIColor clearColor]; self.refreshRemind.textAlignment = NSTextAlignmentCenter; self.refreshRemind.textColor = [UIColor grayColor]; self.refreshRemind.font = [UIFont systemFontOfSize:13.0f]; self.refreshRemind.hidden = YES; // refreshRemindPicView self.refreshRemindPicView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kRefreshImageWidth, kRefreshImageWidth)]; [self addSubview:self.refreshRemindPicView]; self.refreshRemindPicView.hidden = YES; // 设置拖拽临界点的高度 _dragingHeight = height; self.slimeMissWhenGoingBack = NO; self.waterRefreshView = self; } return self; } - (void)setWaterRefreshColor:(UIColor*)color { [_activityIndicatorView setColor:color]; self.refreshRemind.textColor = color; // _slime.bodyColor = color; } - (void)didMoveToSuperview { [super didMoveToSuperview]; if ([self.superview isKindOfClass:[UIScrollView class]]) { _scrollView = (id)[self superview]; if (self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyLoadMore) { [self addLoadMoreView]; } self.isHasMoreContent = YES; // config frame CGRect rect = self.frame; rect.origin.y = rect.size.height ? -rect.size.height : -_dragingHeight; rect.size.width = _scrollView.frame.size.width; self.frame = rect; // inset UIEdgeInsets inset = self.scrollView.contentInset; inset.top = _upInset; self.scrollView.contentInset = inset; [self.scrollView addObserver:self forKeyPath:KEYPATH_CONTENTOFFSET options:0 context:&scrollViewObservanceContext]; [self.scrollView addObserver:self forKeyPath:KEYPATH_CONTENTSIZE options:0 context:&scrollViewContentSizeContext]; // configLoadMoreHide [self configLoadMoreHide]; }else if (!self.superview) { self.scrollView = nil; } } - (void)addLoadMoreView { // add loadmore CGFloat maxTop = self.scrollView.frame.size.height > self.scrollView.contentSize.height ? self.scrollView.frame.size.height : self.scrollView.contentSize.height; self.loadMoreView = [[UIView alloc] initWithFrame:CGRectMake(0, maxTop, [UIScreen mainScreen].bounds.size.width, HEIGHT_DEFAULT)]; [self.scrollView addSubview:self.loadMoreView]; self.loadMoreView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; self.loadMoreActivityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; self.loadMoreActivityView.tag = TAG_LOADMORE_INDICATOR; [self.loadMoreView addSubview:self.loadMoreActivityView]; self.loadMoreActivityView.center = CGPointMake(self.loadMoreView.frame.size.width / 2, self.loadMoreView.frame.size.height / 2); [self showLoadMoreRemind:STRING_LOADMORE]; // config loadMoreRemind self.loadMoreRemind = [[UILabel alloc] initWithFrame:self.loadMoreView.bounds]; self.loadMoreRemind.backgroundColor = [UIColor clearColor]; self.loadMoreRemind.font = [UIFont systemFontOfSize:12.0f]; self.loadMoreRemind.textColor = [UIColor grayColor]; self.loadMoreRemind.textAlignment = NSTextAlignmentCenter; [self.loadMoreView addSubview:self.loadMoreRemind]; //self.loadMoreRemind.hidden = NO; [self showLoadMoreRemind:STRING_LOADMORE]; // gesture self.gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(loadMoreActionByClick)]; [self.loadMoreView addGestureRecognizer:self.gesture]; self.footerState = FooterViewStateNormal; [self configLoadMoreHide]; } - (void)loadMoreActionByClick { [self scrollViewDataSourceDidStartLoadingMore:self.scrollView]; } - (void)dealloc { [self clearObservance]; } - (void)deallocWithCloseConnect // TODO :换成更好的办法 { [self clearObservance]; } - (void)clearObservance { [self.scrollView removeObserver:self forKeyPath:KEYPATH_CONTENTOFFSET context:&scrollViewObservanceContext]; [self.scrollView removeObserver:self forKeyPath:KEYPATH_CONTENTSIZE context:&scrollViewContentSizeContext]; self.scrollView = nil; } - (void)removeFromSuperview { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [super removeFromSuperview]; } #pragma mark - setters And getter - (void)setScrollView:(UIScrollView *)scrollView { if (_scrollView != scrollView) { _scrollView = scrollView; if (self.superview) { [self.loadMoreView removeFromSuperview]; self.loadMoreView = nil; [self removeFromSuperview]; } [self.scrollView addSubview:self]; } } - (void)setType:(WaterViewType)type { if (self.type == type) { return; } if (self.type != WaterRefreshTypeNone) { [self.scrollView removeObserver:self forKeyPath:KEYPATH_CONTENTOFFSET context:&scrollViewObservanceContext]; [self.scrollView removeObserver:self forKeyPath:KEYPATH_CONTENTSIZE context:&scrollViewContentSizeContext]; } _type = type; _isLoadingMore = NO; [self setFooterState:FooterViewStateNormal]; self.closeLoadMore = NO; // 如果切换类型之前动画还未结束 if (self.endAnimation == NO) { self.endAnimation = YES; } self.endRefreshWithRemind = NO; [self endRefreshWithRemindsWords:@"" remindImage:nil]; [self endLoadingMoreWithRemind:@""]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(collapse) object:nil]; [self backStateOfScrollView]; if (type == WaterRefreshTypeRefreshAndLoadMore || type == WaterRefreshTypeOnlyRefresh) { self.hidden = NO; self.loadMoreView.hidden = NO; UIScrollView *scrollView = self.scrollView; self.scrollView = nil; //clear self.scrollView = scrollView; } else if (type == WaterRefreshTypeOnlyLoadMore) { self.loadMoreView.hidden = NO; self.hidden = YES; UIScrollView *scrollView = self.scrollView; self.scrollView = nil; //clear self.scrollView = scrollView; } else if(type == WaterRefreshTypeNone) { self.hidden = YES; self.loadMoreView.hidden = YES; } } - (void)backStateOfScrollView { UIEdgeInsets inset = self.scrollView.contentInset; inset.top = _upInset; inset.bottom = 0.0f; self.scrollView.contentInset = inset; } - (void)setFrame:(CGRect)frame { [super setFrame:frame]; if (_slime.state == SRSlimeStateNormal) { _slime.frame = CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height); _slime.startPoint = CGPointMake(frame.size.width / 2, _dragingHeight / 2); } _refleshView.center = _slime.startPoint; _activityIndicatorView.center = _slime.startPoint; } - (void)setUpInset:(CGFloat)upInset { _upInset = upInset; UIEdgeInsets inset = _scrollView.contentInset; inset.top = _upInset; _scrollView.contentInset = inset; } - (void)setSlimeMissWhenGoingBack:(BOOL)slimeMissWhenGoingBack { _slimeMissWhenGoingBack = slimeMissWhenGoingBack; if (!slimeMissWhenGoingBack) { _slime.alpha = 1; }else { CGPoint p = _scrollView.contentOffset; self.alpha = -(p.y + _upInset) / _dragingHeight; } } #pragma mark - Scroll handle - (void)scrollViewDidScroll { CGPoint offset = _scrollView.contentOffset; CGRect rect = self.frame; // 下拉刷新 if (self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyRefresh) { if (offset.y <= - _dragingHeight - _upInset) { // 处于拉伸变形的状态 rect.origin.y = offset.y + _upInset; rect.size.height = -offset.y; rect.size.height = ceilf(rect.size.height); self.frame = rect; //special setFrame if (!self.isRefreshing) { [_slime setNeedsDisplay]; } if (!_broken) { float l = -(offset.y + _dragingHeight + _upInset); if (l <= _oldLength) { // 往回的时候 l = MIN(distansBetween(_slime.startPoint, _slime.toPoint), l); CGPoint ssp = _slime.startPoint; _slime.toPoint = CGPointMake(ssp.x, ssp.y + l); CGFloat pf = (1.0f - l / _slime.viscous) * (1.0f - kStartTo) + kStartTo; _refleshView.layer.transform = CATransform3DMakeScale(pf, pf, 1); }else if (self.scrollView.isDragging) { // 手拽着往下拉伸的时候 CGPoint ssp = _slime.startPoint; _slime.toPoint = CGPointMake(ssp.x, ssp.y + l); CGFloat pf = (1.0f - l / _slime.viscous) * (1.0f - kStartTo) + kStartTo; _refleshView.layer.transform = CATransform3DMakeScale(pf, pf, 1); } _oldLength = l; } if (self.alpha != 1.0f) self.alpha = 1.0f; //拉出来显示的时候可能 alpha不为1 }else if (offset.y < -_upInset) { rect.origin.y = -_dragingHeight; rect.size.height = _dragingHeight; self.frame = rect; [_slime setNeedsDisplay]; _slime.toPoint = _slime.startPoint; if (_slimeMissWhenGoingBack) self.alpha = -(offset.y + _upInset) / _dragingHeight; } if (self.isRefreshing && self.broken == NO) { // 配合适应inset避免 使用 tableview的话 section位置浮动错误 UIEdgeInsets inset = _scrollView.contentInset; CGFloat offsetmath = MAX(_scrollView.contentOffset.y * -1, 0); offsetmath = MIN(offsetmath, HEIGHT_DEFAULT); inset.top = offsetmath; _scrollView.contentInset = inset; } } if ((self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyLoadMore) && self.closeLoadMore == NO) { // 上拉 if (self.footerState == FooterViewStateNormal) { if (self.scrollView.isDragging && [self offsetFromBottom:self.scrollView] > self.loadMoreView.frame.size.height + 5) { [self setFooterState:FooterViewStateNormalPullling]; if (self.isHasMoreContent == NO) { } else { [self scrollViewDataSourceDidStartLoadingMore:self.scrollView]; } } } else if (self.footerState == FooterViewStateNormalPullling) { if (self.scrollView.isDragging && [self offsetFromBottom:self.scrollView] < self.frame.size.height + 5) { [self setFooterState:FooterViewStateNormal]; } } else if (self.footerState == FooterViewStateLoadingMore) { } } } - (void)scrollViewDidEndDraging { // 下拉刷新 if (self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyRefresh) { if (_broken) { self.broken = NO; if (self.isRefreshing) { [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView animateWithDuration:DURIATION_ANIMATE animations:^{ UIEdgeInsets inset = _scrollView.contentInset; inset.top = _upInset + _dragingHeight; _scrollView.contentInset = inset; } completion:^(BOOL finished) { }]; } } } // 上拉 if ((self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyLoadMore) && self.closeLoadMore == NO) { if (self.footerState == FooterViewStateNormalPullling) { if (self.isHasMoreContent == NO) { } else { [self scrollViewDataSourceDidStartLoadingMore:self.scrollView]; } } } } #pragma mark - loadMore - (void)relayoutLoadMoreViewAndInset { if (self.closeLoadMore == YES) { return; } CGFloat max = self.scrollView.contentSize.height < self.scrollView.frame.size.height ? self.scrollView.frame.size.height - self.scrollView.contentSize.height : 0; UIEdgeInsets inset = self.scrollView.contentInset; inset.bottom = max + self.loadMoreView.frame.size.height; self.scrollView.contentInset = inset; CGFloat footerViewTop = MAX(self.scrollView.frame.size.height, self.scrollView.contentSize.height); self.loadMoreView.frame = CGRectMake(0, footerViewTop, self.loadMoreView.frame.size.width, self.loadMoreView.frame.size.height); } - (void)configLoadMoreHide { if (self.type == WaterRefreshTypeRefreshAndLoadMore || self.type == WaterRefreshTypeOnlyLoadMore) { if (self.scrollView.contentSize.height < self.scrollView.frame.size.height) { self.loadMoreView.hidden = YES; self.closeLoadMore = YES; UIEdgeInsets inset = self.scrollView.contentInset; inset.bottom = 0.0f; self.scrollView.contentInset = inset; } else { self.loadMoreView.hidden = YES; self.closeLoadMore = YES; UIEdgeInsets inset = self.scrollView.contentInset; inset.bottom = self.loadMoreView.frame.size.height; self.scrollView.contentInset = inset; self.loadMoreView.hidden = NO; self.closeLoadMore = NO; } } } - (void)startLoadingMoreExpansion { [self scrollViewDataSourceDidStartLoadingMore:self.scrollView]; } - (void)startLoadingMoreExpansionWithOutDelegate { [self scrollViewDataSourceDidStartLoadingMoreWithOutDelegate:self.scrollView]; } - (void)endLoadingMoreWithRemind:(NSString *)remind { if (self.type == WaterRefreshTypeNone || self.type == WaterRefreshTypeOnlyRefresh) { return; } if (_isLoadingMore) { self.loadMoreRemindWord = remind; [self safeStopLoadingMore:self.scrollView]; } } - (void)safeStartLoadingMore:(UIScrollView *)scrollView { if (!_isLoadingMore) { _isLoadingMore = YES; } else { return; } [self endRefreshWithRemindsWords:nil remindImage:nil]; [self setFooterState:FooterViewStateLoadingMore]; self.gesture.enabled = NO; self.loadMoreView.hidden = NO; [self showLoadMoreRemind:@""]; //清空 [self.loadMoreActivityView startAnimating]; // config 转动时提示 [self configIndicatorPositionWithWord:self.loadMoreIndicatorWord]; self.loadMoreRemind.alpha = 0.45f; // cofig animate with indicatorword NSTimeInterval animateDuration = 0.7f; [UIView animateWithDuration:animateDuration animations:^{ self.loadMoreRemind.alpha = 1.0f; }]; CAKeyframeAnimation *aniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; aniamtion.values = [NSArray arrayWithObjects: [NSValue valueWithCATransform3D: CATransform3DRotate(CATransform3DMakeScale(0.01, 0.01, 0.1), -M_PI, 0, 0, 1)], [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.6, 1.6, 1)], [NSValue valueWithCATransform3D:CATransform3DIdentity], nil]; aniamtion.keyTimes = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0], [NSNumber numberWithFloat:0.6], [NSNumber numberWithFloat:1], nil]; aniamtion.timingFunctions = [NSArray arrayWithObjects: [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut], nil]; aniamtion.duration = animateDuration; self.loadMoreActivityView.layer.transform = CATransform3DIdentity; [self.loadMoreActivityView.layer addAnimation:aniamtion forKey:@""]; } - (void)safeStopLoadingMore:(UIScrollView *)scrollView { if (!_isLoadingMore) { return; } [UIView setAnimationCurve:UIViewAnimationCurveEaseIn]; [UIView animateWithDuration:0.2f animations:^{ self.loadMoreActivityView.layer.transform = CATransform3DRotate(CATransform3DMakeScale(0.01f, 0.01f, 0.1f), -M_PI, 0, 0, 1); self.loadMoreRemind.alpha = 0.2f; } completion:^(BOOL finished) { [self.loadMoreActivityView stopAnimating]; self.loadMoreActivityView.layer.transform = CATransform3DRotate(CATransform3DMakeScale(1.0f, 1.0f, 1.0f), M_PI, 0, 0, 1); self.loadMoreRemind.alpha = 1.0f; [self relayoutLoadMoreViewAndInset]; if (self.isHasMoreContent) { if (self.loadMoreRemindWord) { //错误提示语 [self showLoadMoreRemind:self.loadMoreRemindWord]; [self performSelector:@selector(backToLoadMoreRemind) withObject:nil afterDelay:1.0f]; } else { [self showLoadMoreRemind:STRING_LOADMORE]; _isLoadingMore = NO; [self setFooterState:FooterViewStateNormal]; self.gesture.enabled = YES; if ([self.loadMoreDelegate respondsToSelector:@selector(loadMoreViewEndLoad:)]) { [self.loadMoreDelegate loadMoreViewEndLoad:self]; } } } else { if (self.loadMoreRemindWord) { if ([self.loadMoreRemindWord isEqualToString:@""]) { self.loadMoreRemindWord = STRING_NOMORE; } [self showLoadMoreRemind:self.loadMoreRemindWord]; } else { [self showNoContentRemind]; } [self performSelector:@selector(setInsetBottomAnimateToZeroAndCallDelegate:) withObject:[NSNumber numberWithBool:YES] afterDelay:DURIATION_SHOWNOMORE]; } }]; } - (void)setInsetBottomAnimateToZeroAndCallDelegate:(NSNumber *)callDelegate { [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView animateWithDuration:DURIATION_ANIMATE animations:^{ [self setInsetBottomToZero]; } completion:^(BOOL finished) { _isLoadingMore = NO; [self setFooterState:FooterViewStateNormal]; if (self.isHasMoreContent) { self.gesture.enabled = YES; } if (callDelegate.boolValue) { if ([self.loadMoreDelegate respondsToSelector:@selector(loadMoreViewEndLoad:)]) { [self.loadMoreDelegate loadMoreViewEndLoad:self]; } }; }]; [UIView commitAnimations]; } - (void)setInsetBottomToZero { UIEdgeInsets currentInsets = self.scrollView.contentInset; currentInsets.bottom = 0; self.scrollView.contentInset = currentInsets; } - (void)scrollViewDataSourceDidStartLoadingMore:(UIScrollView *)scrollView { if (!_isLoadingMore) { [self safeStartLoadingMore:scrollView]; if ([self.loadMoreDelegate respondsToSelector:@selector(loadMoreViewStartLoad:)]) { [self.loadMoreDelegate loadMoreViewStartLoad:self]; } } } - (void)scrollViewDataSourceDidStartLoadingMoreWithOutDelegate:(UIScrollView *)scrollView { [self safeStartLoadingMore:scrollView]; } - (void)scrollViewDataSourceDidFinishLoadingMore:(UIScrollView *)scrollView { [self safeStopLoadingMore:scrollView]; } - (CGFloat)offsetFromBottom:(UIScrollView *)scrollView { CGFloat change = scrollView.contentSize.height < scrollView.frame.size.height ? 0 : scrollView.contentSize.height - scrollView.frame.size.height; return scrollView.contentOffset.y - change; } - (void)showNoContentRemind { self.loadMoreRemind.hidden = NO; [self showLoadMoreRemind:STRING_NOMORE]; } - (void)showLoadMoreRemind:(NSString *)remindword { self.loadMoreActivityView.hidden = YES; self.loadMoreRemind.hidden = NO; self.loadMoreRemind.text = remindword; self.loadMoreRemind.center = CGPointMake(self.loadMoreView.frame.size.width / 2, self.loadMoreView.frame.size.height / 2); self.loadMoreRemindWord = nil; //clear } - (void)backToLoadMoreRemind { UILabel *label = self.loadMoreRemind; [UIView animateWithDuration:0.3f animations:^{ label.alpha = 0.0f; } completion:^(BOOL finished) { [self showLoadMoreRemind:STRING_LOADMORE]; [UIView animateWithDuration:0.3f animations:^{ label.alpha = 1.0f; self.gesture.enabled = YES; _isLoadingMore = NO; [self setFooterState:FooterViewStateNormal]; if ([self.loadMoreDelegate respondsToSelector:@selector(loadMoreViewEndLoad:)]) { [self.loadMoreDelegate loadMoreViewEndLoad:self]; } }]; }]; } - (void)banFunctionOfStartLoadMore:(BOOL)ban remind:(NSString *)remind { if (ban) { self.gesture.enabled = NO; self.isHasMoreContent = NO; if (remind) { self.loadMoreRemind.text = remind; [self endLoadingMoreWithRemind:remind]; } else { self.loadMoreRemind.text = STRING_NOMORE; [self endLoadingMoreWithRemind:STRING_NOMORE]; } [self performSelector:@selector(setInsetBottomAnimateToZeroAndCallDelegate:) withObject:[NSNumber numberWithBool:NO] afterDelay:DURIATION_SHOWNOMORE]; } else { self.gesture.enabled = YES; self.isHasMoreContent = YES; if (!_isLoadingMore) { [self showLoadMoreRemind:STRING_LOADMORE]; } } } - (void)configIndicatorPositionWithWord:(NSString *)word { if (word && ![word isEqualToString:@""]) { // 如果有提供的文字就做左偏移 self.loadMoreRemind.hidden = NO; self.loadMoreRemind.alpha = 1.0f; self.loadMoreRemind.text = word; CGSize size = [word sizeWithFont:self.loadMoreRemind.font constrainedToSize:CGSizeMake(self.loadMoreView.bounds.size.width, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping]; CGRect rect = self.loadMoreRemind.frame; rect.size.width = size.width; rect.size.height = size.height; self.loadMoreRemind.frame = rect; self.loadMoreRemind.center = CGPointMake(self.loadMoreView.frame.size.width / 2, self.loadMoreView.frame.size.height / 2); self.loadMoreActivityView.center = CGPointMake(self.loadMoreRemind.frame.origin.x - kDistanceFromRemind, self.loadMoreActivityView.center.y); [self alignmentWithView:self.loadMoreActivityView lable:self.loadMoreRemind distance:kDistanceFromRemind totalWidth:self.scrollView.frame.size.width]; } else { //还原 self.loadMoreRemind.hidden = YES; UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)[self.loadMoreView viewWithTag:TAG_LOADMORE_INDICATOR]; indicator.center = CGPointMake(self.loadMoreView.frame.size.width / 2, self.loadMoreView.frame.size.height / 2); self.loadMoreRemind.center = indicator.center; } } - (void)alignmentWithView:(UIView *)view lable:(UILabel *)label distance:(CGFloat)distance totalWidth:(CGFloat)totalWidth { CGFloat viewWidths = view.frame.size.width + distance + label.frame.size.width; CGFloat left = (totalWidth - viewWidths) / 2; view.center = CGPointMake(left + view.frame.size.width / 2 , view.center.y); label.center = CGPointMake(view.center.x + view.frame.size.width /2 + distance + label.frame.size.width / 2, label.center.y); } #pragma mark - refresh - (void)setIsRefreshing:(BOOL)isRefreshing { if (_isRefreshing == isRefreshing) { return; } _isRefreshing = isRefreshing; if (_isRefreshing) { [self endLoadingMoreWithRemind:nil]; [_activityIndicatorView.layer removeAllAnimations]; [_activityIndicatorView startAnimating]; CAKeyframeAnimation *aniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; aniamtion.values = [NSArray arrayWithObjects: [NSValue valueWithCATransform3D: CATransform3DRotate(CATransform3DMakeScale(0.01, 0.01, 0.1), -M_PI, 0, 0, 1)], [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.6, 1.6, 1)], [NSValue valueWithCATransform3D:CATransform3DIdentity], nil]; aniamtion.keyTimes = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0], [NSNumber numberWithFloat:0.6], [NSNumber numberWithFloat:1], nil]; aniamtion.timingFunctions = [NSArray arrayWithObjects: [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut], nil]; aniamtion.duration = 0.7; aniamtion.delegate = self; _activityIndicatorView.layer.transform = CATransform3DIdentity; [_activityIndicatorView.layer addAnimation:aniamtion forKey:@""]; _refleshView.hidden = YES; if (!_unmissSlime){ _slime.state = SRSlimeStateMiss; }else { _unmissSlime = NO; } }else { // 刷新结束 [_activityIndicatorView stopAnimating]; [_activityIndicatorView.layer removeAllAnimations]; _refleshView.layer.transform = CATransform3DIdentity; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView animateWithDuration:0.35f animations:^{ UIEdgeInsets inset = _scrollView.contentInset; inset.top = _upInset; _scrollView.contentInset = inset; if (_scrollView.contentOffset.y == -_upInset && _slimeMissWhenGoingBack) { self.alpha = 0.0f; } } completion:^(BOOL finished) { _broken = NO; _slime.hidden = NO; _refleshView.hidden = NO; self.refreshRemind.text = @""; self.refreshRemindPicView.hidden = YES; }]; } } - (void)startRefreshWithExpansion { if (self.type == WaterRefreshTypeOnlyLoadMore || self.type == WaterRefreshTypeNone) { return; } if (self.isRefreshing) { return; } [UIView animateWithDuration:DURIATION_ANIMATE animations:^() { UIEdgeInsets inset = _scrollView.contentInset; inset.top = _upInset + _dragingHeight; _scrollView.contentInset = inset; [_scrollView setContentOffset:CGPointMake(_scrollView.contentOffset.x, -_scrollView.contentInset.top) animated:NO]; }]; [self startRefresh]; } -(void)startRefreshWithExpansionWithoutDelegate{ if (self.type == WaterRefreshTypeOnlyLoadMore || self.type == WaterRefreshTypeNone) { return; } if (self.isRefreshing) { return; } [UIView animateWithDuration:DURIATION_ANIMATE animations:^() { UIEdgeInsets inset = _scrollView.contentInset; inset.top = _upInset + _dragingHeight; _scrollView.contentInset = inset; [_scrollView setContentOffset:CGPointMake(_scrollView.contentOffset.x, -_scrollView.contentInset.top) animated:NO]; } completion:^(BOOL finished) { }]; self.isRefreshing = YES; } - (void)startRefresh { self.isRefreshing = YES; if ([self.refreshDelegate respondsToSelector:@selector(slimeRefreshStartRefresh:)]) { [self.refreshDelegate slimeRefreshStartRefresh:self]; } } - (void)pullApart:(WaterRefreshLoadMoreView*)refreshView { self.broken = YES; _unmissSlime = YES; [self startRefresh]; } - (void)endRefreshWithRemindsWords:(NSString *)remind remindImage:(UIImage *)remindImage { if (self.type == WaterRefreshTypeOnlyLoadMore || self.type == WaterRefreshTypeNone ) { return; } if (self.isRefreshing && self.endRefreshWithRemind == NO) { self.refreshRemindWord = remind; self.refreshRemindPicView.image = remindImage; self.endRefreshWithRemind = YES; if (self.endAnimation == YES) { [self restore]; } } } - (void)showRemindWordAndClear; { if (!self.refreshRemindWord || [self.refreshRemindWord isEqualToString:@""]) { [self collapse]; return; } self.refreshRemind.hidden = NO; self.refreshRemind.text = self.refreshRemindWord; self.refreshRemindWord = nil; [self performSelector:@selector(collapse) withObject:nil afterDelay:0.5f]; } - (void)configRefreshPicViewPositionWithWord:(NSString *)word { if (word && ![word isEqualToString:@""]) { // 如果有提供的文字就做左偏移 //self.refreshRemindPicView.hidden = NO; self.refreshRemindPicView.alpha = 1.0f; CGSize size = [word sizeWithFont:self.refreshRemind.font constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping]; CGRect rect = self.refreshRemind.frame; rect.size.width = size.width; rect.size.height = size.height; self.refreshRemind.frame = rect; self.refreshRemind.center = _slime.startPoint; self.refreshRemindPicView.center = CGPointMake(self.refreshRemind.frame.origin.x - kDistanceFromRemind, self.refreshRemind.center.y); [self alignmentWithView:self.refreshRemindPicView lable:self.refreshRemind distance:kDistanceFromRemind totalWidth:self.scrollView.frame.size.width]; } else { //还原 //self.refreshRemindPicView.hidden = NO; self.refreshRemindPicView.alpha = 1.0f; self.refreshRemindPicView.center = _slime.startPoint; } } - (void)restore { _oldLength = 0.0f; _slime.toPoint = _slime.startPoint; [_activityIndicatorView.layer removeAllAnimations]; [UIView setAnimationCurve:UIViewAnimationCurveEaseIn]; [UIView animateWithDuration:0.3f animations:^{ _activityIndicatorView.layer.transform = CATransform3DRotate(CATransform3DMakeScale(0.01f, 0.01f, 0.1f), -M_PI, 0, 0, 1); } completion:^(BOOL finished) { [self relayoutLoadMoreViewAndInset]; if (self.refreshRemindPicView.image) { self.refreshRemindPicView.hidden = NO; [self configRefreshPicViewPositionWithWord:self.refreshRemindWord]; } else { [self alignmentWithView:nil lable:self.refreshRemind distance:0.0f totalWidth:self.scrollView.frame.size.width]; } if (self.refreshRemindWord) { self.refreshRemind.hidden = NO; [self showRemindWordAndClear]; } else { [self collapse]; } }]; } - (void)collapse { if (_isRefreshing == NO) { return; } [self setIsRefreshing:NO]; self.endRefreshWithRemind = NO; _slime.state = SRSlimeStateNormal; if ([self.refreshDelegate respondsToSelector:@selector(slimeRefreshEndRefresh:)]) { [self.refreshDelegate slimeRefreshEndRefresh:self]; } } #pragma mark - Observer handle - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == &scrollViewObservanceContext) { // handle change if ([keyPath isEqualToString:KEYPATH_CONTENTOFFSET]) { [self scrollViewDidScroll]; if (self.scrollView.dragging == YES && self.lastDragScroll == NO) { self.lastDragScroll = YES; } else if (self.scrollView.dragging == NO && self.lastDragScroll == YES) { self.lastDragScroll = NO; [self scrollViewDidEndDraging]; } } } if (context == &scrollViewContentSizeContext) { if ([keyPath isEqualToString:KEYPATH_CONTENTSIZE]) { [self configLoadMoreHide]; [self relayoutLoadMoreViewAndInset]; } } } #pragma mark - Animation Delegate - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { self.endAnimation = YES; if (self.endRefreshWithRemind == YES) { self.endRefreshWithRemind = NO; self.endAnimation = NO; [self restore]; } } - (void)animationDidStart:(CAAnimation *)anim { self.endAnimation = NO; } @end
WaterRefreshLoadMoreView.m
源码用的图片:
使用源码:
// // ViewController.m // Refresh // // Created by YouXianMing on 14/11/21. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import "ViewController.h" #import "WaterRefreshLoadMoreView.h" @interface ViewController () @property (nonatomic, strong) UITableView *tableView; @property (nonatomic, strong) WaterRefreshLoadMoreView *refreshLoadMoreView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, 320, 400) style:UITableViewStylePlain]; [self.view addSubview:self.tableView]; _refreshLoadMoreView = [[WaterRefreshLoadMoreView alloc] initWithWithType:WaterRefreshTypeOnlyRefresh]; _refreshLoadMoreView.scrollView = self.tableView; } - (void)dealloc { [_refreshLoadMoreView deallocWithCloseConnect]; } @end
时间: 2024-10-24 06:47:27