UIScrollView增加刷新
1、
if (!self.scrollView) {
CGRect frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame));
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
//增加这个滚动事件是为了执行BaseViewController中的scrollViewDidEndDragging事件,滚动立马隐藏键盘
scrollView.delegate = self;
scrollView.backgroundColor = [UIColor clearColor];
[scrollView addHeaderWithTarget:self action:@selector(loadData)];
[self.view addSubview:scrollView];
self.scrollView = scrollView;
//为了让scrollView可以滚动,设置contentSize
self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) + 10);
} else {
[self.scrollView headerEndRefreshing];
}
------------------------------------------------------------------------------------------------------------
//
// UIScrollView+MJRefresh.h
// MJRefreshExample
//
// Created by MJ Lee on 14-5-28.
// Copyright (c) 2014年 itcast. All rights reserved.
//
// 版权属于原作者
// http://code4app.com (cn) http://code4app.net (en)
// 发布代码于最专业的源码分享网站: Code4App.com
#import <UIKit/UIKit.h>
@interface UIScrollView (MJRefresh)
#pragma mark - 下拉刷新
/**
* 添加一个下拉刷新头部控件
*
* @param callback 回调
*/
- (void)addHeaderWithCallback:(void (^)())callback;
/**
* 添加一个下拉刷新头部控件
*
* @param target 目标
* @param action 回调方法
*/
- (void)addHeaderWithTarget:(id)target action:(SEL)action;
/**
* 移除下拉刷新头部控件
*/
- (void)removeHeader;
/**
* 主动让下拉刷新头部控件进入刷新状态
*/
- (void)headerBeginRefreshing;
/**
* 让下拉刷新头部控件停止刷新状态
*/
- (void)headerEndRefreshing;
/**
* 下拉刷新头部控件的可见性
*/
@property (nonatomic, assign, getter = isHeaderHidden) BOOL headerHidden;
/**
* 是否正在下拉刷新
*/
@property (nonatomic, assign, readonly, getter = isHeaderRefreshing) BOOL headerRefreshing;
#pragma mark - 上拉刷新
/**
* 添加一个上拉刷新尾部控件
*
* @param callback 回调
*/
- (void)addFooterWithCallback:(void (^)())callback;
/**
* 添加一个上拉刷新尾部控件
*
* @param target 目标
* @param action 回调方法
*/
- (void)addFooterWithTarget:(id)target action:(SEL)action;
/**
* 移除上拉刷新尾部控件
*/
- (void)removeFooter;
/**
* 主动让上拉刷新尾部控件进入刷新状态
*/
- (void)footerBeginRefreshing;
/**
* 让上拉刷新尾部控件停止刷新状态
*/
- (void)footerEndRefreshing;
/**
* 上拉刷新头部控件的可见性
*/
@property (nonatomic, assign, getter = isFooterHidden) BOOL footerHidden;
/**
* 是否正在上拉刷新
*/
@property (nonatomic, assign, readonly, getter = isFooterRefreshing) BOOL footerRefreshing;
/**
* 设置尾部控件的文字
*/
@property (copy, nonatomic) NSString *footerPullToRefreshText; // 默认:@"上拉可以加载更多数据"
@property (copy, nonatomic) NSString *footerReleaseToRefreshText; // 默认:@"松开立即加载更多数据"
@property (copy, nonatomic) NSString *footerRefreshingText; // 默认:@"MJ哥正在帮你加载数据..."
/**
* 设置头部控件的文字
*/
@property (copy, nonatomic) NSString *headerPullToRefreshText; // 默认:@"下拉可以刷新"
@property (copy, nonatomic) NSString *headerReleaseToRefreshText; // 默认:@"松开立即刷新"
@property (copy, nonatomic) NSString *headerRefreshingText; // 默认:@"MJ哥正在帮你刷新..."
@end
--------------------------------------------------------------------
//
// UIScrollView+MJRefresh.m
// MJRefreshExample
//
// Created by MJ Lee on 14-5-28.
// Copyright (c) 2014年 itcast. All rights reserved.
//
// 版权属于原作者
// http://code4app.com (cn) http://code4app.net (en)
// 发布代码于最专业的源码分享网站: Code4App.com
#import "UIScrollView+MJRefresh.h"
#import "MJRefreshHeaderView.h"
#import "MJRefreshFooterView.h"
#import <objc/runtime.h>
@interface UIScrollView()
@property (weak, nonatomic) MJRefreshHeaderView *header;
@property (weak, nonatomic) MJRefreshFooterView *footer;
@end
@implementation UIScrollView (MJRefresh)
#pragma mark - 运行时相关
static char MJRefreshHeaderViewKey;
static char MJRefreshFooterViewKey;
- (void)setHeader:(MJRefreshHeaderView *)header {
[self willChangeValueForKey:@"MJRefreshHeaderViewKey"];
objc_setAssociatedObject(self, &MJRefreshHeaderViewKey,
header,
OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"MJRefreshHeaderViewKey"];
}
- (MJRefreshHeaderView *)header {
return objc_getAssociatedObject(self, &MJRefreshHeaderViewKey);
}
- (void)setFooter:(MJRefreshFooterView *)footer {
[self willChangeValueForKey:@"MJRefreshFooterViewKey"];
objc_setAssociatedObject(self, &MJRefreshFooterViewKey,
footer,
OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"MJRefreshFooterViewKey"];
}
- (MJRefreshFooterView *)footer {
return objc_getAssociatedObject(self, &MJRefreshFooterViewKey);
}
#pragma mark - 下拉刷新
/**
* 添加一个下拉刷新头部控件
*
* @param callback 回调
*/
- (void)addHeaderWithCallback:(void (^)())callback
{
// 1.创建新的header
if (!self.header) {
MJRefreshHeaderView *header = [MJRefreshHeaderView header];
[self addSubview:header];
self.header = header;
}
// 2.设置block回调
self.header.beginRefreshingCallback = callback;
}
/**
* 添加一个下拉刷新头部控件
*
* @param target 目标
* @param action 回调方法
*/
- (void)addHeaderWithTarget:(id)target action:(SEL)action
{
// 1.创建新的header
if (!self.header) {
MJRefreshHeaderView *header = [MJRefreshHeaderView header];
[self addSubview:header];
self.header = header;
}
// 2.设置目标和回调方法
self.header.beginRefreshingTaget = target;
self.header.beginRefreshingAction = action;
}
/**
* 移除下拉刷新头部控件
*/
- (void)removeHeader
{
[self.header removeFromSuperview];
self.header = nil;
}
/**
* 主动让下拉刷新头部控件进入刷新状态
*/
- (void)headerBeginRefreshing
{
[self.header beginRefreshing];
}
/**
* 让下拉刷新头部控件停止刷新状态
*/
- (void)headerEndRefreshing
{
[self.header endRefreshing];
}
/**
* 下拉刷新头部控件的可见性
*/
- (void)setHeaderHidden:(BOOL)hidden
{
self.header.hidden = hidden;
}
- (BOOL)isHeaderHidden
{
return self.header.isHidden;
}
- (BOOL)isHeaderRefreshing
{
return self.header.state == MJRefreshStateRefreshing;
}
#pragma mark - 上拉刷新
/**
* 添加一个上拉刷新尾部控件
*
* @param callback 回调
*/
- (void)addFooterWithCallback:(void (^)())callback
{
// 1.创建新的footer
if (!self.footer) {
MJRefreshFooterView *footer = [MJRefreshFooterView footer];
[self addSubview:footer];
self.footer = footer;
}
// 2.设置block回调
self.footer.beginRefreshingCallback = callback;
}
/**
* 添加一个上拉刷新尾部控件
*
* @param target 目标
* @param action 回调方法
*/
- (void)addFooterWithTarget:(id)target action:(SEL)action
{
// 1.创建新的footer
if (!self.footer) {
MJRefreshFooterView *footer = [MJRefreshFooterView footer];
[self addSubview:footer];
self.footer = footer;
}
// 2.设置目标和回调方法
self.footer.beginRefreshingTaget = target;
self.footer.beginRefreshingAction = action;
}
/**
* 移除上拉刷新尾部控件
*/
- (void)removeFooter
{
[self.footer removeFromSuperview];
self.footer = nil;
}
/**
* 主动让上拉刷新尾部控件进入刷新状态
*/
- (void)footerBeginRefreshing
{
[self.footer beginRefreshing];
}
/**
* 让上拉刷新尾部控件停止刷新状态
*/
- (void)footerEndRefreshing
{
[self.footer endRefreshing];
}
/**
* 下拉刷新头部控件的可见性
*/
- (void)setFooterHidden:(BOOL)hidden
{
self.footer.hidden = hidden;
}
- (BOOL)isFooterHidden
{
return self.footer.isHidden;
}
- (BOOL)isFooterRefreshing
{
return self.footer.state == MJRefreshStateRefreshing;
}
/**
* 文字
*/
- (void)setFooterPullToRefreshText:(NSString *)footerPullToRefreshText
{
self.footer.pullToRefreshText = footerPullToRefreshText;
}
- (NSString *)footerPullToRefreshText
{
return self.footer.pullToRefreshText;
}
- (void)setFooterReleaseToRefreshText:(NSString *)footerReleaseToRefreshText
{
self.footer.releaseToRefreshText = footerReleaseToRefreshText;
}
- (NSString *)footerReleaseToRefreshText
{
return self.footer.releaseToRefreshText;
}
- (void)setFooterRefreshingText:(NSString *)footerRefreshingText
{
self.footer.refreshingText = footerRefreshingText;
}
- (NSString *)footerRefreshingText
{
return self.footer.refreshingText;
}
- (void)setHeaderPullToRefreshText:(NSString *)headerPullToRefreshText
{
self.header.pullToRefreshText = headerPullToRefreshText;
}
- (NSString *)headerPullToRefreshText
{
return self.header.pullToRefreshText;
}
- (void)setHeaderReleaseToRefreshText:(NSString *)headerReleaseToRefreshText
{
self.header.releaseToRefreshText = headerReleaseToRefreshText;
}
- (NSString *)headerReleaseToRefreshText
{
return self.header.releaseToRefreshText;
}
- (void)setHeaderRefreshingText:(NSString *)headerRefreshingText
{
self.header.refreshingText = headerRefreshingText;
}
- (NSString *)headerRefreshingText
{
return self.header.refreshingText;
}
@end
---------------------------------------------------------------------------
//
// MJRefreshHeaderView.h
// MJRefresh
//
// Created by mj on 13-2-26.
// Copyright (c) 2013年 itcast. All rights reserved.
// 下拉刷新
// 版权属于原作者
// http://code4app.com (cn) http://code4app.net (en)
// 发布代码于最专业的源码分享网站: Code4App.com
#import "MJRefreshBaseView.h"
@interface MJRefreshHeaderView : MJRefreshBaseView
+ (instancetype)header;
@end
--------------------------------------------------------------
//
// MJRefreshHeaderView.m
// MJRefresh
//
// Created by mj on 13-2-26.
// Copyright (c) 2013年 itcast. All rights reserved.
// 下拉刷新
// 版权属于原作者
// http://code4app.com (cn) http://code4app.net (en)
// 发布代码于最专业的源码分享网站: Code4App.com
#import "MJRefreshConst.h"
#import "MJRefreshHeaderView.h"
#import "UIView+MJExtension.h"
#import "UIScrollView+MJExtension.h"
@interface MJRefreshHeaderView()
// 最后的更新时间
@property (nonatomic, strong) NSDate *lastUpdateTime;
@property (nonatomic, weak) UILabel *lastUpdateTimeLabel;
@end
@implementation MJRefreshHeaderView
#pragma mark - 控件初始化
/**
* 时间标签
*/
- (UILabel *)lastUpdateTimeLabel
{
if (!_lastUpdateTimeLabel) {
// 1.创建控件
UILabel *lastUpdateTimeLabel = [[UILabel alloc] init];
lastUpdateTimeLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
lastUpdateTimeLabel.font = [UIFont boldSystemFontOfSize:12];
lastUpdateTimeLabel.textColor = MJRefreshLabelTextColor;
lastUpdateTimeLabel.backgroundColor = [UIColor clearColor];
lastUpdateTimeLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_lastUpdateTimeLabel = lastUpdateTimeLabel];
// 2.加载时间
self.lastUpdateTime = [[NSUserDefaults standardUserDefaults] objectForKey:MJRefreshHeaderTimeKey];
}
return _lastUpdateTimeLabel;
}
+ (instancetype)header
{
return [[MJRefreshHeaderView alloc] init];
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.pullToRefreshText = MJRefreshHeaderPullToRefresh;
self.releaseToRefreshText = MJRefreshHeaderReleaseToRefresh;
self.refreshingText = MJRefreshHeaderRefreshing;
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat statusX = 0;
CGFloat statusY = 0;
CGFloat statusHeight = self.mj_height * 0.5;
CGFloat statusWidth = self.mj_width;
// 1.状态标签
self.statusLabel.frame = CGRectMake(statusX, statusY, statusWidth, statusHeight);
// 2.时间标签
CGFloat lastUpdateY = statusHeight;
CGFloat lastUpdateX = 0;
CGFloat lastUpdateHeight = statusHeight;
CGFloat lastUpdateWidth = statusWidth;
self.lastUpdateTimeLabel.frame = CGRectMake(lastUpdateX, lastUpdateY, lastUpdateWidth, lastUpdateHeight);
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
// 设置自己的位置和尺寸
self.mj_y = - self.mj_height;
}
#pragma mark - 状态相关
#pragma mark 设置最后的更新时间
- (void)setLastUpdateTime:(NSDate *)lastUpdateTime
{
_lastUpdateTime = lastUpdateTime;
// 1.归档
[[NSUserDefaults standardUserDefaults] setObject:lastUpdateTime forKey:MJRefreshHeaderTimeKey];
[[NSUserDefaults standardUserDefaults] synchronize];
// 2.更新时间
[self updateTimeLabel];
}
#pragma mark 更新时间字符串
- (void)updateTimeLabel
{
if (!self.lastUpdateTime) return;
// 1.获得年月日
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger unitFlags = NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit |NSHourCalendarUnit |NSMinuteCalendarUnit;
NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:_lastUpdateTime];
NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]];
// 2.格式化日期
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
if ([cmp1 day] == [cmp2 day]) { // 今天
formatter.dateFormat = @"今天 HH:mm";
} else if ([cmp1 year] == [cmp2 year]) { // 今年
formatter.dateFormat = @"MM-dd HH:mm";
} else {
formatter.dateFormat = @"yyyy-MM-dd HH:mm";
}
NSString *time = [formatter stringFromDate:self.lastUpdateTime];
// 3.显示日期
self.lastUpdateTimeLabel.text = [NSString stringWithFormat:@"最后更新:%@", time];
}
#pragma mark - 监听UIScrollView的contentOffset属性
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// 不能跟用户交互就直接返回
if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden) return;
// 如果正在刷新,直接返回
if (self.state == MJRefreshStateRefreshing) return;
if ([MJRefreshContentOffset isEqualToString:keyPath]) {
[self adjustStateWithContentOffset];
}
}
/**
* 调整状态
*/
- (void)adjustStateWithContentOffset
{
// 当前的contentOffset
CGFloat currentOffsetY = self.scrollView.mj_contentOffsetY;
// 头部控件刚好出现的offsetY
CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
// 如果是向上滚动到看不见头部控件,直接返回
if (currentOffsetY >= happenOffsetY) return;
if (self.scrollView.isDragging) {
// 普通 和 即将刷新 的临界点
CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_height;
if (self.state == MJRefreshStateNormal && currentOffsetY < normal2pullingOffsetY) {
// 转为即将刷新状态
self.state = MJRefreshStatePulling;
} else if (self.state == MJRefreshStatePulling && currentOffsetY >= normal2pullingOffsetY) {
// 转为普通状态
self.state = MJRefreshStateNormal;
}
} else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
// 开始刷新
self.state = MJRefreshStateRefreshing;
}
}
#pragma mark 设置状态
- (void)setState:(MJRefreshState)state
{
// 1.一样的就直接返回
if (self.state == state) return;
// 2.保存旧状态
MJRefreshState oldState = self.state;
// 3.调用父类方法
[super setState:state];
// 4.根据状态执行不同的操作
switch (state) {
case MJRefreshStateNormal: // 下拉可以刷新
{
// 刷新完毕
if (MJRefreshStateRefreshing == oldState) {
self.arrowImage.transform = CGAffineTransformIdentity;
// 保存刷新时间
self.lastUpdateTime = [NSDate date];
[UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
self.scrollView.mj_contentInsetTop -= self.mj_height;
}];
} else {
// 执行动画
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
self.arrowImage.transform = CGAffineTransformIdentity;
}];
}
break;
}
case MJRefreshStatePulling: // 松开可立即刷新
{
// 执行动画
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
}];
break;
}
case MJRefreshStateRefreshing: // 正在刷新中
{
// 执行动画
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
// 1.增加滚动区域
CGFloat top = self.scrollViewOriginalInset.top + self.mj_height;
self.scrollView.mj_contentInsetTop = top;
// 2.设置滚动位置
self.scrollView.mj_contentOffsetY = - top;
}];
break;
}
default:
break;
}
}
@end