一、app
if (!self.scrollView) {動畫
CGRect frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame));網站
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];atom
//增長這個滾動事件是爲了執行BaseViewController中的scrollViewDidEndDragging事件,滾動立馬隱藏鍵盤spa
scrollView.delegate = self;.net
scrollView.backgroundColor = [UIColor clearColor];code
[scrollView addHeaderWithTarget:self action:@selector(loadData)];component
[self.view addSubview:scrollView];orm
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