MJRefreshTrailer.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // MJRefreshTrailer.m
  3. // MJRefresh
  4. //
  5. // Created by kinarobin on 2020/5/3.
  6. // Copyright © 2020 小码哥. All rights reserved.
  7. //
  8. #import "MJRefreshTrailer.h"
  9. @interface MJRefreshTrailer()
  10. @property (assign, nonatomic) NSInteger lastRefreshCount;
  11. @property (assign, nonatomic) CGFloat lastRightDelta;
  12. @end
  13. @implementation MJRefreshTrailer
  14. #pragma mark - 构造方法
  15. + (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock {
  16. MJRefreshTrailer *cmp = [[self alloc] init];
  17. cmp.refreshingBlock = refreshingBlock;
  18. return cmp;
  19. }
  20. + (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action {
  21. MJRefreshTrailer *cmp = [[self alloc] init];
  22. [cmp setRefreshingTarget:target refreshingAction:action];
  23. return cmp;
  24. }
  25. - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change {
  26. [super scrollViewContentOffsetDidChange:change];
  27. // 如果正在刷新,直接返回
  28. if (self.state == MJRefreshStateRefreshing) return;
  29. _scrollViewOriginalInset = self.scrollView.mj_inset;
  30. // 当前的contentOffset
  31. CGFloat currentOffsetX = self.scrollView.mj_offsetX;
  32. // 尾部控件刚好出现的offsetX
  33. CGFloat happenOffsetX = [self happenOffsetX];
  34. // 如果是向右滚动到看不见右边控件,直接返回
  35. if (currentOffsetX <= happenOffsetX) return;
  36. CGFloat pullingPercent = (currentOffsetX - happenOffsetX) / self.mj_w;
  37. // 如果已全部加载,仅设置pullingPercent,然后返回
  38. if (self.state == MJRefreshStateNoMoreData) {
  39. self.pullingPercent = pullingPercent;
  40. return;
  41. }
  42. if (self.scrollView.isDragging) {
  43. self.pullingPercent = pullingPercent;
  44. // 普通 和 即将刷新 的临界点
  45. CGFloat normal2pullingOffsetX = happenOffsetX + self.mj_w;
  46. if (self.state == MJRefreshStateIdle && currentOffsetX > normal2pullingOffsetX) {
  47. self.state = MJRefreshStatePulling;
  48. } else if (self.state == MJRefreshStatePulling && currentOffsetX <= normal2pullingOffsetX) {
  49. // 转为普通状态
  50. self.state = MJRefreshStateIdle;
  51. }
  52. } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
  53. // 开始刷新
  54. [self beginRefreshing];
  55. } else if (pullingPercent < 1) {
  56. self.pullingPercent = pullingPercent;
  57. }
  58. }
  59. - (void)setState:(MJRefreshState)state {
  60. MJRefreshCheckState
  61. // 根据状态来设置属性
  62. if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
  63. // 刷新完毕
  64. if (MJRefreshStateRefreshing == oldState) {
  65. [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
  66. if (self.endRefreshingAnimationBeginAction) {
  67. self.endRefreshingAnimationBeginAction();
  68. }
  69. self.scrollView.mj_insetR -= self.lastRightDelta;
  70. // 自动调整透明度
  71. if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
  72. } completion:^(BOOL finished) {
  73. self.pullingPercent = 0.0;
  74. if (self.endRefreshingCompletionBlock) {
  75. self.endRefreshingCompletionBlock();
  76. }
  77. }];
  78. }
  79. CGFloat deltaW = [self widthForContentBreakView];
  80. // 刚刷新完毕
  81. if (MJRefreshStateRefreshing == oldState && deltaW > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
  82. self.scrollView.mj_offsetX = self.scrollView.mj_offsetX;
  83. }
  84. } else if (state == MJRefreshStateRefreshing) {
  85. // 记录刷新前的数量
  86. self.lastRefreshCount = self.scrollView.mj_totalDataCount;
  87. [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
  88. CGFloat right = self.mj_w + self.scrollViewOriginalInset.right;
  89. CGFloat deltaW = [self widthForContentBreakView];
  90. if (deltaW < 0) { // 如果内容宽度小于view的宽度
  91. right -= deltaW;
  92. }
  93. self.lastRightDelta = right - self.scrollView.mj_insetR;
  94. self.scrollView.mj_insetR = right;
  95. // 设置滚动位置
  96. CGPoint offset = self.scrollView.contentOffset;
  97. offset.x = [self happenOffsetX] + self.mj_w;
  98. [self.scrollView setContentOffset:offset animated:NO];
  99. } completion:^(BOOL finished) {
  100. [self executeRefreshingCallback];
  101. }];
  102. }
  103. }
  104. - (void)scrollViewContentSizeDidChange:(NSDictionary *)change {
  105. [super scrollViewContentSizeDidChange:change];
  106. // 内容的宽度
  107. CGFloat contentWidth = self.scrollView.mj_contentW + self.ignoredScrollViewContentInsetRight;
  108. // 表格的宽度
  109. CGFloat scrollWidth = self.scrollView.mj_w - self.scrollViewOriginalInset.left - self.scrollViewOriginalInset.right + self.ignoredScrollViewContentInsetRight;
  110. // 设置位置和尺寸
  111. self.mj_x = MAX(contentWidth, scrollWidth);
  112. }
  113. - (void)placeSubviews {
  114. [super placeSubviews];
  115. self.mj_h = _scrollView.mj_h;
  116. // 设置自己的宽度
  117. self.mj_w = MJRefreshTrailWidth;
  118. }
  119. - (void)willMoveToSuperview:(UIView *)newSuperview {
  120. [super willMoveToSuperview:newSuperview];
  121. if (newSuperview) {
  122. // 设置支持水平弹簧效果
  123. _scrollView.alwaysBounceHorizontal = YES;
  124. _scrollView.alwaysBounceVertical = NO;
  125. }
  126. }
  127. #pragma mark 刚好看到上拉刷新控件时的contentOffset.x
  128. - (CGFloat)happenOffsetX {
  129. CGFloat deltaW = [self widthForContentBreakView];
  130. if (deltaW > 0) {
  131. return deltaW - self.scrollViewOriginalInset.left;
  132. } else {
  133. return - self.scrollViewOriginalInset.left;
  134. }
  135. }
  136. #pragma mark 获得scrollView的内容 超出 view 的宽度
  137. - (CGFloat)widthForContentBreakView {
  138. CGFloat w = self.scrollView.frame.size.width - self.scrollViewOriginalInset.right - self.scrollViewOriginalInset.left;
  139. return self.scrollView.contentSize.width - w;
  140. }
  141. @end