MJRefreshBaseView.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //
  2. // MJRefreshBaseView.m
  3. // MJRefresh
  4. //
  5. // Created by mj on 13-3-4.
  6. // Copyright (c) 2013年 itcast. All rights reserved.
  7. //
  8. #import "MJRefreshBaseView.h"
  9. #import "MJRefreshConst.h"
  10. @interface MJRefreshBaseView()
  11. {
  12. BOOL _hasInitInset;
  13. }
  14. /**
  15. 交给子类去实现
  16. */
  17. // 合理的Y值
  18. - (CGFloat)validY;
  19. // view的类型
  20. - (MJRefreshViewType)viewType;
  21. @end
  22. @implementation MJRefreshBaseView
  23. #pragma mark 创建一个UILabel
  24. - (UILabel *)labelWithFontSize:(CGFloat)size
  25. {
  26. UILabel *label = [[UILabel alloc] init];
  27. label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  28. label.font = [UIFont boldSystemFontOfSize:size];
  29. label.textColor = MJRefreshLabelTextColor;
  30. label.backgroundColor = [UIColor clearColor];
  31. label.textAlignment = NSTextAlignmentCenter;
  32. return label;
  33. }
  34. #pragma mark - 初始化方法
  35. - (instancetype)initWithScrollView:(UIScrollView *)scrollView
  36. {
  37. if (self = [super init]) {
  38. self.scrollView = scrollView;
  39. }
  40. return self;
  41. }
  42. - (void)layoutSubviews
  43. {
  44. [super layoutSubviews];
  45. if (!_hasInitInset) {
  46. _scrollViewInitInset = _scrollView.contentInset;
  47. [self observeValueForKeyPath:MJRefreshContentSize ofObject:nil change:nil context:nil];
  48. _hasInitInset = YES;
  49. if (_state == MJRefreshStateWillRefreshing) {
  50. [self setState:MJRefreshStateRefreshing];
  51. }
  52. }
  53. }
  54. #pragma mark 构造方法
  55. - (instancetype)initWithFrame:(CGRect)frame {
  56. if (self = [super initWithFrame:frame]) {
  57. // 1.自己的属性
  58. self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  59. self.backgroundColor = [UIColor clearColor];
  60. // 2.时间标签
  61. [self addSubview:_lastUpdateTimeLabel = [self labelWithFontSize:12]];
  62. // 3.状态标签
  63. [self addSubview:_statusLabel = [self labelWithFontSize:13]];
  64. // 4.箭头图片
  65. UIImageView *arrowImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:kSrcName(@"arrow")]];
  66. arrowImage.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  67. [self addSubview:_arrowImage = arrowImage];
  68. // 5.指示器
  69. UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  70. activityView.bounds = arrowImage.bounds;
  71. activityView.autoresizingMask = arrowImage.autoresizingMask;
  72. [self addSubview:_activityView = activityView];
  73. // 6.设置默认状态
  74. [self setState:MJRefreshStateNormal];
  75. }
  76. return self;
  77. }
  78. #pragma mark 设置frame
  79. - (void)setFrame:(CGRect)frame
  80. {
  81. frame.size.height = MJRefreshViewHeight;
  82. [super setFrame:frame];
  83. CGFloat w = frame.size.width;
  84. CGFloat h = frame.size.height;
  85. if (w == 0 || _arrowImage.center.y == h * 0.5) return;
  86. CGFloat statusX = 0;
  87. CGFloat statusY = 5;
  88. CGFloat statusHeight = 20;
  89. CGFloat statusWidth = w;
  90. // 1.状态标签
  91. _statusLabel.frame = CGRectMake(statusX, statusY, statusWidth, statusHeight);
  92. // 2.时间标签
  93. CGFloat lastUpdateY = statusY + statusHeight + 5;
  94. _lastUpdateTimeLabel.frame = CGRectMake(statusX, lastUpdateY, statusWidth, statusHeight);
  95. // 3.箭头
  96. CGFloat arrowX = w * 0.5 - 90;
  97. _arrowImage.center = CGPointMake(arrowX, h * 0.5);
  98. // 4.指示器
  99. _activityView.center = _arrowImage.center;
  100. }
  101. - (void)setBounds:(CGRect)bounds
  102. {
  103. bounds.size.height = MJRefreshViewHeight;
  104. [super setBounds:bounds];
  105. }
  106. #pragma mark - UIScrollView相关
  107. #pragma mark 设置UIScrollView
  108. - (void)setScrollView:(UIScrollView *)scrollView
  109. {
  110. // 移除之前的监听器
  111. [_scrollView removeObserver:self forKeyPath:MJRefreshContentOffset context:nil];
  112. // 监听contentOffset
  113. [scrollView addObserver:self forKeyPath:MJRefreshContentOffset options:NSKeyValueObservingOptionNew context:nil];
  114. // 设置scrollView
  115. _scrollView = scrollView;
  116. [_scrollView addSubview:self];
  117. }
  118. #pragma mark 监听UIScrollView的contentOffset属性
  119. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  120. {
  121. if (![MJRefreshContentOffset isEqualToString:keyPath]) return;
  122. if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden
  123. || _state == MJRefreshStateRefreshing) return;
  124. // scrollView所滚动的Y值 * 控件的类型(头部控件是-1,尾部控件是1)
  125. CGFloat offsetY = _scrollView.contentOffset.y * self.viewType;
  126. CGFloat validY = self.validY;
  127. if (offsetY <= validY) return;
  128. if (_scrollView.isDragging) {
  129. CGFloat validOffsetY = validY + MJRefreshViewHeight;
  130. if (_state == MJRefreshStatePulling && offsetY <= validOffsetY) {
  131. // 转为普通状态
  132. [self setState:MJRefreshStateNormal];
  133. // 通知代理
  134. if ([_delegate respondsToSelector:@selector(refreshView:stateChange:)]) {
  135. [_delegate refreshView:self stateChange:MJRefreshStateNormal];
  136. }
  137. // 回调
  138. if (_refreshStateChangeBlock) {
  139. _refreshStateChangeBlock(self, MJRefreshStateNormal);
  140. }
  141. } else if (_state == MJRefreshStateNormal && offsetY > validOffsetY) {
  142. // 转为即将刷新状态
  143. [self setState:MJRefreshStatePulling];
  144. // 通知代理
  145. if ([_delegate respondsToSelector:@selector(refreshView:stateChange:)]) {
  146. [_delegate refreshView:self stateChange:MJRefreshStatePulling];
  147. }
  148. // 回调
  149. if (_refreshStateChangeBlock) {
  150. _refreshStateChangeBlock(self, MJRefreshStatePulling);
  151. }
  152. }
  153. } else { // 即将刷新 && 手松开
  154. if (_state == MJRefreshStatePulling) {
  155. // 开始刷新
  156. [self setState:MJRefreshStateRefreshing];
  157. // 通知代理
  158. if ([_delegate respondsToSelector:@selector(refreshView:stateChange:)]) {
  159. [_delegate refreshView:self stateChange:MJRefreshStateRefreshing];
  160. }
  161. // 回调
  162. if (_refreshStateChangeBlock) {
  163. _refreshStateChangeBlock(self, MJRefreshStateRefreshing);
  164. }
  165. }
  166. }
  167. }
  168. #pragma mark 设置状态
  169. - (void)setState:(MJRefreshState)state
  170. {
  171. if (_state != MJRefreshStateRefreshing) {
  172. // 存储当前的contentInset
  173. _scrollViewInitInset = _scrollView.contentInset;
  174. }
  175. // 1.一样的就直接返回
  176. if (_state == state) return;
  177. // 2.根据状态执行不同的操作
  178. switch (state) {
  179. case MJRefreshStateNormal: // 普通状态
  180. // 显示箭头
  181. _arrowImage.hidden = NO;
  182. // 停止转圈圈
  183. [_activityView stopAnimating];
  184. // 说明是刚刷新完毕 回到 普通状态的
  185. if (MJRefreshStateRefreshing == _state) {
  186. // 通知代理
  187. if ([_delegate respondsToSelector:@selector(refreshViewEndRefreshing:)]) {
  188. [_delegate refreshViewEndRefreshing:self];
  189. }
  190. // 回调
  191. if (_endStateChangeBlock) {
  192. _endStateChangeBlock(self);
  193. }
  194. }
  195. break;
  196. case MJRefreshStatePulling:
  197. break;
  198. case MJRefreshStateRefreshing:
  199. // 开始转圈圈
  200. [_activityView startAnimating];
  201. // 隐藏箭头
  202. _arrowImage.hidden = YES;
  203. _arrowImage.transform = CGAffineTransformIdentity;
  204. // 通知代理
  205. if ([_delegate respondsToSelector:@selector(refreshViewBeginRefreshing:)]) {
  206. [_delegate refreshViewBeginRefreshing:self];
  207. }
  208. // 回调
  209. if (_beginRefreshingBlock) {
  210. _beginRefreshingBlock(self);
  211. }
  212. break;
  213. default:
  214. break;
  215. }
  216. // 3.存储状态
  217. _state = state;
  218. }
  219. #pragma mark - 状态相关
  220. #pragma mark 是否正在刷新
  221. - (BOOL)isRefreshing
  222. {
  223. return MJRefreshStateRefreshing == _state;
  224. }
  225. #pragma mark 开始刷新
  226. - (void)beginRefreshing
  227. {
  228. if (self.window) {
  229. [self setState:MJRefreshStateRefreshing];
  230. } else {
  231. _state = MJRefreshStateWillRefreshing;
  232. }
  233. }
  234. #pragma mark 结束刷新
  235. - (void)endRefreshing
  236. {
  237. double delayInSeconds = self.viewType == MJRefreshViewTypeFooter ? 0.3 : 0.0;
  238. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
  239. dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  240. [self setState:MJRefreshStateNormal];
  241. });
  242. }
  243. #pragma mark - 随便实现
  244. - (CGFloat)validY { return 0;}
  245. - (MJRefreshViewType)viewType {return MJRefreshViewTypeHeader;}
  246. - (void)free
  247. {
  248. [_scrollView removeObserver:self forKeyPath:MJRefreshContentOffset];
  249. }
  250. - (void)removeFromSuperview
  251. {
  252. [self free];
  253. [super removeFromSuperview];
  254. _scrollView = nil;
  255. }
  256. - (void)endRefreshingWithoutIdle
  257. {
  258. [self endRefreshing];
  259. }
  260. - (int)totalDataCountInScrollView
  261. {
  262. int totalCount = 0;
  263. if ([self.scrollView isKindOfClass:[UITableView class]]) {
  264. UITableView *tableView = (UITableView *)self.scrollView;
  265. for (int section = 0; section<tableView.numberOfSections; section++) {
  266. totalCount += [tableView numberOfRowsInSection:section];
  267. }
  268. } else if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
  269. UICollectionView *collectionView = (UICollectionView *)self.scrollView;
  270. for (int section = 0; section<collectionView.numberOfSections; section++) {
  271. totalCount += [collectionView numberOfItemsInSection:section];
  272. }
  273. }
  274. return totalCount;
  275. }
  276. @end