MJRefreshComponent.m 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // 代码地址: https://github.com/CoderMJLee/MJRefresh
  2. // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
  3. // MJRefreshComponent.m
  4. // MJRefreshExample
  5. //
  6. // Created by MJ Lee on 15/3/4.
  7. // Copyright (c) 2015年 小码哥. All rights reserved.
  8. //
  9. #import "MJRefreshComponent.h"
  10. #import "MJRefreshConst.h"
  11. @interface MJRefreshComponent()
  12. @property (strong, nonatomic) UIPanGestureRecognizer *pan;
  13. @end
  14. @implementation MJRefreshComponent
  15. #pragma mark - 初始化
  16. - (instancetype)initWithFrame:(CGRect)frame
  17. {
  18. if (self = [super initWithFrame:frame]) {
  19. // 准备工作
  20. [self prepare];
  21. // 默认是普通状态
  22. self.state = MJRefreshStateIdle;
  23. }
  24. return self;
  25. }
  26. - (void)prepare
  27. {
  28. // 基本属性
  29. self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  30. self.backgroundColor = [UIColor clearColor];
  31. }
  32. - (void)layoutSubviews
  33. {
  34. [self placeSubviews];
  35. [super layoutSubviews];
  36. }
  37. - (void)placeSubviews{}
  38. - (void)willMoveToSuperview:(UIView *)newSuperview
  39. {
  40. [super willMoveToSuperview:newSuperview];
  41. // 如果不是UIScrollView,不做任何事情
  42. if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
  43. // 旧的父控件移除监听
  44. [self removeObservers];
  45. if (newSuperview) { // 新的父控件
  46. // 记录UIScrollView
  47. _scrollView = (UIScrollView *)newSuperview;
  48. // 设置宽度
  49. self.mj_w = _scrollView.mj_w;
  50. // 设置位置
  51. self.mj_x = -_scrollView.mj_insetL;
  52. // 设置永远支持垂直弹簧效果
  53. _scrollView.alwaysBounceVertical = YES;
  54. // 记录UIScrollView最开始的contentInset
  55. _scrollViewOriginalInset = _scrollView.mj_inset;
  56. // 添加监听
  57. [self addObservers];
  58. }
  59. }
  60. - (void)drawRect:(CGRect)rect
  61. {
  62. [super drawRect:rect];
  63. if (self.state == MJRefreshStateWillRefresh) {
  64. // 预防view还没显示出来就调用了beginRefreshing
  65. self.state = MJRefreshStateRefreshing;
  66. }
  67. }
  68. #pragma mark - KVO监听
  69. - (void)addObservers
  70. {
  71. NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
  72. [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];
  73. [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
  74. self.pan = self.scrollView.panGestureRecognizer;
  75. [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
  76. }
  77. - (void)removeObservers
  78. {
  79. [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset];
  80. [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];
  81. [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState];
  82. self.pan = nil;
  83. }
  84. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  85. {
  86. // 遇到这些情况就直接返回
  87. if (!self.userInteractionEnabled) return;
  88. // 这个就算看不见也需要处理
  89. if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) {
  90. [self scrollViewContentSizeDidChange:change];
  91. }
  92. // 看不见
  93. if (self.hidden) return;
  94. if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) {
  95. [self scrollViewContentOffsetDidChange:change];
  96. } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) {
  97. [self scrollViewPanStateDidChange:change];
  98. }
  99. }
  100. - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
  101. - (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
  102. - (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
  103. #pragma mark - 公共方法
  104. #pragma mark 设置回调对象和回调方法
  105. - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action
  106. {
  107. self.refreshingTarget = target;
  108. self.refreshingAction = action;
  109. }
  110. - (void)setState:(MJRefreshState)state
  111. {
  112. _state = state;
  113. // 加入主队列的目的是等setState:方法调用完毕、设置完文字后再去布局子控件
  114. MJRefreshDispatchAsyncOnMainQueue([self setNeedsLayout];)
  115. }
  116. #pragma mark 进入刷新状态
  117. - (void)beginRefreshing
  118. {
  119. [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
  120. self.alpha = 1.0;
  121. }];
  122. self.pullingPercent = 1.0;
  123. // 只要正在刷新,就完全显示
  124. if (self.window) {
  125. self.state = MJRefreshStateRefreshing;
  126. } else {
  127. // 预防正在刷新中时,调用本方法使得header inset回置失败
  128. if (self.state != MJRefreshStateRefreshing) {
  129. self.state = MJRefreshStateWillRefresh;
  130. // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下)
  131. [self setNeedsDisplay];
  132. }
  133. }
  134. }
  135. - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock
  136. {
  137. self.beginRefreshingCompletionBlock = completionBlock;
  138. [self beginRefreshing];
  139. }
  140. #pragma mark 结束刷新状态
  141. - (void)endRefreshing
  142. {
  143. MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;)
  144. }
  145. - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock
  146. {
  147. self.endRefreshingCompletionBlock = completionBlock;
  148. [self endRefreshing];
  149. }
  150. #pragma mark 是否正在刷新
  151. - (BOOL)isRefreshing
  152. {
  153. return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh;
  154. }
  155. #pragma mark 自动切换透明度
  156. - (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha
  157. {
  158. self.automaticallyChangeAlpha = autoChangeAlpha;
  159. }
  160. - (BOOL)isAutoChangeAlpha
  161. {
  162. return self.isAutomaticallyChangeAlpha;
  163. }
  164. - (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha
  165. {
  166. _automaticallyChangeAlpha = automaticallyChangeAlpha;
  167. if (self.isRefreshing) return;
  168. if (automaticallyChangeAlpha) {
  169. self.alpha = self.pullingPercent;
  170. } else {
  171. self.alpha = 1.0;
  172. }
  173. }
  174. #pragma mark 根据拖拽进度设置透明度
  175. - (void)setPullingPercent:(CGFloat)pullingPercent
  176. {
  177. _pullingPercent = pullingPercent;
  178. if (self.isRefreshing) return;
  179. if (self.isAutomaticallyChangeAlpha) {
  180. self.alpha = pullingPercent;
  181. }
  182. }
  183. #pragma mark - 内部方法
  184. - (void)executeRefreshingCallback
  185. {
  186. MJRefreshDispatchAsyncOnMainQueue({
  187. if (self.refreshingBlock) {
  188. self.refreshingBlock();
  189. }
  190. if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
  191. MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
  192. }
  193. if (self.beginRefreshingCompletionBlock) {
  194. self.beginRefreshingCompletionBlock();
  195. }
  196. })
  197. }
  198. #pragma mark - <<< Deprecation compatible function >>> -
  199. - (void)setEndRefreshingAnimateCompletionBlock:(MJRefreshComponentEndRefreshingCompletionBlock)endRefreshingAnimateCompletionBlock {
  200. _endRefreshingAnimationBeginAction = endRefreshingAnimateCompletionBlock;
  201. }
  202. @end
  203. @implementation UILabel(MJRefresh)
  204. + (instancetype)mj_label
  205. {
  206. UILabel *label = [[self alloc] init];
  207. label.font = MJRefreshLabelFont;
  208. label.textColor = MJRefreshLabelTextColor;
  209. label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  210. label.textAlignment = NSTextAlignmentCenter;
  211. label.backgroundColor = [UIColor clearColor];
  212. return label;
  213. }
  214. - (CGFloat)mj_textWidth {
  215. CGFloat stringWidth = 0;
  216. CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT);
  217. if (self.attributedText) {
  218. if (self.attributedText.length == 0) { return 0; }
  219. stringWidth = [self.attributedText boundingRectWithSize:size
  220. options:NSStringDrawingUsesLineFragmentOrigin
  221. context:nil].size.width;
  222. } else {
  223. if (self.text.length == 0) { return 0; }
  224. NSAssert(self.font != nil, @"请检查 mj_label's `font` 是否设置正确");
  225. stringWidth = [self.text boundingRectWithSize:size
  226. options:NSStringDrawingUsesLineFragmentOrigin
  227. attributes:@{NSFontAttributeName:self.font}
  228. context:nil].size.width;
  229. }
  230. return stringWidth;
  231. }
  232. @end