initial
This commit is contained in:
34
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h
generated
Normal file
34
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// MJRefreshAutoFooter.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/4/24.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#if __has_include(<MJRefresh/MJRefreshFooter.h>)
|
||||
#import <MJRefresh/MJRefreshFooter.h>
|
||||
#else
|
||||
#import "MJRefreshFooter.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MJRefreshAutoFooter : MJRefreshFooter
|
||||
/** 是否自动刷新(默认为YES) */
|
||||
@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh;
|
||||
|
||||
/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
|
||||
@property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用triggerAutomaticallyRefreshPercent属性");
|
||||
|
||||
/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
|
||||
@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;
|
||||
|
||||
/** 自动触发次数, 默认为 1, 仅在拖拽 ScrollView 时才生效,
|
||||
|
||||
如果为 -1, 则为无限触发
|
||||
*/
|
||||
@property (nonatomic) NSInteger autoTriggerTimes;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
216
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m
generated
Normal file
216
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m
generated
Normal file
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// MJRefreshAutoFooter.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/4/24.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshAutoFooter.h"
|
||||
#import "NSBundle+MJRefresh.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
#import "UIScrollView+MJExtension.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
|
||||
@interface MJRefreshAutoFooter()
|
||||
/** 一个新的拖拽 */
|
||||
@property (nonatomic) BOOL triggerByDrag;
|
||||
@property (nonatomic) NSInteger leftTriggerTimes;
|
||||
@end
|
||||
|
||||
@implementation MJRefreshAutoFooter
|
||||
|
||||
#pragma mark - 初始化
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview
|
||||
{
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
if (newSuperview) { // 新的父控件
|
||||
if (self.hidden == NO) {
|
||||
self.scrollView.mj_insetB += self.mj_h;
|
||||
}
|
||||
|
||||
// 设置位置
|
||||
self.mj_y = _scrollView.mj_contentH;
|
||||
} else { // 被移除了
|
||||
if (self.hidden == NO) {
|
||||
self.scrollView.mj_insetB -= self.mj_h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 过期方法
|
||||
- (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh
|
||||
{
|
||||
self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh;
|
||||
}
|
||||
|
||||
- (CGFloat)appearencePercentTriggerAutoRefresh
|
||||
{
|
||||
return self.triggerAutomaticallyRefreshPercent;
|
||||
}
|
||||
|
||||
#pragma mark - 实现父类的方法
|
||||
- (void)prepare
|
||||
{
|
||||
[super prepare];
|
||||
|
||||
// 默认底部控件100%出现时才会自动刷新
|
||||
self.triggerAutomaticallyRefreshPercent = 1.0;
|
||||
|
||||
// 设置为默认状态
|
||||
self.automaticallyRefresh = YES;
|
||||
|
||||
self.autoTriggerTimes = 1;
|
||||
}
|
||||
|
||||
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewContentSizeDidChange:change];
|
||||
|
||||
CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue];
|
||||
CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height;
|
||||
// 设置位置
|
||||
CGFloat y = contentHeight + self.ignoredScrollViewContentInsetBottom;
|
||||
if (self.mj_y != y) {
|
||||
self.mj_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewContentOffsetDidChange:change];
|
||||
|
||||
if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return;
|
||||
|
||||
if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕
|
||||
// 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理
|
||||
if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) {
|
||||
// 防止手松开时连续调用
|
||||
CGPoint old = [change[@"old"] CGPointValue];
|
||||
CGPoint new = [change[@"new"] CGPointValue];
|
||||
if (new.y <= old.y) return;
|
||||
|
||||
if (_scrollView.isDragging) {
|
||||
self.triggerByDrag = YES;
|
||||
}
|
||||
// 当底部刷新控件完全出现时,才刷新
|
||||
[self beginRefreshing];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewPanStateDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewPanStateDidChange:change];
|
||||
|
||||
if (self.state != MJRefreshStateIdle) return;
|
||||
|
||||
UIGestureRecognizerState panState = _scrollView.panGestureRecognizer.state;
|
||||
|
||||
switch (panState) {
|
||||
// 手松开
|
||||
case UIGestureRecognizerStateEnded: {
|
||||
if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕
|
||||
if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
|
||||
self.triggerByDrag = YES;
|
||||
[self beginRefreshing];
|
||||
}
|
||||
} else { // 超出一个屏幕
|
||||
if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
|
||||
self.triggerByDrag = YES;
|
||||
[self beginRefreshing];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UIGestureRecognizerStateBegan: {
|
||||
[self resetTriggerTimes];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unlimitedTrigger {
|
||||
return self.leftTriggerTimes == -1;
|
||||
}
|
||||
|
||||
- (void)beginRefreshing
|
||||
{
|
||||
if (self.triggerByDrag && self.leftTriggerTimes <= 0 && !self.unlimitedTrigger) {
|
||||
return;
|
||||
}
|
||||
|
||||
[super beginRefreshing];
|
||||
}
|
||||
|
||||
- (void)setState:(MJRefreshState)state
|
||||
{
|
||||
MJRefreshCheckState
|
||||
|
||||
if (state == MJRefreshStateRefreshing) {
|
||||
[self executeRefreshingCallback];
|
||||
} else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
|
||||
if (self.triggerByDrag) {
|
||||
if (!self.unlimitedTrigger) {
|
||||
self.leftTriggerTimes -= 1;
|
||||
}
|
||||
self.triggerByDrag = NO;
|
||||
}
|
||||
|
||||
if (MJRefreshStateRefreshing == oldState) {
|
||||
if (self.scrollView.pagingEnabled) {
|
||||
CGPoint offset = self.scrollView.contentOffset;
|
||||
offset.y -= self.scrollView.mj_insetB;
|
||||
[UIView animateWithDuration:self.slowAnimationDuration animations:^{
|
||||
self.scrollView.contentOffset = offset;
|
||||
|
||||
if (self.endRefreshingAnimationBeginAction) {
|
||||
self.endRefreshingAnimationBeginAction();
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetTriggerTimes {
|
||||
self.leftTriggerTimes = self.autoTriggerTimes;
|
||||
}
|
||||
|
||||
- (void)setHidden:(BOOL)hidden
|
||||
{
|
||||
BOOL lastHidden = self.isHidden;
|
||||
|
||||
[super setHidden:hidden];
|
||||
|
||||
if (!lastHidden && hidden) {
|
||||
self.state = MJRefreshStateIdle;
|
||||
|
||||
self.scrollView.mj_insetB -= self.mj_h;
|
||||
} else if (lastHidden && !hidden) {
|
||||
self.scrollView.mj_insetB += self.mj_h;
|
||||
|
||||
// 设置位置
|
||||
self.mj_y = _scrollView.mj_contentH;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAutoTriggerTimes:(NSInteger)autoTriggerTimes {
|
||||
_autoTriggerTimes = autoTriggerTimes;
|
||||
self.leftTriggerTimes = autoTriggerTimes;
|
||||
}
|
||||
@end
|
||||
21
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h
generated
Normal file
21
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// MJRefreshBackFooter.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/4/24.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#if __has_include(<MJRefresh/MJRefreshFooter.h>)
|
||||
#import <MJRefresh/MJRefreshFooter.h>
|
||||
#else
|
||||
#import "MJRefreshFooter.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MJRefreshBackFooter : MJRefreshFooter
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
158
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m
generated
Normal file
158
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m
generated
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// MJRefreshBackFooter.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/4/24.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshBackFooter.h"
|
||||
#import "NSBundle+MJRefresh.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
#import "UIScrollView+MJExtension.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
|
||||
@interface MJRefreshBackFooter()
|
||||
@property (assign, nonatomic) NSInteger lastRefreshCount;
|
||||
@property (assign, nonatomic) CGFloat lastBottomDelta;
|
||||
@end
|
||||
|
||||
@implementation MJRefreshBackFooter
|
||||
|
||||
#pragma mark - 初始化
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview
|
||||
{
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
[self scrollViewContentSizeDidChange:nil];
|
||||
}
|
||||
|
||||
#pragma mark - 实现父类的方法
|
||||
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewContentOffsetDidChange:change];
|
||||
|
||||
// 如果正在刷新,直接返回
|
||||
if (self.state == MJRefreshStateRefreshing) return;
|
||||
|
||||
_scrollViewOriginalInset = self.scrollView.mj_inset;
|
||||
|
||||
// 当前的contentOffset
|
||||
CGFloat currentOffsetY = self.scrollView.mj_offsetY;
|
||||
// 尾部控件刚好出现的offsetY
|
||||
CGFloat happenOffsetY = [self happenOffsetY];
|
||||
// 如果是向下滚动到看不见尾部控件,直接返回
|
||||
if (currentOffsetY <= happenOffsetY) return;
|
||||
|
||||
CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h;
|
||||
|
||||
// 如果已全部加载,仅设置pullingPercent,然后返回
|
||||
if (self.state == MJRefreshStateNoMoreData) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.scrollView.isDragging) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
// 普通 和 即将刷新 的临界点
|
||||
CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h;
|
||||
|
||||
if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) {
|
||||
// 转为即将刷新状态
|
||||
self.state = MJRefreshStatePulling;
|
||||
} else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) {
|
||||
// 转为普通状态
|
||||
self.state = MJRefreshStateIdle;
|
||||
}
|
||||
} else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
|
||||
// 开始刷新
|
||||
[self beginRefreshing];
|
||||
} else if (pullingPercent < 1) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewContentSizeDidChange:change];
|
||||
|
||||
CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue];
|
||||
CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height;
|
||||
// 内容的高度
|
||||
contentHeight += self.ignoredScrollViewContentInsetBottom;
|
||||
// 表格的高度
|
||||
CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
|
||||
// 设置位置
|
||||
CGFloat y = MAX(contentHeight, scrollHeight);
|
||||
if (self.mj_y != y) {
|
||||
self.mj_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setState:(MJRefreshState)state
|
||||
{
|
||||
MJRefreshCheckState
|
||||
|
||||
// 根据状态来设置属性
|
||||
if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
|
||||
// 刷新完毕
|
||||
if (MJRefreshStateRefreshing == oldState) {
|
||||
[UIView animateWithDuration:self.slowAnimationDuration animations:^{
|
||||
if (self.endRefreshingAnimationBeginAction) {
|
||||
self.endRefreshingAnimationBeginAction();
|
||||
}
|
||||
|
||||
self.scrollView.mj_insetB -= self.lastBottomDelta;
|
||||
// 自动调整透明度
|
||||
if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
|
||||
} completion:^(BOOL finished) {
|
||||
self.pullingPercent = 0.0;
|
||||
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
CGFloat deltaH = [self heightForContentBreakView];
|
||||
// 刚刷新完毕
|
||||
if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
|
||||
self.scrollView.mj_offsetY = self.scrollView.mj_offsetY;
|
||||
}
|
||||
} else if (state == MJRefreshStateRefreshing) {
|
||||
// 记录刷新前的数量
|
||||
self.lastRefreshCount = self.scrollView.mj_totalDataCount;
|
||||
|
||||
[UIView animateWithDuration:self.fastAnimationDuration animations:^{
|
||||
CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom;
|
||||
CGFloat deltaH = [self heightForContentBreakView];
|
||||
if (deltaH < 0) { // 如果内容高度小于view的高度
|
||||
bottom -= deltaH;
|
||||
}
|
||||
self.lastBottomDelta = bottom - self.scrollView.mj_insetB;
|
||||
self.scrollView.mj_insetB = bottom;
|
||||
self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h;
|
||||
} completion:^(BOOL finished) {
|
||||
[self executeRefreshingCallback];
|
||||
}];
|
||||
}
|
||||
}
|
||||
#pragma mark - 私有方法
|
||||
#pragma mark 获得scrollView的内容 超出 view 的高度
|
||||
- (CGFloat)heightForContentBreakView
|
||||
{
|
||||
CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
|
||||
return self.scrollView.contentSize.height - h;
|
||||
}
|
||||
|
||||
#pragma mark 刚好看到上拉刷新控件时的contentOffset.y
|
||||
- (CGFloat)happenOffsetY
|
||||
{
|
||||
CGFloat deltaH = [self heightForContentBreakView];
|
||||
if (deltaH > 0) {
|
||||
return deltaH - self.scrollViewOriginalInset.top;
|
||||
} else {
|
||||
return - self.scrollViewOriginalInset.top;
|
||||
}
|
||||
}
|
||||
@end
|
||||
151
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h
generated
Normal file
151
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h
generated
Normal file
@@ -0,0 +1,151 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshComponent.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/4.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
// 刷新控件的基类
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#if __has_include(<MJRefresh/MJRefreshConst.h>)
|
||||
#import <MJRefresh/MJRefreshConst.h>
|
||||
#else
|
||||
#import "MJRefreshConst.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** 刷新控件的状态 */
|
||||
typedef NS_ENUM(NSInteger, MJRefreshState) {
|
||||
/** 普通闲置状态 */
|
||||
MJRefreshStateIdle = 1,
|
||||
/** 松开就可以进行刷新的状态 */
|
||||
MJRefreshStatePulling,
|
||||
/** 正在刷新中的状态 */
|
||||
MJRefreshStateRefreshing,
|
||||
/** 即将刷新的状态 */
|
||||
MJRefreshStateWillRefresh,
|
||||
/** 所有数据加载完毕,没有更多的数据了 */
|
||||
MJRefreshStateNoMoreData
|
||||
};
|
||||
|
||||
/** 进入刷新状态的回调 */
|
||||
typedef void (^MJRefreshComponentRefreshingBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
|
||||
/** 开始刷新后的回调(进入刷新状态后的回调) */
|
||||
typedef void (^MJRefreshComponentBeginRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
|
||||
/** 结束刷新后的回调 */
|
||||
typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
|
||||
|
||||
/** 刷新用到的回调类型 */
|
||||
typedef void (^MJRefreshComponentAction)(void);
|
||||
|
||||
/** 刷新控件的基类 */
|
||||
@interface MJRefreshComponent : UIView
|
||||
{
|
||||
/** 记录scrollView刚开始的inset */
|
||||
UIEdgeInsets _scrollViewOriginalInset;
|
||||
/** 父控件 */
|
||||
__weak UIScrollView *_scrollView;
|
||||
}
|
||||
|
||||
#pragma mark - 刷新动画时间控制
|
||||
/** 快速动画时间(一般用在刷新开始的回弹动画), 默认 0.25 */
|
||||
@property (nonatomic) NSTimeInterval fastAnimationDuration;
|
||||
/** 慢速动画时间(一般用在刷新结束后的回弹动画), 默认 0.4*/
|
||||
@property (nonatomic) NSTimeInterval slowAnimationDuration;
|
||||
/** 关闭全部默认动画效果, 可以简单粗暴地解决 CollectionView 的回弹动画 bug */
|
||||
- (instancetype)setAnimationDisabled;
|
||||
|
||||
#pragma mark - 刷新回调
|
||||
/** 正在刷新的回调 */
|
||||
@property (copy, nonatomic, nullable) MJRefreshComponentAction refreshingBlock;
|
||||
/** 设置回调对象和回调方法 */
|
||||
- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;
|
||||
|
||||
/** 回调对象 */
|
||||
@property (weak, nonatomic) id refreshingTarget;
|
||||
/** 回调方法 */
|
||||
@property (assign, nonatomic) SEL refreshingAction;
|
||||
/** 触发回调(交给子类去调用) */
|
||||
- (void)executeRefreshingCallback;
|
||||
|
||||
#pragma mark - 刷新状态控制
|
||||
/** 进入刷新状态 */
|
||||
- (void)beginRefreshing;
|
||||
- (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
|
||||
/** 开始刷新后的回调(进入刷新状态后的回调) */
|
||||
@property (copy, nonatomic, nullable) MJRefreshComponentAction beginRefreshingCompletionBlock;
|
||||
/** 带动画的结束刷新的回调 */
|
||||
@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimateCompletionBlock MJRefreshDeprecated("first deprecated in 3.3.0 - Use `endRefreshingAnimationBeginAction` instead");
|
||||
@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimationBeginAction;
|
||||
/** 结束刷新的回调 */
|
||||
@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingCompletionBlock;
|
||||
/** 结束刷新状态 */
|
||||
- (void)endRefreshing;
|
||||
- (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
|
||||
/** 是否正在刷新 */
|
||||
@property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
|
||||
|
||||
/** 刷新状态 一般交给子类内部实现 */
|
||||
@property (assign, nonatomic) MJRefreshState state;
|
||||
|
||||
#pragma mark - 交给子类去访问
|
||||
/** 记录scrollView刚开始的inset */
|
||||
@property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset;
|
||||
/** 父控件 */
|
||||
@property (weak, nonatomic, readonly) UIScrollView *scrollView;
|
||||
|
||||
#pragma mark - 交给子类们去实现
|
||||
/** 初始化 */
|
||||
- (void)prepare NS_REQUIRES_SUPER;
|
||||
/** 摆放子控件frame */
|
||||
- (void)placeSubviews NS_REQUIRES_SUPER;
|
||||
/** 当scrollView的contentOffset发生改变的时候调用 */
|
||||
- (void)scrollViewContentOffsetDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
|
||||
/** 当scrollView的contentSize发生改变的时候调用 */
|
||||
- (void)scrollViewContentSizeDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
|
||||
/** 当scrollView的拖拽状态发生改变的时候调用 */
|
||||
- (void)scrollViewPanStateDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
|
||||
|
||||
/** 多语言配置 language 发生变化时调用
|
||||
|
||||
`MJRefreshConfig.defaultConfig.language` 发生改变时调用.
|
||||
|
||||
⚠️ 父类会调用 `placeSubviews` 方法, 请勿在 placeSubviews 中调用本方法, 造成死循环. 子类在需要重新布局时, 在配置完修改后, 最后再调用 super 方法, 否则可能导致配置修改后, 定位先于修改执行.
|
||||
*/
|
||||
- (void)i18nDidChange NS_REQUIRES_SUPER;
|
||||
|
||||
#pragma mark - 其他
|
||||
/** 拉拽的百分比(交给子类重写) */
|
||||
@property (assign, nonatomic) CGFloat pullingPercent;
|
||||
/** 根据拖拽比例自动切换透明度 */
|
||||
@property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性");
|
||||
/** 根据拖拽比例自动切换透明度 */
|
||||
@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;
|
||||
@end
|
||||
|
||||
@interface UILabel(MJRefresh)
|
||||
+ (instancetype)mj_label;
|
||||
- (CGFloat)mj_textWidth;
|
||||
@end
|
||||
|
||||
@interface MJRefreshComponent (ChainingGrammar)
|
||||
|
||||
#pragma mark - <<< 为 Swift 扩展链式语法 >>> -
|
||||
/// 自动变化透明度
|
||||
- (instancetype)autoChangeTransparency:(BOOL)isAutoChange;
|
||||
/// 刷新开始后立即调用的回调
|
||||
- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action;
|
||||
/// 刷新动画开始后立即调用的回调
|
||||
- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action;
|
||||
/// 刷新结束后立即调用的回调
|
||||
- (instancetype)afterEndingAction:(MJRefreshComponentAction)action;
|
||||
|
||||
|
||||
/// 需要子类必须实现
|
||||
/// @param scrollView 赋值给的 ScrollView 的 Header/Footer/Trailer
|
||||
- (instancetype)linkTo:(UIScrollView *)scrollView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
323
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m
generated
Normal file
323
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m
generated
Normal file
@@ -0,0 +1,323 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshComponent.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/4.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshComponent.h"
|
||||
#import "MJRefreshConst.h"
|
||||
#import "MJRefreshConfig.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
#import "UIScrollView+MJExtension.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
#import "NSBundle+MJRefresh.h"
|
||||
|
||||
@interface MJRefreshComponent()
|
||||
@property (strong, nonatomic) UIPanGestureRecognizer *pan;
|
||||
@end
|
||||
|
||||
@implementation MJRefreshComponent
|
||||
#pragma mark - 初始化
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
// 准备工作
|
||||
[self prepare];
|
||||
|
||||
// 默认是普通状态
|
||||
self.state = MJRefreshStateIdle;
|
||||
self.fastAnimationDuration = 0.25;
|
||||
self.slowAnimationDuration = 0.4;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)prepare
|
||||
{
|
||||
// 基本属性
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[self placeSubviews];
|
||||
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)placeSubviews{}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview
|
||||
{
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
// 如果不是UIScrollView,不做任何事情
|
||||
if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
|
||||
|
||||
// 旧的父控件移除监听
|
||||
[self removeObservers];
|
||||
|
||||
if (newSuperview) { // 新的父控件
|
||||
// 记录UIScrollView
|
||||
_scrollView = (UIScrollView *)newSuperview;
|
||||
|
||||
// 设置宽度
|
||||
self.mj_w = _scrollView.mj_w;
|
||||
// 设置位置
|
||||
self.mj_x = -_scrollView.mj_insetL;
|
||||
|
||||
// 设置永远支持垂直弹簧效果
|
||||
_scrollView.alwaysBounceVertical = YES;
|
||||
// 记录UIScrollView最开始的contentInset
|
||||
_scrollViewOriginalInset = _scrollView.mj_inset;
|
||||
|
||||
// 添加监听
|
||||
[self addObservers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
[super drawRect:rect];
|
||||
|
||||
if (self.state == MJRefreshStateWillRefresh) {
|
||||
// 预防view还没显示出来就调用了beginRefreshing
|
||||
self.state = MJRefreshStateRefreshing;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - KVO监听
|
||||
- (void)addObservers
|
||||
{
|
||||
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
|
||||
[self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];
|
||||
[self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
|
||||
self.pan = self.scrollView.panGestureRecognizer;
|
||||
[self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(i18nDidChange) name:MJRefreshDidChangeLanguageNotification object:MJRefreshConfig.defaultConfig];
|
||||
}
|
||||
|
||||
- (void)removeObservers
|
||||
{
|
||||
[self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset];
|
||||
[self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];
|
||||
[self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState];
|
||||
self.pan = nil;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
// 遇到这些情况就直接返回
|
||||
if (!self.userInteractionEnabled) return;
|
||||
|
||||
// 这个就算看不见也需要处理
|
||||
if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) {
|
||||
[self scrollViewContentSizeDidChange:change];
|
||||
}
|
||||
|
||||
// 看不见
|
||||
if (self.hidden) return;
|
||||
if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) {
|
||||
[self scrollViewContentOffsetDidChange:change];
|
||||
} else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) {
|
||||
[self scrollViewPanStateDidChange:change];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
|
||||
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
|
||||
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
|
||||
|
||||
- (void)i18nDidChange {
|
||||
[self placeSubviews];
|
||||
}
|
||||
|
||||
#pragma mark - 公共方法
|
||||
#pragma mark 设置回调对象和回调方法
|
||||
- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action
|
||||
{
|
||||
self.refreshingTarget = target;
|
||||
self.refreshingAction = action;
|
||||
}
|
||||
|
||||
- (void)setState:(MJRefreshState)state
|
||||
{
|
||||
_state = state;
|
||||
|
||||
// 加入主队列的目的是等setState:方法调用完毕、设置完文字后再去布局子控件
|
||||
MJRefreshDispatchAsyncOnMainQueue([self setNeedsLayout];)
|
||||
}
|
||||
|
||||
#pragma mark 进入刷新状态
|
||||
- (void)beginRefreshing
|
||||
{
|
||||
[UIView animateWithDuration:self.fastAnimationDuration animations:^{
|
||||
self.alpha = 1.0;
|
||||
}];
|
||||
self.pullingPercent = 1.0;
|
||||
// 只要正在刷新,就完全显示
|
||||
if (self.window) {
|
||||
self.state = MJRefreshStateRefreshing;
|
||||
} else {
|
||||
// 预防正在刷新中时,调用本方法使得header inset回置失败
|
||||
if (self.state != MJRefreshStateRefreshing) {
|
||||
self.state = MJRefreshStateWillRefresh;
|
||||
// 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下)
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
self.beginRefreshingCompletionBlock = completionBlock;
|
||||
|
||||
[self beginRefreshing];
|
||||
}
|
||||
|
||||
#pragma mark 结束刷新状态
|
||||
- (void)endRefreshing
|
||||
{
|
||||
MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;)
|
||||
}
|
||||
|
||||
- (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
self.endRefreshingCompletionBlock = completionBlock;
|
||||
|
||||
[self endRefreshing];
|
||||
}
|
||||
|
||||
#pragma mark 是否正在刷新
|
||||
- (BOOL)isRefreshing
|
||||
{
|
||||
return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh;
|
||||
}
|
||||
|
||||
#pragma mark 自动切换透明度
|
||||
- (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha
|
||||
{
|
||||
self.automaticallyChangeAlpha = autoChangeAlpha;
|
||||
}
|
||||
|
||||
- (BOOL)isAutoChangeAlpha
|
||||
{
|
||||
return self.isAutomaticallyChangeAlpha;
|
||||
}
|
||||
|
||||
- (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha
|
||||
{
|
||||
_automaticallyChangeAlpha = automaticallyChangeAlpha;
|
||||
|
||||
if (self.isRefreshing) return;
|
||||
|
||||
if (automaticallyChangeAlpha) {
|
||||
self.alpha = self.pullingPercent;
|
||||
} else {
|
||||
self.alpha = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark 根据拖拽进度设置透明度
|
||||
- (void)setPullingPercent:(CGFloat)pullingPercent
|
||||
{
|
||||
_pullingPercent = pullingPercent;
|
||||
|
||||
if (self.isRefreshing) return;
|
||||
|
||||
if (self.isAutomaticallyChangeAlpha) {
|
||||
self.alpha = pullingPercent;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 内部方法
|
||||
- (void)executeRefreshingCallback
|
||||
{
|
||||
if (self.refreshingBlock) {
|
||||
self.refreshingBlock();
|
||||
}
|
||||
if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
|
||||
MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
|
||||
}
|
||||
if (self.beginRefreshingCompletionBlock) {
|
||||
self.beginRefreshingCompletionBlock();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 刷新动画时间控制
|
||||
- (instancetype)setAnimationDisabled {
|
||||
self.fastAnimationDuration = 0;
|
||||
self.slowAnimationDuration = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - <<< Deprecation compatible function >>> -
|
||||
- (void)setEndRefreshingAnimateCompletionBlock:(MJRefreshComponentEndRefreshingCompletionBlock)endRefreshingAnimateCompletionBlock {
|
||||
_endRefreshingAnimationBeginAction = endRefreshingAnimateCompletionBlock;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation UILabel(MJRefresh)
|
||||
+ (instancetype)mj_label
|
||||
{
|
||||
UILabel *label = [[self alloc] init];
|
||||
label.font = MJRefreshLabelFont;
|
||||
label.textColor = MJRefreshLabelTextColor;
|
||||
label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
label.backgroundColor = [UIColor clearColor];
|
||||
return label;
|
||||
}
|
||||
|
||||
- (CGFloat)mj_textWidth {
|
||||
CGFloat stringWidth = 0;
|
||||
CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT);
|
||||
|
||||
if (self.attributedText) {
|
||||
if (self.attributedText.length == 0) { return 0; }
|
||||
stringWidth = [self.attributedText boundingRectWithSize:size
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
context:nil].size.width;
|
||||
} else {
|
||||
if (self.text.length == 0) { return 0; }
|
||||
NSAssert(self.font != nil, @"请检查 mj_label's `font` 是否设置正确");
|
||||
stringWidth = [self.text boundingRectWithSize:size
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
attributes:@{NSFontAttributeName:self.font}
|
||||
context:nil].size.width;
|
||||
}
|
||||
return stringWidth;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - <<< 为 Swift 扩展链式语法 >>> -
|
||||
@implementation MJRefreshComponent (ChainingGrammar)
|
||||
|
||||
- (instancetype)autoChangeTransparency:(BOOL)isAutoChange {
|
||||
self.automaticallyChangeAlpha = isAutoChange;
|
||||
return self;
|
||||
}
|
||||
- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action {
|
||||
self.beginRefreshingCompletionBlock = action;
|
||||
return self;
|
||||
}
|
||||
- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action {
|
||||
self.endRefreshingAnimationBeginAction = action;
|
||||
return self;
|
||||
}
|
||||
- (instancetype)afterEndingAction:(MJRefreshComponentAction)action {
|
||||
self.endRefreshingCompletionBlock = action;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)linkTo:(UIScrollView *)scrollView {
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
37
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h
generated
Normal file
37
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshFooter.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/5.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
// 上拉刷新控件
|
||||
|
||||
#if __has_include(<MJRefresh/MJRefreshComponent.h>)
|
||||
#import <MJRefresh/MJRefreshComponent.h>
|
||||
#else
|
||||
#import "MJRefreshComponent.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MJRefreshFooter : MJRefreshComponent
|
||||
/** 创建footer */
|
||||
+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
|
||||
/** 创建footer */
|
||||
+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
|
||||
|
||||
/** 提示没有更多的数据 */
|
||||
- (void)endRefreshingWithNoMoreData;
|
||||
- (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData");
|
||||
|
||||
/** 重置没有更多的数据(消除没有更多数据的状态) */
|
||||
- (void)resetNoMoreData;
|
||||
|
||||
/** 忽略多少scrollView的contentInset的bottom */
|
||||
@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom;
|
||||
|
||||
/** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */
|
||||
@property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("已废弃此属性,开发者请自行控制footer的显示和隐藏");
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
71
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m
generated
Normal file
71
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m
generated
Normal file
@@ -0,0 +1,71 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshFooter.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/5.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshFooter.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
|
||||
@interface MJRefreshFooter()
|
||||
|
||||
@end
|
||||
|
||||
@implementation MJRefreshFooter
|
||||
#pragma mark - 构造方法
|
||||
+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock
|
||||
{
|
||||
MJRefreshFooter *cmp = [[self alloc] init];
|
||||
cmp.refreshingBlock = refreshingBlock;
|
||||
return cmp;
|
||||
}
|
||||
+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
|
||||
{
|
||||
MJRefreshFooter *cmp = [[self alloc] init];
|
||||
[cmp setRefreshingTarget:target refreshingAction:action];
|
||||
return cmp;
|
||||
}
|
||||
|
||||
#pragma mark - 重写父类的方法
|
||||
- (void)prepare
|
||||
{
|
||||
[super prepare];
|
||||
|
||||
// 设置自己的高度
|
||||
self.mj_h = MJRefreshFooterHeight;
|
||||
|
||||
// 默认不会自动隐藏
|
||||
// self.automaticallyHidden = NO;
|
||||
}
|
||||
|
||||
#pragma mark . 链式语法部分 .
|
||||
|
||||
- (instancetype)linkTo:(UIScrollView *)scrollView {
|
||||
scrollView.mj_footer = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - 公共方法
|
||||
- (void)endRefreshingWithNoMoreData
|
||||
{
|
||||
MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateNoMoreData;)
|
||||
}
|
||||
|
||||
- (void)noticeNoMoreData
|
||||
{
|
||||
[self endRefreshingWithNoMoreData];
|
||||
}
|
||||
|
||||
- (void)resetNoMoreData
|
||||
{
|
||||
MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;)
|
||||
}
|
||||
|
||||
- (void)setAutomaticallyHidden:(BOOL)automaticallyHidden
|
||||
{
|
||||
_automaticallyHidden = automaticallyHidden;
|
||||
}
|
||||
@end
|
||||
35
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h
generated
Normal file
35
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshHeader.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/4.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
// 下拉刷新控件:负责监控用户下拉的状态
|
||||
|
||||
#if __has_include(<MJRefresh/MJRefreshComponent.h>)
|
||||
#import <MJRefresh/MJRefreshComponent.h>
|
||||
#else
|
||||
#import "MJRefreshComponent.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MJRefreshHeader : MJRefreshComponent
|
||||
/** 创建header */
|
||||
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
|
||||
/** 创建header */
|
||||
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
|
||||
|
||||
/** 这个key用来存储上一次下拉刷新成功的时间 */
|
||||
@property (copy, nonatomic) NSString *lastUpdatedTimeKey;
|
||||
/** 上一次下拉刷新成功的时间 */
|
||||
@property (strong, nonatomic, readonly, nullable) NSDate *lastUpdatedTime;
|
||||
|
||||
/** 忽略多少scrollView的contentInset的top */
|
||||
@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop;
|
||||
|
||||
/** 默认是关闭状态, 如果遇到 CollectionView 的动画异常问题可以尝试打开 */
|
||||
@property (nonatomic) BOOL isCollectionViewAnimationBug;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
297
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m
generated
Normal file
297
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m
generated
Normal file
@@ -0,0 +1,297 @@
|
||||
// 代码地址: https://github.com/CoderMJLee/MJRefresh
|
||||
// MJRefreshHeader.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by MJ Lee on 15/3/4.
|
||||
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshHeader.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
#import "UIScrollView+MJExtension.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
|
||||
NSString * const MJRefreshHeaderRefreshing2IdleBoundsKey = @"MJRefreshHeaderRefreshing2IdleBounds";
|
||||
NSString * const MJRefreshHeaderRefreshingBoundsKey = @"MJRefreshHeaderRefreshingBounds";
|
||||
|
||||
@interface MJRefreshHeader() <CAAnimationDelegate>
|
||||
@property (assign, nonatomic) CGFloat insetTDelta;
|
||||
@end
|
||||
|
||||
@implementation MJRefreshHeader
|
||||
#pragma mark - 构造方法
|
||||
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock
|
||||
{
|
||||
MJRefreshHeader *cmp = [[self alloc] init];
|
||||
cmp.refreshingBlock = refreshingBlock;
|
||||
return cmp;
|
||||
}
|
||||
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
|
||||
{
|
||||
MJRefreshHeader *cmp = [[self alloc] init];
|
||||
[cmp setRefreshingTarget:target refreshingAction:action];
|
||||
return cmp;
|
||||
}
|
||||
|
||||
#pragma mark - 覆盖父类的方法
|
||||
- (void)prepare
|
||||
{
|
||||
[super prepare];
|
||||
|
||||
// 设置key
|
||||
self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey;
|
||||
|
||||
// 设置高度
|
||||
self.mj_h = MJRefreshHeaderHeight;
|
||||
}
|
||||
|
||||
- (void)placeSubviews
|
||||
{
|
||||
[super placeSubviews];
|
||||
|
||||
// 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值)
|
||||
self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop;
|
||||
}
|
||||
|
||||
- (void)resetInset {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
} else {
|
||||
// 如果 iOS 10 及以下系统在刷新时, push 新的 VC, 等待刷新完成后回来, 会导致顶部 Insets.top 异常, 不能 resetInset, 检查一下这种特殊情况
|
||||
if (!self.window) { return; }
|
||||
}
|
||||
|
||||
// sectionheader停留解决
|
||||
CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;
|
||||
insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;
|
||||
self.insetTDelta = _scrollViewOriginalInset.top - insetT;
|
||||
// 避免 CollectionView 在使用根据 Autolayout 和 内容自动伸缩 Cell, 刷新时导致的 Layout 异常渲染问题
|
||||
if (self.scrollView.mj_insetT != insetT) {
|
||||
self.scrollView.mj_insetT = insetT;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
|
||||
{
|
||||
[super scrollViewContentOffsetDidChange:change];
|
||||
|
||||
// 在刷新的refreshing状态
|
||||
if (self.state == MJRefreshStateRefreshing) {
|
||||
[self resetInset];
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳转到下一个控制器时,contentInset可能会变
|
||||
_scrollViewOriginalInset = self.scrollView.mj_inset;
|
||||
|
||||
// 当前的contentOffset
|
||||
CGFloat offsetY = self.scrollView.mj_offsetY;
|
||||
// 头部控件刚好出现的offsetY
|
||||
CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
|
||||
|
||||
// 如果是向上滚动到看不见头部控件,直接返回
|
||||
// >= -> >
|
||||
if (offsetY > happenOffsetY) return;
|
||||
|
||||
// 普通 和 即将刷新 的临界点
|
||||
CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h;
|
||||
CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h;
|
||||
|
||||
if (self.scrollView.isDragging) { // 如果正在拖拽
|
||||
self.pullingPercent = pullingPercent;
|
||||
if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {
|
||||
// 转为即将刷新状态
|
||||
self.state = MJRefreshStatePulling;
|
||||
} else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {
|
||||
// 转为普通状态
|
||||
self.state = MJRefreshStateIdle;
|
||||
}
|
||||
} else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
|
||||
// 开始刷新
|
||||
[self beginRefreshing];
|
||||
} else if (pullingPercent < 1) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setState:(MJRefreshState)state
|
||||
{
|
||||
MJRefreshCheckState
|
||||
|
||||
// 根据状态做事情
|
||||
if (state == MJRefreshStateIdle) {
|
||||
if (oldState != MJRefreshStateRefreshing) return;
|
||||
|
||||
[self headerEndingAction];
|
||||
} else if (state == MJRefreshStateRefreshing) {
|
||||
[self headerRefreshingAction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)headerEndingAction {
|
||||
// 保存刷新时间
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
// 默认使用 UIViewAnimation 动画
|
||||
if (!self.isCollectionViewAnimationBug) {
|
||||
// 恢复inset和offset
|
||||
[UIView animateWithDuration:self.slowAnimationDuration animations:^{
|
||||
self.scrollView.mj_insetT += self.insetTDelta;
|
||||
|
||||
if (self.endRefreshingAnimationBeginAction) {
|
||||
self.endRefreshingAnimationBeginAction();
|
||||
}
|
||||
// 自动调整透明度
|
||||
if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
|
||||
} completion:^(BOOL finished) {
|
||||
self.pullingPercent = 0.0;
|
||||
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
}];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
这个解决方法的思路出自 https://github.com/CoderMJLee/MJRefresh/pull/844
|
||||
修改了用+ [UIView animateWithDuration: animations:]实现的修改contentInset的动画
|
||||
fix issue#225 https://github.com/CoderMJLee/MJRefresh/issues/225
|
||||
另一种解法 pull#737 https://github.com/CoderMJLee/MJRefresh/pull/737
|
||||
|
||||
同时, 处理了 Refreshing 中的动画替换.
|
||||
*/
|
||||
|
||||
// 由于修改 Inset 会导致 self.pullingPercent 联动设置 self.alpha, 故提前获取 alpha 值, 后续用于还原 alpha 动画
|
||||
CGFloat viewAlpha = self.alpha;
|
||||
|
||||
self.scrollView.mj_insetT += self.insetTDelta;
|
||||
// 禁用交互, 如果不禁用可能会引起渲染问题.
|
||||
self.scrollView.userInteractionEnabled = NO;
|
||||
|
||||
//CAAnimation keyPath 不支持 contentInset 用Bounds的动画代替
|
||||
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
|
||||
boundsAnimation.fromValue = [NSValue valueWithCGRect:CGRectOffset(self.scrollView.bounds, 0, self.insetTDelta)];
|
||||
boundsAnimation.duration = self.slowAnimationDuration;
|
||||
//在delegate里移除
|
||||
boundsAnimation.removedOnCompletion = NO;
|
||||
boundsAnimation.fillMode = kCAFillModeBoth;
|
||||
boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
boundsAnimation.delegate = self;
|
||||
[boundsAnimation setValue:MJRefreshHeaderRefreshing2IdleBoundsKey forKey:@"identity"];
|
||||
|
||||
[self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshing2IdleBoundsKey];
|
||||
|
||||
if (self.endRefreshingAnimationBeginAction) {
|
||||
self.endRefreshingAnimationBeginAction();
|
||||
}
|
||||
// 自动调整透明度的动画
|
||||
if (self.isAutomaticallyChangeAlpha) {
|
||||
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||
opacityAnimation.fromValue = @(viewAlpha);
|
||||
opacityAnimation.toValue = @(0.0);
|
||||
opacityAnimation.duration = self.slowAnimationDuration;
|
||||
opacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
[self.layer addAnimation:opacityAnimation forKey:@"MJRefreshHeaderRefreshing2IdleOpacity"];
|
||||
|
||||
// 由于修改了 inset 导致, pullingPercent 被设置值, alpha 已经被提前修改为 0 了. 所以这里不用置 0, 但为了代码的严谨性, 不依赖其他的特殊实现方式, 这里还是置 0.
|
||||
self.alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)headerRefreshingAction {
|
||||
// 默认使用 UIViewAnimation 动画
|
||||
if (!self.isCollectionViewAnimationBug) {
|
||||
[UIView animateWithDuration:self.fastAnimationDuration animations:^{
|
||||
if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
|
||||
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
|
||||
// 增加滚动区域top
|
||||
self.scrollView.mj_insetT = top;
|
||||
// 设置滚动位置
|
||||
CGPoint offset = self.scrollView.contentOffset;
|
||||
offset.y = -top;
|
||||
[self.scrollView setContentOffset:offset animated:NO];
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
[self executeRefreshingCallback];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
|
||||
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
|
||||
// 禁用交互, 如果不禁用可能会引起渲染问题.
|
||||
self.scrollView.userInteractionEnabled = NO;
|
||||
|
||||
// CAAnimation keyPath不支持 contentOffset 用Bounds的动画代替
|
||||
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
|
||||
CGRect bounds = self.scrollView.bounds;
|
||||
bounds.origin.y = -top;
|
||||
boundsAnimation.fromValue = [NSValue valueWithCGRect:self.scrollView.bounds];
|
||||
boundsAnimation.toValue = [NSValue valueWithCGRect:bounds];
|
||||
boundsAnimation.duration = self.fastAnimationDuration;
|
||||
//在delegate里移除
|
||||
boundsAnimation.removedOnCompletion = NO;
|
||||
boundsAnimation.fillMode = kCAFillModeBoth;
|
||||
boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
boundsAnimation.delegate = self;
|
||||
[boundsAnimation setValue:MJRefreshHeaderRefreshingBoundsKey forKey:@"identity"];
|
||||
[self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshingBoundsKey];
|
||||
} else {
|
||||
[self executeRefreshingCallback];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark . 链式语法部分 .
|
||||
|
||||
- (instancetype)linkTo:(UIScrollView *)scrollView {
|
||||
scrollView.mj_header = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - CAAnimationDelegate
|
||||
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
|
||||
NSString *identity = [anim valueForKey:@"identity"];
|
||||
if ([identity isEqualToString:MJRefreshHeaderRefreshing2IdleBoundsKey]) {
|
||||
self.pullingPercent = 0.0;
|
||||
self.scrollView.userInteractionEnabled = YES;
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
} else if ([identity isEqualToString:MJRefreshHeaderRefreshingBoundsKey]) {
|
||||
// 避免出现 end 先于 Refreshing 状态
|
||||
if (self.state != MJRefreshStateIdle) {
|
||||
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
|
||||
self.scrollView.mj_insetT = top;
|
||||
// 设置最终滚动位置
|
||||
CGPoint offset = self.scrollView.contentOffset;
|
||||
offset.y = -top;
|
||||
[self.scrollView setContentOffset:offset animated:NO];
|
||||
}
|
||||
self.scrollView.userInteractionEnabled = YES;
|
||||
[self executeRefreshingCallback];
|
||||
}
|
||||
|
||||
if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey]) {
|
||||
[self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey];
|
||||
}
|
||||
|
||||
if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshingBoundsKey]) {
|
||||
[self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshingBoundsKey];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 公共方法
|
||||
- (NSDate *)lastUpdatedTime
|
||||
{
|
||||
return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey];
|
||||
}
|
||||
|
||||
- (void)setIgnoredScrollViewContentInsetTop:(CGFloat)ignoredScrollViewContentInsetTop {
|
||||
_ignoredScrollViewContentInsetTop = ignoredScrollViewContentInsetTop;
|
||||
|
||||
self.mj_y = - self.mj_h - _ignoredScrollViewContentInsetTop;
|
||||
}
|
||||
|
||||
@end
|
||||
30
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h
generated
Normal file
30
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MJRefreshTrailer.h
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by kinarobin on 2020/5/3.
|
||||
// Copyright © 2020 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#if __has_include(<MJRefresh/MJRefreshComponent.h>)
|
||||
#import <MJRefresh/MJRefreshComponent.h>
|
||||
#else
|
||||
#import "MJRefreshComponent.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MJRefreshTrailer : MJRefreshComponent
|
||||
|
||||
/** 创建trailer*/
|
||||
+ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
|
||||
/** 创建trailer */
|
||||
+ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
|
||||
|
||||
/** 忽略多少scrollView的contentInset的right */
|
||||
@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetRight;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
179
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m
generated
Normal file
179
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m
generated
Normal file
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// MJRefreshTrailer.m
|
||||
// MJRefresh
|
||||
//
|
||||
// Created by kinarobin on 2020/5/3.
|
||||
// Copyright © 2020 小码哥. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MJRefreshTrailer.h"
|
||||
#import "UIView+MJExtension.h"
|
||||
#import "UIScrollView+MJRefresh.h"
|
||||
#import "UIScrollView+MJExtension.h"
|
||||
|
||||
@interface MJRefreshTrailer()
|
||||
@property (assign, nonatomic) NSInteger lastRefreshCount;
|
||||
@property (assign, nonatomic) CGFloat lastRightDelta;
|
||||
@end
|
||||
|
||||
@implementation MJRefreshTrailer
|
||||
|
||||
#pragma mark - 构造方法
|
||||
+ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock {
|
||||
MJRefreshTrailer *cmp = [[self alloc] init];
|
||||
cmp.refreshingBlock = refreshingBlock;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
+ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action {
|
||||
MJRefreshTrailer *cmp = [[self alloc] init];
|
||||
[cmp setRefreshingTarget:target refreshingAction:action];
|
||||
return cmp;
|
||||
}
|
||||
|
||||
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change {
|
||||
[super scrollViewContentOffsetDidChange:change];
|
||||
|
||||
// 如果正在刷新,直接返回
|
||||
if (self.state == MJRefreshStateRefreshing) return;
|
||||
|
||||
_scrollViewOriginalInset = self.scrollView.mj_inset;
|
||||
|
||||
// 当前的contentOffset
|
||||
CGFloat currentOffsetX = self.scrollView.mj_offsetX;
|
||||
// 尾部控件刚好出现的offsetX
|
||||
CGFloat happenOffsetX = [self happenOffsetX];
|
||||
// 如果是向右滚动到看不见右边控件,直接返回
|
||||
if (currentOffsetX <= happenOffsetX) return;
|
||||
|
||||
CGFloat pullingPercent = (currentOffsetX - happenOffsetX) / self.mj_w;
|
||||
|
||||
// 如果已全部加载,仅设置pullingPercent,然后返回
|
||||
if (self.state == MJRefreshStateNoMoreData) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.scrollView.isDragging) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
// 普通 和 即将刷新 的临界点
|
||||
CGFloat normal2pullingOffsetX = happenOffsetX + self.mj_w;
|
||||
|
||||
if (self.state == MJRefreshStateIdle && currentOffsetX > normal2pullingOffsetX) {
|
||||
self.state = MJRefreshStatePulling;
|
||||
} else if (self.state == MJRefreshStatePulling && currentOffsetX <= normal2pullingOffsetX) {
|
||||
// 转为普通状态
|
||||
self.state = MJRefreshStateIdle;
|
||||
}
|
||||
} else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
|
||||
// 开始刷新
|
||||
[self beginRefreshing];
|
||||
} else if (pullingPercent < 1) {
|
||||
self.pullingPercent = pullingPercent;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setState:(MJRefreshState)state {
|
||||
MJRefreshCheckState
|
||||
// 根据状态来设置属性
|
||||
if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
|
||||
// 刷新完毕
|
||||
if (MJRefreshStateRefreshing == oldState) {
|
||||
[UIView animateWithDuration:self.slowAnimationDuration animations:^{
|
||||
if (self.endRefreshingAnimationBeginAction) {
|
||||
self.endRefreshingAnimationBeginAction();
|
||||
}
|
||||
|
||||
self.scrollView.mj_insetR -= self.lastRightDelta;
|
||||
// 自动调整透明度
|
||||
if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
|
||||
} completion:^(BOOL finished) {
|
||||
self.pullingPercent = 0.0;
|
||||
|
||||
if (self.endRefreshingCompletionBlock) {
|
||||
self.endRefreshingCompletionBlock();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
CGFloat deltaW = [self widthForContentBreakView];
|
||||
// 刚刷新完毕
|
||||
if (MJRefreshStateRefreshing == oldState && deltaW > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
|
||||
self.scrollView.mj_offsetX = self.scrollView.mj_offsetX;
|
||||
}
|
||||
} else if (state == MJRefreshStateRefreshing) {
|
||||
// 记录刷新前的数量
|
||||
self.lastRefreshCount = self.scrollView.mj_totalDataCount;
|
||||
|
||||
[UIView animateWithDuration:self.fastAnimationDuration animations:^{
|
||||
CGFloat right = self.mj_w + self.scrollViewOriginalInset.right;
|
||||
CGFloat deltaW = [self widthForContentBreakView];
|
||||
if (deltaW < 0) { // 如果内容宽度小于view的宽度
|
||||
right -= deltaW;
|
||||
}
|
||||
self.lastRightDelta = right - self.scrollView.mj_insetR;
|
||||
self.scrollView.mj_insetR = right;
|
||||
|
||||
// 设置滚动位置
|
||||
CGPoint offset = self.scrollView.contentOffset;
|
||||
offset.x = [self happenOffsetX] + self.mj_w;
|
||||
[self.scrollView setContentOffset:offset animated:NO];
|
||||
} completion:^(BOOL finished) {
|
||||
[self executeRefreshingCallback];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change {
|
||||
[super scrollViewContentSizeDidChange:change];
|
||||
|
||||
// 内容的宽度
|
||||
CGFloat contentWidth = self.scrollView.mj_contentW + self.ignoredScrollViewContentInsetRight;
|
||||
// 表格的宽度
|
||||
CGFloat scrollWidth = self.scrollView.mj_w - self.scrollViewOriginalInset.left - self.scrollViewOriginalInset.right + self.ignoredScrollViewContentInsetRight;
|
||||
// 设置位置和尺寸
|
||||
self.mj_x = MAX(contentWidth, scrollWidth);
|
||||
}
|
||||
|
||||
- (void)placeSubviews {
|
||||
[super placeSubviews];
|
||||
|
||||
self.mj_h = _scrollView.mj_h;
|
||||
// 设置自己的宽度
|
||||
self.mj_w = MJRefreshTrailWidth;
|
||||
}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
if (newSuperview) {
|
||||
// 设置支持水平弹簧效果
|
||||
_scrollView.alwaysBounceHorizontal = YES;
|
||||
_scrollView.alwaysBounceVertical = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark . 链式语法部分 .
|
||||
|
||||
- (instancetype)linkTo:(UIScrollView *)scrollView {
|
||||
scrollView.mj_trailer = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - 刚好看到上拉刷新控件时的contentOffset.x
|
||||
- (CGFloat)happenOffsetX {
|
||||
CGFloat deltaW = [self widthForContentBreakView];
|
||||
if (deltaW > 0) {
|
||||
return deltaW - self.scrollViewOriginalInset.left;
|
||||
} else {
|
||||
return - self.scrollViewOriginalInset.left;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark 获得scrollView的内容 超出 view 的宽度
|
||||
- (CGFloat)widthForContentBreakView {
|
||||
CGFloat w = self.scrollView.frame.size.width - self.scrollViewOriginalInset.right - self.scrollViewOriginalInset.left;
|
||||
return self.scrollView.contentSize.width - w;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user