MJRefreshBackFooter.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. //
  2. // MJRefreshBackFooter.m
  3. // MJRefreshExample
  4. //
  5. // Created by MJ Lee on 15/4/24.
  6. // Copyright (c) 2015年 小码哥. All rights reserved.
  7. //
  8. #import "MJRefreshBackFooter.h"
  9. @interface MJRefreshBackFooter()
  10. @property (assign, nonatomic) NSInteger lastRefreshCount;
  11. @property (assign, nonatomic) CGFloat lastBottomDelta;
  12. @end
  13. @implementation MJRefreshBackFooter
  14. #pragma mark - 初始化
  15. - (void)willMoveToSuperview:(UIView *)newSuperview
  16. {
  17. [super willMoveToSuperview:newSuperview];
  18. [self scrollViewContentSizeDidChange:nil];
  19. }
  20. #pragma mark - 实现父类的方法
  21. - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
  22. {
  23. [super scrollViewContentOffsetDidChange:change];
  24. // 如果正在刷新,直接返回
  25. if (self.state == MJRefreshStateRefreshing) return;
  26. _scrollViewOriginalInset = self.scrollView.contentInset;
  27. // 当前的contentOffset
  28. CGFloat currentOffsetY = self.scrollView.mj_offsetY;
  29. // 尾部控件刚好出现的offsetY
  30. CGFloat happenOffsetY = [self happenOffsetY];
  31. // 如果是向下滚动到看不见尾部控件,直接返回
  32. if (currentOffsetY <= happenOffsetY) return;
  33. CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h;
  34. // 如果已全部加载,仅设置pullingPercent,然后返回
  35. if (self.state == MJRefreshStateNoMoreData) {
  36. self.pullingPercent = pullingPercent;
  37. return;
  38. }
  39. if (self.scrollView.isDragging) {
  40. self.pullingPercent = pullingPercent;
  41. // 普通 和 即将刷新 的临界点
  42. CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h;
  43. if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) {
  44. // 转为即将刷新状态
  45. self.state = MJRefreshStatePulling;
  46. } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) {
  47. // 转为普通状态
  48. self.state = MJRefreshStateIdle;
  49. }
  50. } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
  51. // 开始刷新
  52. [self beginRefreshing];
  53. } else if (pullingPercent < 1) {
  54. self.pullingPercent = pullingPercent;
  55. }
  56. }
  57. - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
  58. {
  59. [super scrollViewContentSizeDidChange:change];
  60. // 内容的高度
  61. CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
  62. // 表格的高度
  63. CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
  64. // 设置位置和尺寸
  65. self.mj_y = MAX(contentHeight, scrollHeight);
  66. }
  67. - (void)setState:(MJRefreshState)state
  68. {
  69. MJRefreshCheckState
  70. // 根据状态来设置属性
  71. if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
  72. // 刷新完毕
  73. if (MJRefreshStateRefreshing == oldState) {
  74. [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
  75. self.scrollView.mj_insetB -= self.lastBottomDelta;
  76. // 自动调整透明度
  77. if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
  78. } completion:^(BOOL finished) {
  79. self.pullingPercent = 0.0;
  80. }];
  81. }
  82. CGFloat deltaH = [self heightForContentBreakView];
  83. // 刚刷新完毕
  84. if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
  85. self.scrollView.mj_offsetY = self.scrollView.mj_offsetY;
  86. }
  87. } else if (state == MJRefreshStateRefreshing) {
  88. // 记录刷新前的数量
  89. self.lastRefreshCount = self.scrollView.mj_totalDataCount;
  90. [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
  91. CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom;
  92. CGFloat deltaH = [self heightForContentBreakView];
  93. if (deltaH < 0) { // 如果内容高度小于view的高度
  94. bottom -= deltaH;
  95. }
  96. self.lastBottomDelta = bottom - self.scrollView.mj_insetB;
  97. self.scrollView.mj_insetB = bottom;
  98. self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h;
  99. } completion:^(BOOL finished) {
  100. [self executeRefreshingCallback];
  101. }];
  102. }
  103. }
  104. #pragma mark - 公共方法
  105. - (void)endRefreshing
  106. {
  107. if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
  108. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  109. [super endRefreshing];
  110. });
  111. } else {
  112. [super endRefreshing];
  113. }
  114. }
  115. - (void)noticeNoMoreData
  116. {
  117. if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
  118. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  119. [super noticeNoMoreData];
  120. });
  121. } else {
  122. [super noticeNoMoreData];
  123. }
  124. }
  125. #pragma mark - 私有方法
  126. #pragma mark 获得scrollView的内容 超出 view 的高度
  127. - (CGFloat)heightForContentBreakView
  128. {
  129. CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
  130. return self.scrollView.contentSize.height - h;
  131. }
  132. #pragma mark 刚好看到上拉刷新控件时的contentOffset.y
  133. - (CGFloat)happenOffsetY
  134. {
  135. CGFloat deltaH = [self heightForContentBreakView];
  136. if (deltaH > 0) {
  137. return deltaH - self.scrollViewOriginalInset.top;
  138. } else {
  139. return - self.scrollViewOriginalInset.top;
  140. }
  141. }
  142. @end