// 代码地址: 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
|