\*)layoutAttributesForElementsInRect:(CGRect)rect | 返回rect范围内所有元素的布局属性的数组
16 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath | 返回indexPath位置上的元素的布局属性
17 | - (CGSize)collectionViewContentSize | 返回collectionView的滚动范围
18 |
19 | ### 注意 : 因为layoutAttributesForElementsInRect方法调用十分频繁, 所以布局属性的数组应该只计算一次保存起来而不是每次调用该方法的时候重新计算
20 |
21 | * 代理
22 |
23 | 提供接口给外界修改一些瀑布流布局的参数, 例如显示的列数, 列距, 行距, 边缘距(UIEdgeInsets), 最重要的还是item的**高度**!! 因为每个图片(item)的高度是由图片的宽高比和itemWidth来共同决定的. 所以itemHeight必须由代理来决定.这里展示几个代理方法 :
24 |
25 | 代理方法 | 说明
26 | --- | ---
27 | **@required** |
28 | - (CGFloat)waterFallLayout:(JRWaterFallLayout *)waterFallLayout heightForItemAtIndex:(NSUInteger)index width:(CGFloat)width | 返回index位置下的item的高度
29 | **@optional** |
30 | - (NSUInteger)columnCountOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout | 返回瀑布流显示的列数
31 | - (CGFloat)rowMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout | 返回行间距
32 | - (CGFloat)columnMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout | 返回列间距
33 | - (UIEdgeInsets)edgeInsetsOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout | 返回边缘间距
34 |
35 | 注意 : 由于上面所说的, layoutAttributesForElementsInRect方法调用十分频繁, 所以代理方法势必也会频繁调用. 但是并不是所有代理方法都是@required的, 所以在调用@optional的代理方法时需要每次都判断代理是否响应了该选择子
36 |
37 |
38 | if ( [self.delegate respondsToSelector:@selector(columnCountOfWaterFallLayout:)] ) {
39 | _columnCount = [self.delegate columnCountOfWaterFallLayout:self];
40 | } else {
41 | _columnCount = JRDefaultColumnCount;
42 | }
43 |
44 |
45 | 每次都这样判断显然效率很低, 我们可以在prepareLayout方法中进行一次性判断, 然后用一个flags存储起来, 那么下次我们在调用的时候直接对flag进行判断即可. 如下 :
46 |
47 |
48 | struct { // 记录代理是否响应选择子
49 | BOOL didRespondColumnCount : 1; // 这里的1是用1个字节存储
50 | BOOL didRespondColumnMargin : 1;
51 | BOOL didRespondRowMargin : 1;
52 | BOOL didRespondEdgeInsets : 1;
53 | } _delegateFlags;
54 |
55 | - (void)setupDelegateFlags
56 | {
57 | _delegateFlags.didRespondColumnCount = [self.delegate respondsToSelector:@selector(columnCountOfWaterFallLayout:)];
58 |
59 | _delegateFlags.didRespondColumnMargin = [self.delegate respondsToSelector:@selector(columnMarginOfWaterFallLayout:)];
60 |
61 | _delegateFlags.didRespondRowMargin = [self.delegate respondsToSelector:@selector(rowMarginOfWaterFallLayout:)];
62 |
63 | _delegateFlags.didRespondEdgeInsets = [self.delegate respondsToSelector:@selector(edgeInsetsOfWaterFallLayout:)];
64 | }
65 |
66 | // 那么下次调用方法的时候就变成下面那么优雅了
67 | _columnCount = _delegateFlags.didRespondColumnCount ? [self.delegate columnCountOfWaterFallLayout:self] : JRDefaultColumnCount;
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Base/MJRefreshComponent.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshComponent.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | // 刷新控件的基类
9 |
10 | #import
11 | #import "MJRefreshConst.h"
12 | #import "UIView+MJExtension.h"
13 | #import "UIScrollView+MJExtension.h"
14 | #import "UIScrollView+MJRefresh.h"
15 |
16 | /** 刷新控件的状态 */
17 | typedef NS_ENUM(NSInteger, MJRefreshState) {
18 | /** 普通闲置状态 */
19 | MJRefreshStateIdle = 1,
20 | /** 松开就可以进行刷新的状态 */
21 | MJRefreshStatePulling,
22 | /** 正在刷新中的状态 */
23 | MJRefreshStateRefreshing,
24 | /** 即将刷新的状态 */
25 | MJRefreshStateWillRefresh,
26 | /** 所有数据加载完毕,没有更多的数据了 */
27 | MJRefreshStateNoMoreData
28 | };
29 |
30 | /** 进入刷新状态的回调 */
31 | typedef void (^MJRefreshComponentRefreshingBlock)();
32 |
33 | /** 刷新控件的基类 */
34 | @interface MJRefreshComponent : UIView
35 | {
36 | /** 记录scrollView刚开始的inset */
37 | UIEdgeInsets _scrollViewOriginalInset;
38 | /** 父控件 */
39 | __weak UIScrollView *_scrollView;
40 | }
41 | #pragma mark - 刷新回调
42 | /** 正在刷新的回调 */
43 | @property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock;
44 | /** 设置回调对象和回调方法 */
45 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;
46 |
47 | - (NSString *)localizedStringForKey:(NSString *)key;
48 |
49 | /** 回调对象 */
50 | @property (weak, nonatomic) id refreshingTarget;
51 | /** 回调方法 */
52 | @property (assign, nonatomic) SEL refreshingAction;
53 | /** 触发回调(交给子类去调用) */
54 | - (void)executeRefreshingCallback;
55 |
56 | #pragma mark - 刷新状态控制
57 | /** 进入刷新状态 */
58 | - (void)beginRefreshing;
59 | /** 结束刷新状态 */
60 | - (void)endRefreshing;
61 | /** 是否正在刷新 */
62 | - (BOOL)isRefreshing;
63 | /** 刷新状态 一般交给子类内部实现 */
64 | @property (assign, nonatomic) MJRefreshState state;
65 |
66 | #pragma mark - 交给子类去访问
67 | /** 记录scrollView刚开始的inset */
68 | @property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset;
69 | /** 父控件 */
70 | @property (weak, nonatomic, readonly) UIScrollView *scrollView;
71 |
72 | #pragma mark - 交给子类们去实现
73 | /** 初始化 */
74 | - (void)prepare NS_REQUIRES_SUPER;
75 | /** 摆放子控件frame */
76 | - (void)placeSubviews NS_REQUIRES_SUPER;
77 | /** 当scrollView的contentOffset发生改变的时候调用 */
78 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
79 | /** 当scrollView的contentSize发生改变的时候调用 */
80 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
81 | /** 当scrollView的拖拽状态发生改变的时候调用 */
82 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
83 |
84 |
85 | #pragma mark - 其他
86 | /** 拉拽的百分比(交给子类重写) */
87 | @property (assign, nonatomic) CGFloat pullingPercent;
88 | /** 根据拖拽比例自动切换透明度 */
89 | @property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性");
90 | /** 根据拖拽比例自动切换透明度 */
91 | @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;
92 | @end
93 |
94 | @interface UILabel(MJRefresh)
95 | + (instancetype)mj_label;
96 | @end
97 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoGifFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoGifFooter.h"
10 |
11 | @interface MJRefreshAutoGifFooter()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshAutoGifFooter
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)placeSubviews
70 | {
71 | [super placeSubviews];
72 |
73 | if (self.gifView.constraints.count) return;
74 |
75 | self.gifView.frame = self.bounds;
76 | if (self.isRefreshingTitleHidden) {
77 | self.gifView.contentMode = UIViewContentModeCenter;
78 | } else {
79 | self.gifView.contentMode = UIViewContentModeRight;
80 | self.gifView.mj_w = self.mj_w * 0.5 - 90;
81 | }
82 | }
83 |
84 | - (void)setState:(MJRefreshState)state
85 | {
86 | MJRefreshCheckState
87 |
88 | // 根据状态做事情
89 | if (state == MJRefreshStateRefreshing) {
90 | NSArray *images = self.stateImages[@(state)];
91 | if (images.count == 0) return;
92 | [self.gifView stopAnimating];
93 |
94 | self.gifView.hidden = NO;
95 | if (images.count == 1) { // 单张图片
96 | self.gifView.image = [images lastObject];
97 | } else { // 多张图片
98 | self.gifView.animationImages = images;
99 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
100 | [self.gifView startAnimating];
101 | }
102 | } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
103 | [self.gifView stopAnimating];
104 | self.gifView.hidden = YES;
105 | }
106 | }
107 | @end
108 |
109 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJExtension/NSString+MJExtension.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+MJExtension.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/6/7.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSString+MJExtension.h"
10 |
11 | @implementation NSString (MJExtension)
12 | - (NSString *)mj_underlineFromCamel
13 | {
14 | if (self.length == 0) return self;
15 | NSMutableString *string = [NSMutableString string];
16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]];
40 | } else {
41 | [string appendString:cmp];
42 | }
43 | }
44 | return string;
45 | }
46 |
47 | - (NSString *)mj_firstCharLower
48 | {
49 | if (self.length == 0) return self;
50 | NSMutableString *string = [NSMutableString string];
51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString];
52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
53 | return string;
54 | }
55 |
56 | - (NSString *)mj_firstCharUpper
57 | {
58 | if (self.length == 0) return self;
59 | NSMutableString *string = [NSMutableString string];
60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString];
61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
62 | return string;
63 | }
64 |
65 | - (BOOL)mj_isPureInt
66 | {
67 | NSScanner *scan = [NSScanner scannerWithString:self];
68 | int val;
69 | return [scan scanInt:&val] && [scan isAtEnd];
70 | }
71 |
72 | - (NSURL *)mj_url
73 | {
74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]];
75 |
76 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
77 | }
78 | @end
79 |
80 | @implementation NSString (MJExtensionDeprecated_v_2_5_16)
81 | - (NSString *)underlineFromCamel
82 | {
83 | return self.mj_underlineFromCamel;
84 | }
85 |
86 | - (NSString *)camelFromUnderline
87 | {
88 | return self.mj_camelFromUnderline;
89 | }
90 |
91 | - (NSString *)firstCharLower
92 | {
93 | return self.mj_firstCharLower;
94 | }
95 |
96 | - (NSString *)firstCharUpper
97 | {
98 | return self.mj_firstCharUpper;
99 | }
100 |
101 | - (BOOL)isPureInt
102 | {
103 | return self.mj_isPureInt;
104 | }
105 |
106 | - (NSURL *)url
107 | {
108 | return self.mj_url;
109 | }
110 | @end
111 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Header/MJRefreshGifHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshGifHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshGifHeader.h"
10 |
11 | @interface MJRefreshGifHeader()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshGifHeader
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)setPullingPercent:(CGFloat)pullingPercent
70 | {
71 | [super setPullingPercent:pullingPercent];
72 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
73 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
74 | // 停止动画
75 | [self.gifView stopAnimating];
76 | // 设置当前需要显示的图片
77 | NSUInteger index = images.count * pullingPercent;
78 | if (index >= images.count) index = images.count - 1;
79 | self.gifView.image = images[index];
80 | }
81 |
82 | - (void)placeSubviews
83 | {
84 | [super placeSubviews];
85 |
86 | if (self.gifView.constraints.count) return;
87 |
88 | self.gifView.frame = self.bounds;
89 | if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) {
90 | self.gifView.contentMode = UIViewContentModeCenter;
91 | } else {
92 | self.gifView.contentMode = UIViewContentModeRight;
93 | self.gifView.mj_w = self.mj_w * 0.5 - 90;
94 | }
95 | }
96 |
97 | - (void)setState:(MJRefreshState)state
98 | {
99 | MJRefreshCheckState
100 |
101 | // 根据状态做事情
102 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
103 | NSArray *images = self.stateImages[@(state)];
104 | if (images.count == 0) return;
105 |
106 | [self.gifView stopAnimating];
107 | if (images.count == 1) { // 单张图片
108 | self.gifView.image = [images lastObject];
109 | } else { // 多张图片
110 | self.gifView.animationImages = images;
111 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
112 | [self.gifView startAnimating];
113 | }
114 | } else if (state == MJRefreshStateIdle) {
115 | [self.gifView stopAnimating];
116 | }
117 | }
118 | @end
119 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackGifFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackGifFooter.h"
10 |
11 | @interface MJRefreshBackGifFooter()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshBackGifFooter
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)setPullingPercent:(CGFloat)pullingPercent
70 | {
71 | [super setPullingPercent:pullingPercent];
72 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
73 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
74 | [self.gifView stopAnimating];
75 | NSUInteger index = images.count * pullingPercent;
76 | if (index >= images.count) index = images.count - 1;
77 | self.gifView.image = images[index];
78 | }
79 |
80 | - (void)placeSubviews
81 | {
82 | [super placeSubviews];
83 |
84 | if (self.gifView.constraints.count) return;
85 |
86 | self.gifView.frame = self.bounds;
87 | if (self.stateLabel.hidden) {
88 | self.gifView.contentMode = UIViewContentModeCenter;
89 | } else {
90 | self.gifView.contentMode = UIViewContentModeRight;
91 | self.gifView.mj_w = self.mj_w * 0.5 - 90;
92 | }
93 | }
94 |
95 | - (void)setState:(MJRefreshState)state
96 | {
97 | MJRefreshCheckState
98 |
99 | // 根据状态做事情
100 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
101 | NSArray *images = self.stateImages[@(state)];
102 | if (images.count == 0) return;
103 |
104 | self.gifView.hidden = NO;
105 | [self.gifView stopAnimating];
106 | if (images.count == 1) { // 单张图片
107 | self.gifView.image = [images lastObject];
108 | } else { // 多张图片
109 | self.gifView.animationImages = images;
110 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
111 | [self.gifView startAnimating];
112 | }
113 | } else if (state == MJRefreshStateIdle) {
114 | self.gifView.hidden = NO;
115 | } else if (state == MJRefreshStateNoMoreData) {
116 | self.gifView.hidden = YES;
117 | }
118 | }
119 | @end
120 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/UIImage+MultiFormat.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+MultiFormat.m
3 | // SDWebImage
4 | //
5 | // Created by Olivier Poitrey on 07/06/13.
6 | // Copyright (c) 2013 Dailymotion. All rights reserved.
7 | //
8 |
9 | #import "UIImage+MultiFormat.h"
10 | #import "UIImage+GIF.h"
11 | #import "NSData+ImageContentType.h"
12 | #import
13 |
14 | #ifdef SD_WEBP
15 | #import "UIImage+WebP.h"
16 | #endif
17 |
18 | @implementation UIImage (MultiFormat)
19 |
20 | + (UIImage *)sd_imageWithData:(NSData *)data {
21 | if (!data) {
22 | return nil;
23 | }
24 |
25 | UIImage *image;
26 | NSString *imageContentType = [NSData sd_contentTypeForImageData:data];
27 | if ([imageContentType isEqualToString:@"image/gif"]) {
28 | image = [UIImage sd_animatedGIFWithData:data];
29 | }
30 | #ifdef SD_WEBP
31 | else if ([imageContentType isEqualToString:@"image/webp"])
32 | {
33 | image = [UIImage sd_imageWithWebPData:data];
34 | }
35 | #endif
36 | else {
37 | image = [[UIImage alloc] initWithData:data];
38 | UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
39 | if (orientation != UIImageOrientationUp) {
40 | image = [UIImage imageWithCGImage:image.CGImage
41 | scale:image.scale
42 | orientation:orientation];
43 | }
44 | }
45 |
46 |
47 | return image;
48 | }
49 |
50 |
51 | +(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData {
52 | UIImageOrientation result = UIImageOrientationUp;
53 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
54 | if (imageSource) {
55 | CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
56 | if (properties) {
57 | CFTypeRef val;
58 | int exifOrientation;
59 | val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
60 | if (val) {
61 | CFNumberGetValue(val, kCFNumberIntType, &exifOrientation);
62 | result = [self sd_exifOrientationToiOSOrientation:exifOrientation];
63 | } // else - if it's not set it remains at up
64 | CFRelease((CFTypeRef) properties);
65 | } else {
66 | //NSLog(@"NO PROPERTIES, FAIL");
67 | }
68 | CFRelease(imageSource);
69 | }
70 | return result;
71 | }
72 |
73 | #pragma mark EXIF orientation tag converter
74 | // Convert an EXIF image orientation to an iOS one.
75 | // reference see here: http://sylvana.net/jpegcrop/exif_orientation.html
76 | + (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation {
77 | UIImageOrientation orientation = UIImageOrientationUp;
78 | switch (exifOrientation) {
79 | case 1:
80 | orientation = UIImageOrientationUp;
81 | break;
82 |
83 | case 3:
84 | orientation = UIImageOrientationDown;
85 | break;
86 |
87 | case 8:
88 | orientation = UIImageOrientationLeft;
89 | break;
90 |
91 | case 6:
92 | orientation = UIImageOrientationRight;
93 | break;
94 |
95 | case 2:
96 | orientation = UIImageOrientationUpMirrored;
97 | break;
98 |
99 | case 4:
100 | orientation = UIImageOrientationDownMirrored;
101 | break;
102 |
103 | case 5:
104 | orientation = UIImageOrientationLeftMirrored;
105 | break;
106 |
107 | case 7:
108 | orientation = UIImageOrientationRightMirrored;
109 | break;
110 | default:
111 | break;
112 | }
113 | return orientation;
114 | }
115 |
116 |
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/SDWebImageDecoder.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * Created by james on 9/28/11.
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | #import "SDWebImageDecoder.h"
12 |
13 | @implementation UIImage (ForceDecode)
14 |
15 | + (UIImage *)decodedImageWithImage:(UIImage *)image {
16 | // while downloading huge amount of images
17 | // autorelease the bitmap context
18 | // and all vars to help system to free memory
19 | // when there are memory warning.
20 | // on iOS7, do not forget to call
21 | // [[SDImageCache sharedImageCache] clearMemory];
22 |
23 | if (image == nil) { // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
24 | return nil;
25 | }
26 |
27 | @autoreleasepool{
28 | // do not decode animated images
29 | if (image.images != nil) {
30 | return image;
31 | }
32 |
33 | CGImageRef imageRef = image.CGImage;
34 |
35 | CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
36 | BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
37 | alpha == kCGImageAlphaLast ||
38 | alpha == kCGImageAlphaPremultipliedFirst ||
39 | alpha == kCGImageAlphaPremultipliedLast);
40 | if (anyAlpha) {
41 | return image;
42 | }
43 |
44 | // current
45 | CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
46 | CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
47 |
48 | BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
49 | imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
50 | imageColorSpaceModel == kCGColorSpaceModelCMYK ||
51 | imageColorSpaceModel == kCGColorSpaceModelIndexed);
52 | if (unsupportedColorSpace) {
53 | colorspaceRef = CGColorSpaceCreateDeviceRGB();
54 | }
55 |
56 | size_t width = CGImageGetWidth(imageRef);
57 | size_t height = CGImageGetHeight(imageRef);
58 | NSUInteger bytesPerPixel = 4;
59 | NSUInteger bytesPerRow = bytesPerPixel * width;
60 | NSUInteger bitsPerComponent = 8;
61 |
62 |
63 | // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
64 | // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
65 | // to create bitmap graphics contexts without alpha info.
66 | CGContextRef context = CGBitmapContextCreate(NULL,
67 | width,
68 | height,
69 | bitsPerComponent,
70 | bytesPerRow,
71 | colorspaceRef,
72 | kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
73 |
74 | // Draw the image into the context and retrieve the new bitmap image without alpha
75 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
76 | CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
77 | UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
78 | scale:image.scale
79 | orientation:image.imageOrientation];
80 |
81 | if (unsupportedColorSpace) {
82 | CGColorSpaceRelease(colorspaceRef);
83 | }
84 |
85 | CGContextRelease(context);
86 | CGImageRelease(imageRefWithoutAlpha);
87 |
88 | return imageWithoutAlpha;
89 | }
90 | }
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackNormalFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackNormalFooter.h"
10 |
11 | @interface MJRefreshBackNormalFooter()
12 | {
13 | __unsafe_unretained UIImageView *_arrowView;
14 | }
15 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
16 | @end
17 |
18 | @implementation MJRefreshBackNormalFooter
19 | #pragma mark - 懒加载子控件
20 | - (UIImageView *)arrowView
21 | {
22 | if (!_arrowView) {
23 | UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")];
24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:image];
25 | [self addSubview:_arrowView = arrowView];
26 | }
27 | return _arrowView;
28 | }
29 |
30 |
31 | - (UIActivityIndicatorView *)loadingView
32 | {
33 | if (!_loadingView) {
34 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle];
35 | loadingView.hidesWhenStopped = YES;
36 | [self addSubview:_loadingView = loadingView];
37 | }
38 | return _loadingView;
39 | }
40 |
41 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
42 | {
43 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
44 |
45 | self.loadingView = nil;
46 | [self setNeedsLayout];
47 | }
48 | #pragma mark - 重写父类的方法
49 | - (void)prepare
50 | {
51 | [super prepare];
52 |
53 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
54 | }
55 |
56 | - (void)placeSubviews
57 | {
58 | [super placeSubviews];
59 |
60 | // 箭头的中心点
61 | CGFloat arrowCenterX = self.mj_w * 0.5;
62 | if (!self.stateLabel.hidden) {
63 | arrowCenterX -= 100;
64 | }
65 | CGFloat arrowCenterY = self.mj_h * 0.5;
66 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
67 |
68 | // 箭头
69 | if (self.arrowView.constraints.count == 0) {
70 | self.arrowView.mj_size = self.arrowView.image.size;
71 | self.arrowView.center = arrowCenter;
72 | }
73 |
74 | // 圈圈
75 | if (self.loadingView.constraints.count == 0) {
76 | self.loadingView.center = arrowCenter;
77 | }
78 | }
79 |
80 | - (void)setState:(MJRefreshState)state
81 | {
82 | MJRefreshCheckState
83 |
84 | // 根据状态做事情
85 | if (state == MJRefreshStateIdle) {
86 | if (oldState == MJRefreshStateRefreshing) {
87 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
88 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
89 | self.loadingView.alpha = 0.0;
90 | } completion:^(BOOL finished) {
91 | self.loadingView.alpha = 1.0;
92 | [self.loadingView stopAnimating];
93 |
94 | self.arrowView.hidden = NO;
95 | }];
96 | } else {
97 | self.arrowView.hidden = NO;
98 | [self.loadingView stopAnimating];
99 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
100 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
101 | }];
102 | }
103 | } else if (state == MJRefreshStatePulling) {
104 | self.arrowView.hidden = NO;
105 | [self.loadingView stopAnimating];
106 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
107 | self.arrowView.transform = CGAffineTransformIdentity;
108 | }];
109 | } else if (state == MJRefreshStateRefreshing) {
110 | self.arrowView.hidden = YES;
111 | [self.loadingView startAnimating];
112 | } else if (state == MJRefreshStateNoMoreData) {
113 | self.arrowView.hidden = YES;
114 | [self.loadingView stopAnimating];
115 | }
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/WaterFallLayout/Cell/JRShopCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Base/MJRefreshAutoFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoFooter.h"
10 |
11 | @interface MJRefreshAutoFooter()
12 | @end
13 |
14 | @implementation MJRefreshAutoFooter
15 |
16 | #pragma mark - 初始化
17 | - (void)willMoveToSuperview:(UIView *)newSuperview
18 | {
19 | [super willMoveToSuperview:newSuperview];
20 |
21 | if (newSuperview) { // 新的父控件
22 | if (self.hidden == NO) {
23 | self.scrollView.mj_insetB += self.mj_h;
24 | }
25 |
26 | // 设置位置
27 | self.mj_y = _scrollView.mj_contentH;
28 | } else { // 被移除了
29 | if (self.hidden == NO) {
30 | self.scrollView.mj_insetB -= self.mj_h;
31 | }
32 | }
33 | }
34 |
35 | #pragma mark - 过期方法
36 | - (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh
37 | {
38 | self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh;
39 | }
40 |
41 | - (CGFloat)appearencePercentTriggerAutoRefresh
42 | {
43 | return self.triggerAutomaticallyRefreshPercent;
44 | }
45 |
46 | #pragma mark - 实现父类的方法
47 | - (void)prepare
48 | {
49 | [super prepare];
50 |
51 | // 默认底部控件100%出现时才会自动刷新
52 | self.triggerAutomaticallyRefreshPercent = 1.0;
53 |
54 | // 设置为默认状态
55 | self.automaticallyRefresh = YES;
56 | }
57 |
58 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
59 | {
60 | [super scrollViewContentSizeDidChange:change];
61 |
62 | // 设置位置
63 | self.mj_y = self.scrollView.mj_contentH;
64 | }
65 |
66 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
67 | {
68 | [super scrollViewContentOffsetDidChange:change];
69 |
70 | if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return;
71 |
72 | if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕
73 | // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理
74 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) {
75 | // 防止手松开时连续调用
76 | CGPoint old = [change[@"old"] CGPointValue];
77 | CGPoint new = [change[@"new"] CGPointValue];
78 | if (new.y <= old.y) return;
79 |
80 | // 当底部刷新控件完全出现时,才刷新
81 | [self beginRefreshing];
82 | }
83 | }
84 | }
85 |
86 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change
87 | {
88 | [super scrollViewPanStateDidChange:change];
89 |
90 | if (self.state != MJRefreshStateIdle) return;
91 |
92 | if (_scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {// 手松开
93 | if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕
94 | if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
95 | [self beginRefreshing];
96 | }
97 | } else { // 超出一个屏幕
98 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
99 | [self beginRefreshing];
100 | }
101 | }
102 | }
103 | }
104 |
105 | - (void)setState:(MJRefreshState)state
106 | {
107 | MJRefreshCheckState
108 |
109 | if (state == MJRefreshStateRefreshing) {
110 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
111 | [self executeRefreshingCallback];
112 | });
113 | }
114 | }
115 |
116 | - (void)setHidden:(BOOL)hidden
117 | {
118 | BOOL lastHidden = self.isHidden;
119 |
120 | [super setHidden:hidden];
121 |
122 | if (!lastHidden && hidden) {
123 | self.state = MJRefreshStateIdle;
124 |
125 | self.scrollView.mj_insetB -= self.mj_h;
126 | } else if (lastHidden && !hidden) {
127 | self.scrollView.mj_insetB += self.mj_h;
128 |
129 | // 设置位置
130 | self.mj_y = _scrollView.mj_contentH;
131 | }
132 | }
133 | @end
134 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/SDWebImagePrefetcher.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageManager.h"
11 |
12 | @class SDWebImagePrefetcher;
13 |
14 | @protocol SDWebImagePrefetcherDelegate
15 |
16 | @optional
17 |
18 | /**
19 | * Called when an image was prefetched.
20 | *
21 | * @param imagePrefetcher The current image prefetcher
22 | * @param imageURL The image url that was prefetched
23 | * @param finishedCount The total number of images that were prefetched (successful or not)
24 | * @param totalCount The total number of images that were to be prefetched
25 | */
26 | - (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
27 |
28 | /**
29 | * Called when all images are prefetched.
30 | * @param imagePrefetcher The current image prefetcher
31 | * @param totalCount The total number of images that were prefetched (whether successful or not)
32 | * @param skippedCount The total number of images that were skipped
33 | */
34 | - (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
35 |
36 | @end
37 |
38 | typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
39 | typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
40 |
41 | /**
42 | * Prefetch some URLs in the cache for future use. Images are downloaded in low priority.
43 | */
44 | @interface SDWebImagePrefetcher : NSObject
45 |
46 | /**
47 | * The web image manager
48 | */
49 | @property (strong, nonatomic, readonly) SDWebImageManager *manager;
50 |
51 | /**
52 | * Maximum number of URLs to prefetch at the same time. Defaults to 3.
53 | */
54 | @property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
55 |
56 | /**
57 | * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority.
58 | */
59 | @property (nonatomic, assign) SDWebImageOptions options;
60 |
61 | /**
62 | * Queue options for Prefetcher. Defaults to Main Queue.
63 | */
64 | @property (nonatomic, assign) dispatch_queue_t prefetcherQueue;
65 |
66 | @property (weak, nonatomic) id delegate;
67 |
68 | /**
69 | * Return the global image prefetcher instance.
70 | */
71 | + (SDWebImagePrefetcher *)sharedImagePrefetcher;
72 |
73 | /**
74 | * Allows you to instantiate a prefetcher with any arbitrary image manager.
75 | */
76 | - (id)initWithImageManager:(SDWebImageManager *)manager;
77 |
78 | /**
79 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
80 | * currently one image is downloaded at a time,
81 | * and skips images for failed downloads and proceed to the next image in the list
82 | *
83 | * @param urls list of URLs to prefetch
84 | */
85 | - (void)prefetchURLs:(NSArray *)urls;
86 |
87 | /**
88 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
89 | * currently one image is downloaded at a time,
90 | * and skips images for failed downloads and proceed to the next image in the list
91 | *
92 | * @param urls list of URLs to prefetch
93 | * @param progressBlock block to be called when progress updates;
94 | * first parameter is the number of completed (successful or not) requests,
95 | * second parameter is the total number of images originally requested to be prefetched
96 | * @param completionBlock block to be called when prefetching is completed
97 | * first param is the number of completed (successful or not) requests,
98 | * second parameter is the number of skipped requests
99 | */
100 | - (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock;
101 |
102 | /**
103 | * Remove and cancel queued list
104 | */
105 | - (void)cancelPrefetching;
106 |
107 |
108 | @end
109 |
--------------------------------------------------------------------------------
/WaterFallLayout.xcodeproj/xcuserdata/sky.xcuserdatad/xcschemes/WaterFallLayout.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/UIScrollView+MJRefresh.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIScrollView+MJRefresh.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "UIScrollView+MJRefresh.h"
11 | #import "MJRefreshHeader.h"
12 | #import "MJRefreshFooter.h"
13 | #import
14 |
15 | @implementation NSObject (MJRefresh)
16 |
17 | + (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
18 | {
19 | method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
20 | }
21 |
22 | + (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2
23 | {
24 | method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2));
25 | }
26 |
27 | @end
28 |
29 | @implementation UIScrollView (MJRefresh)
30 |
31 | #pragma mark - header
32 | static const char MJRefreshHeaderKey = '\0';
33 | - (void)setMj_header:(MJRefreshHeader *)mj_header
34 | {
35 | if (mj_header != self.mj_header) {
36 | // 删除旧的,添加新的
37 | [self.mj_header removeFromSuperview];
38 | [self insertSubview:mj_header atIndex:0];
39 |
40 | // 存储新的
41 | [self willChangeValueForKey:@"mj_header"]; // KVO
42 | objc_setAssociatedObject(self, &MJRefreshHeaderKey,
43 | mj_header, OBJC_ASSOCIATION_ASSIGN);
44 | [self didChangeValueForKey:@"mj_header"]; // KVO
45 | }
46 | }
47 |
48 | - (MJRefreshHeader *)mj_header
49 | {
50 | return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
51 | }
52 |
53 | #pragma mark - footer
54 | static const char MJRefreshFooterKey = '\0';
55 | - (void)setMj_footer:(MJRefreshFooter *)mj_footer
56 | {
57 | if (mj_footer != self.mj_footer) {
58 | // 删除旧的,添加新的
59 | [self.mj_footer removeFromSuperview];
60 | [self addSubview:mj_footer];
61 |
62 | // 存储新的
63 | [self willChangeValueForKey:@"mj_footer"]; // KVO
64 | objc_setAssociatedObject(self, &MJRefreshFooterKey,
65 | mj_footer, OBJC_ASSOCIATION_ASSIGN);
66 | [self didChangeValueForKey:@"mj_footer"]; // KVO
67 | }
68 | }
69 |
70 | - (MJRefreshFooter *)mj_footer
71 | {
72 | return objc_getAssociatedObject(self, &MJRefreshFooterKey);
73 | }
74 |
75 | #pragma mark - 过期
76 | - (void)setFooter:(MJRefreshFooter *)footer
77 | {
78 | self.mj_footer = footer;
79 | }
80 |
81 | - (MJRefreshFooter *)footer
82 | {
83 | return self.mj_footer;
84 | }
85 |
86 | - (void)setHeader:(MJRefreshHeader *)header
87 | {
88 | self.mj_header = header;
89 | }
90 |
91 | - (MJRefreshHeader *)header
92 | {
93 | return self.mj_header;
94 | }
95 |
96 | #pragma mark - other
97 | - (NSInteger)mj_totalDataCount
98 | {
99 | NSInteger totalCount = 0;
100 | if ([self isKindOfClass:[UITableView class]]) {
101 | UITableView *tableView = (UITableView *)self;
102 |
103 | for (NSInteger section = 0; section
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImagePrefetcher.h"
10 |
11 | @interface SDWebImagePrefetcher ()
12 |
13 | @property (strong, nonatomic) SDWebImageManager *manager;
14 | @property (strong, nonatomic) NSArray *prefetchURLs;
15 | @property (assign, nonatomic) NSUInteger requestedCount;
16 | @property (assign, nonatomic) NSUInteger skippedCount;
17 | @property (assign, nonatomic) NSUInteger finishedCount;
18 | @property (assign, nonatomic) NSTimeInterval startedTime;
19 | @property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock;
20 | @property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock;
21 |
22 | @end
23 |
24 | @implementation SDWebImagePrefetcher
25 |
26 | + (SDWebImagePrefetcher *)sharedImagePrefetcher {
27 | static dispatch_once_t once;
28 | static id instance;
29 | dispatch_once(&once, ^{
30 | instance = [self new];
31 | });
32 | return instance;
33 | }
34 |
35 | - (id)init {
36 | return [self initWithImageManager:[SDWebImageManager new]];
37 | }
38 |
39 | - (id)initWithImageManager:(SDWebImageManager *)manager {
40 | if ((self = [super init])) {
41 | _manager = manager;
42 | _options = SDWebImageLowPriority;
43 | _prefetcherQueue = dispatch_get_main_queue();
44 | self.maxConcurrentDownloads = 3;
45 | }
46 | return self;
47 | }
48 |
49 | - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
50 | self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
51 | }
52 |
53 | - (NSUInteger)maxConcurrentDownloads {
54 | return self.manager.imageDownloader.maxConcurrentDownloads;
55 | }
56 |
57 | - (void)startPrefetchingAtIndex:(NSUInteger)index {
58 | if (index >= self.prefetchURLs.count) return;
59 | self.requestedCount++;
60 | [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
61 | if (!finished) return;
62 | self.finishedCount++;
63 |
64 | if (image) {
65 | if (self.progressBlock) {
66 | self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
67 | }
68 | }
69 | else {
70 | if (self.progressBlock) {
71 | self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
72 | }
73 | // Add last failed
74 | self.skippedCount++;
75 | }
76 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
77 | [self.delegate imagePrefetcher:self
78 | didPrefetchURL:self.prefetchURLs[index]
79 | finishedCount:self.finishedCount
80 | totalCount:self.prefetchURLs.count
81 | ];
82 | }
83 | if (self.prefetchURLs.count > self.requestedCount) {
84 | dispatch_async(self.prefetcherQueue, ^{
85 | [self startPrefetchingAtIndex:self.requestedCount];
86 | });
87 | } else if (self.finishedCount == self.requestedCount) {
88 | [self reportStatus];
89 | if (self.completionBlock) {
90 | self.completionBlock(self.finishedCount, self.skippedCount);
91 | self.completionBlock = nil;
92 | }
93 | self.progressBlock = nil;
94 | }
95 | }];
96 | }
97 |
98 | - (void)reportStatus {
99 | NSUInteger total = [self.prefetchURLs count];
100 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
101 | [self.delegate imagePrefetcher:self
102 | didFinishWithTotalCount:(total - self.skippedCount)
103 | skippedCount:self.skippedCount
104 | ];
105 | }
106 | }
107 |
108 | - (void)prefetchURLs:(NSArray *)urls {
109 | [self prefetchURLs:urls progress:nil completed:nil];
110 | }
111 |
112 | - (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock {
113 | [self cancelPrefetching]; // Prevent duplicate prefetch request
114 | self.startedTime = CFAbsoluteTimeGetCurrent();
115 | self.prefetchURLs = urls;
116 | self.completionBlock = completionBlock;
117 | self.progressBlock = progressBlock;
118 |
119 | if (urls.count == 0) {
120 | if (completionBlock) {
121 | completionBlock(0,0);
122 | }
123 | } else {
124 | // Starts prefetching from the very first image on the list with the max allowed concurrency
125 | NSUInteger listCount = self.prefetchURLs.count;
126 | for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
127 | [self startPrefetchingAtIndex:i];
128 | }
129 | }
130 | }
131 |
132 | - (void)cancelPrefetching {
133 | self.prefetchURLs = nil;
134 | self.skippedCount = 0;
135 | self.requestedCount = 0;
136 | self.finishedCount = 0;
137 | [self.manager cancelAll];
138 | }
139 |
140 | @end
141 |
--------------------------------------------------------------------------------
/WaterFallLayout/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // WaterFallLayout
4 | //
5 | // Created by sky on 16/6/6.
6 | // Copyright © 2016年 sky. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "JRShop.h"
11 | #import "JRShopCell.h"
12 | #import "MJRefresh.h"
13 | #import "MJExtension.h"
14 | #import "JRWaterFallLayout.h"
15 |
16 | // collectionViewCell的重用标识符
17 | static NSString * const shopCellReuseID = @"shop";
18 |
19 | @interface ViewController ()
20 |
21 | /** 瀑布流view */
22 | @property (nonatomic, weak) UICollectionView *collectionView;
23 |
24 | /** shops */
25 | @property (nonatomic, strong) NSMutableArray *shops;
26 |
27 | /** 当前页码 */
28 | @property (nonatomic, assign) NSUInteger currentPage;
29 |
30 | @end
31 |
32 | @implementation ViewController
33 |
34 | - (NSMutableArray *)shops
35 | {
36 | if (_shops == nil) {
37 | _shops = [NSMutableArray array];
38 | }
39 | return _shops;
40 | }
41 |
42 | - (void)viewDidLoad {
43 | [super viewDidLoad];
44 |
45 | // 设置当前页码为0
46 | self.currentPage = 0;
47 |
48 | // 初始化瀑布流view
49 | [self setupCollectionView];
50 |
51 | }
52 |
53 | - (void)setupCollectionView
54 | {
55 | // 创建瀑布流layout
56 | JRWaterFallLayout *layout = [[JRWaterFallLayout alloc] init];
57 | // 设置代理
58 | layout.delegate = self;
59 |
60 | // 创建瀑布流view
61 | UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
62 | // 设置数据源
63 | collectionView.dataSource = self;
64 | collectionView.backgroundColor = [UIColor whiteColor];
65 |
66 | [self.view addSubview:collectionView];
67 | self.collectionView = collectionView;
68 |
69 | // 注册cell
70 | [self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([JRShopCell class]) bundle:nil] forCellWithReuseIdentifier:shopCellReuseID];
71 |
72 | // 为瀑布流控件添加下拉加载和上拉加载
73 | self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
74 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 模拟网络请求延迟
75 |
76 | // 清空数据
77 | [self.shops removeAllObjects];
78 |
79 | [self.shops addObjectsFromArray:[self newShops]];
80 |
81 | // 刷新数据
82 | [self.collectionView reloadData];
83 |
84 | // 停止刷新
85 | [self.collectionView.mj_header endRefreshing];
86 | });
87 | }];
88 | // 第一次进入则自动加载
89 | [self.collectionView.mj_header beginRefreshing];
90 |
91 |
92 | self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
93 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 模拟网络请求延迟
94 |
95 | [self.shops addObjectsFromArray:[self moreShopsWithCurrentPage:self.currentPage]];
96 |
97 | // 刷新数据
98 | [self.collectionView reloadData];
99 |
100 | // 停止刷新
101 | [self.collectionView.mj_footer endRefreshing];
102 | });
103 | }];
104 | }
105 |
106 | #pragma mark - 内部方法
107 | - (NSArray *)newShops
108 | {
109 | return [JRShop mj_objectArrayWithFilename:@"0.plist"];
110 | }
111 |
112 | - (NSArray *)moreShopsWithCurrentPage:(NSUInteger)currentPage
113 | {
114 | // 页码的判断
115 | if (currentPage == 3) {
116 | self.currentPage = 0;
117 | } else {
118 | self.currentPage++;
119 | }
120 |
121 | NSString *nextPage = [NSString stringWithFormat:@"%lu.plist", self.currentPage];
122 |
123 | return [JRShop mj_objectArrayWithFilename:nextPage];
124 | }
125 |
126 |
127 | #pragma mark -
128 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
129 | {
130 | return self.shops.count;
131 | }
132 |
133 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
134 | {
135 | // 创建cell
136 | JRShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:shopCellReuseID forIndexPath:indexPath];
137 |
138 | // 给cell传递模型
139 | cell.shop = self.shops[indexPath.item];
140 |
141 | // 返回cell
142 | return cell;
143 | }
144 |
145 | #pragma mark -
146 | /**
147 | * 返回每个item的高度
148 | */
149 | - (CGFloat)waterFallLayout:(JRWaterFallLayout *)waterFallLayout heightForItemAtIndex:(NSUInteger)index width:(CGFloat)width
150 | {
151 | JRShop *shop = self.shops[index];
152 | CGFloat shopHeight = [shop.h doubleValue];
153 | CGFloat shopWidth = [shop.w doubleValue];
154 | return shopHeight * width / shopWidth;
155 | }
156 |
157 | //- (CGFloat)columnMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout
158 | //{
159 | // return 50;
160 | //}
161 |
162 | //- (NSUInteger)columnCountOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout
163 | //{
164 | // return 4;
165 | //}
166 |
167 | //- (CGFloat)rowMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout
168 | //{
169 | // return 50;
170 | //}
171 |
172 | //- (UIEdgeInsets)edgeInsetsOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout
173 | //{
174 | // return UIEdgeInsetsMake(30, 40, 50, 70);
175 | //}
176 |
177 |
178 | @end
179 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Header/MJRefreshNormalHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshNormalHeader.h"
10 |
11 | @interface MJRefreshNormalHeader()
12 | {
13 | __unsafe_unretained UIImageView *_arrowView;
14 | }
15 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
16 | @end
17 |
18 | @implementation MJRefreshNormalHeader
19 | #pragma mark - 懒加载子控件
20 | - (UIImageView *)arrowView
21 | {
22 | if (!_arrowView) {
23 | UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")];
24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[image imageWithRenderingMode:(UIImageRenderingModeAlwaysTemplate)]];
25 | arrowView.tintColor = self.stateLabel.textColor;
26 | [self addSubview:_arrowView = arrowView];
27 | }
28 | return _arrowView;
29 | }
30 |
31 | - (UIActivityIndicatorView *)loadingView
32 | {
33 | if (!_loadingView) {
34 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle];
35 | loadingView.hidesWhenStopped = YES;
36 | [self addSubview:_loadingView = loadingView];
37 | }
38 | return _loadingView;
39 | }
40 |
41 | #pragma mark - 公共方法
42 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
43 | {
44 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
45 |
46 | self.loadingView = nil;
47 | [self setNeedsLayout];
48 | }
49 |
50 | #pragma mark - 重写父类的方法
51 | - (void)prepare
52 | {
53 | [super prepare];
54 |
55 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
56 | }
57 |
58 | - (CGFloat)stringWidth:(UILabel *)_label
59 | {
60 | CGFloat stringWidth = 0;
61 | CGSize size = CGSizeMake(self.mj_w, self.mj_h);
62 | if (_label.text.length > 0) {
63 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
64 | stringWidth =[_label.text
65 | boundingRectWithSize:size
66 | options:NSStringDrawingUsesLineFragmentOrigin
67 | attributes:@{NSFontAttributeName:_label.font}
68 | context:nil].size.width;
69 | #else
70 |
71 | stringWidth = [_label.text sizeWithFont:_label.font
72 | constrainedToSize:size
73 | lineBreakMode:NSLineBreakByCharWrapping].width;
74 | #endif
75 | }
76 |
77 |
78 | return stringWidth;
79 | }
80 |
81 | - (void)placeSubviews
82 | {
83 | [super placeSubviews];
84 |
85 | // 箭头的中心点
86 | CGFloat arrowCenterX = self.mj_w * 0.5;
87 | if (!self.stateLabel.hidden) {
88 | CGFloat offset = 20;
89 | CGFloat stateWidth = [self stringWidth:self.stateLabel];
90 | CGFloat timeWidth = 0.0;
91 | if (!self.lastUpdatedTimeLabel.hidden) {
92 | timeWidth = [self stringWidth:self.lastUpdatedTimeLabel];
93 | }
94 | CGFloat textWidth = MAX(stateWidth, timeWidth);
95 | arrowCenterX -= textWidth / 2 + offset;
96 | }
97 | CGFloat arrowCenterY = self.mj_h * 0.5;
98 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
99 |
100 | // 箭头
101 | if (self.arrowView.constraints.count == 0) {
102 | self.arrowView.mj_size = self.arrowView.image.size;
103 | self.arrowView.center = arrowCenter;
104 | }
105 |
106 | // 圈圈
107 | if (self.loadingView.constraints.count == 0) {
108 | self.loadingView.center = arrowCenter;
109 | }
110 | }
111 |
112 | - (void)setState:(MJRefreshState)state
113 | {
114 | MJRefreshCheckState
115 |
116 | // 根据状态做事情
117 | if (state == MJRefreshStateIdle) {
118 | if (oldState == MJRefreshStateRefreshing) {
119 | self.arrowView.transform = CGAffineTransformIdentity;
120 |
121 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
122 | self.loadingView.alpha = 0.0;
123 | } completion:^(BOOL finished) {
124 | // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态
125 | if (self.state != MJRefreshStateIdle) return;
126 |
127 | self.loadingView.alpha = 1.0;
128 | [self.loadingView stopAnimating];
129 | self.arrowView.hidden = NO;
130 | }];
131 | } else {
132 | [self.loadingView stopAnimating];
133 | self.arrowView.hidden = NO;
134 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
135 | self.arrowView.transform = CGAffineTransformIdentity;
136 | }];
137 | }
138 | } else if (state == MJRefreshStatePulling) {
139 | [self.loadingView stopAnimating];
140 | self.arrowView.hidden = NO;
141 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
142 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
143 | }];
144 | } else if (state == MJRefreshStateRefreshing) {
145 | self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行
146 | [self.loadingView startAnimating];
147 | self.arrowView.hidden = YES;
148 | }
149 | }
150 | @end
151 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/UIImageView+HighlightedWebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 | #import "SDWebImageManager.h"
12 |
13 | /**
14 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state.
15 | */
16 | @interface UIImageView (HighlightedWebCache)
17 |
18 | /**
19 | * Set the imageView `highlightedImage` with an `url`.
20 | *
21 | * The download is asynchronous and cached.
22 | *
23 | * @param url The url for the image.
24 | */
25 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url;
26 |
27 | /**
28 | * Set the imageView `highlightedImage` with an `url` and custom options.
29 | *
30 | * The download is asynchronous and cached.
31 | *
32 | * @param url The url for the image.
33 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
34 | */
35 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options;
36 |
37 | /**
38 | * Set the imageView `highlightedImage` with an `url`.
39 | *
40 | * The download is asynchronous and cached.
41 | *
42 | * @param url The url for the image.
43 | * @param completedBlock A block called when operation has been completed. This block has no return value
44 | * and takes the requested UIImage as first parameter. In case of error the image parameter
45 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
46 | * indicating if the image was retrieved from the local cache or from the network.
47 | * The fourth parameter is the original image url.
48 | */
49 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock;
50 |
51 | /**
52 | * Set the imageView `highlightedImage` with an `url` and custom options.
53 | *
54 | * The download is asynchronous and cached.
55 | *
56 | * @param url The url for the image.
57 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
58 | * @param completedBlock A block called when operation has been completed. This block has no return value
59 | * and takes the requested UIImage as first parameter. In case of error the image parameter
60 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
61 | * indicating if the image was retrieved from the local cache or from the network.
62 | * The fourth parameter is the original image url.
63 | */
64 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock;
65 |
66 | /**
67 | * Set the imageView `highlightedImage` with an `url` and custom options.
68 | *
69 | * The download is asynchronous and cached.
70 | *
71 | * @param url The url for the image.
72 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
73 | * @param progressBlock A block called while image is downloading
74 | * @param completedBlock A block called when operation has been completed. This block has no return value
75 | * and takes the requested UIImage as first parameter. In case of error the image parameter
76 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
77 | * indicating if the image was retrieved from the local cache or from the network.
78 | * The fourth parameter is the original image url.
79 | */
80 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
81 |
82 | /**
83 | * Cancel the current download
84 | */
85 | - (void)sd_cancelCurrentHighlightedImageLoad;
86 |
87 | @end
88 |
89 |
90 | @interface UIImageView (HighlightedWebCacheDeprecated)
91 |
92 | - (void)setHighlightedImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:`");
93 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:`");
94 | - (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:completed:`");
95 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:completed:`");
96 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:progress:completed:`");
97 |
98 | - (void)cancelCurrentHighlightedImageLoad __deprecated_msg("Use `sd_cancelCurrentHighlightedImageLoad`");
99 |
100 | @end
101 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/UIImageView+HighlightedWebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImageView+HighlightedWebCache.h"
10 | #import "UIView+WebCacheOperation.h"
11 |
12 | #define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage"
13 |
14 | @implementation UIImageView (HighlightedWebCache)
15 |
16 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url {
17 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil];
18 | }
19 |
20 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options {
21 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil];
22 | }
23 |
24 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock {
25 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock];
26 | }
27 |
28 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock {
29 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock];
30 | }
31 |
32 | - (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
33 | [self sd_cancelCurrentHighlightedImageLoad];
34 |
35 | if (url) {
36 | __weak __typeof(self)wself = self;
37 | id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
38 | if (!wself) return;
39 | dispatch_main_sync_safe (^
40 | {
41 | if (!wself) return;
42 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
43 | {
44 | completedBlock(image, error, cacheType, url);
45 | return;
46 | }
47 | else if (image) {
48 | wself.highlightedImage = image;
49 | [wself setNeedsLayout];
50 | }
51 | if (completedBlock && finished) {
52 | completedBlock(image, error, cacheType, url);
53 | }
54 | });
55 | }];
56 | [self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey];
57 | } else {
58 | dispatch_main_async_safe(^{
59 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
60 | if (completedBlock) {
61 | completedBlock(nil, error, SDImageCacheTypeNone, url);
62 | }
63 | });
64 | }
65 | }
66 |
67 | - (void)sd_cancelCurrentHighlightedImageLoad {
68 | [self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey];
69 | }
70 |
71 | @end
72 |
73 |
74 | @implementation UIImageView (HighlightedWebCacheDeprecated)
75 |
76 | - (void)setHighlightedImageWithURL:(NSURL *)url {
77 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil];
78 | }
79 |
80 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options {
81 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil];
82 | }
83 |
84 | - (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock {
85 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
86 | if (completedBlock) {
87 | completedBlock(image, error, cacheType);
88 | }
89 | }];
90 | }
91 |
92 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock {
93 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
94 | if (completedBlock) {
95 | completedBlock(image, error, cacheType);
96 | }
97 | }];
98 | }
99 |
100 | - (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock {
101 | [self sd_setHighlightedImageWithURL:url options:0 progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
102 | if (completedBlock) {
103 | completedBlock(image, error, cacheType);
104 | }
105 | }];
106 | }
107 |
108 | - (void)cancelCurrentHighlightedImageLoad {
109 | [self sd_cancelCurrentHighlightedImageLoad];
110 | }
111 |
112 | @end
113 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Base/MJRefreshHeader.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshHeader.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "MJRefreshHeader.h"
11 |
12 | @interface MJRefreshHeader()
13 | @property (assign, nonatomic) CGFloat insetTDelta;
14 | @end
15 |
16 | @implementation MJRefreshHeader
17 | #pragma mark - 构造方法
18 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
19 | {
20 | MJRefreshHeader *cmp = [[self alloc] init];
21 | cmp.refreshingBlock = refreshingBlock;
22 | return cmp;
23 | }
24 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
25 | {
26 | MJRefreshHeader *cmp = [[self alloc] init];
27 | [cmp setRefreshingTarget:target refreshingAction:action];
28 | return cmp;
29 | }
30 |
31 | #pragma mark - 覆盖父类的方法
32 | - (void)prepare
33 | {
34 | [super prepare];
35 |
36 | // 设置key
37 | self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey;
38 |
39 | // 设置高度
40 | self.mj_h = MJRefreshHeaderHeight;
41 | }
42 |
43 | - (void)placeSubviews
44 | {
45 | [super placeSubviews];
46 |
47 | // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值)
48 | self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop;
49 | }
50 |
51 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
52 | {
53 | [super scrollViewContentOffsetDidChange:change];
54 |
55 | // 在刷新的refreshing状态
56 | if (self.state == MJRefreshStateRefreshing) {
57 | if (self.window == nil) return;
58 |
59 | // sectionheader停留解决
60 | CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;
61 | insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;
62 | self.scrollView.mj_insetT = insetT;
63 |
64 | self.insetTDelta = _scrollViewOriginalInset.top - insetT;
65 | return;
66 | }
67 |
68 | // 跳转到下一个控制器时,contentInset可能会变
69 | _scrollViewOriginalInset = self.scrollView.contentInset;
70 |
71 | // 当前的contentOffset
72 | CGFloat offsetY = self.scrollView.mj_offsetY;
73 | // 头部控件刚好出现的offsetY
74 | CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
75 |
76 | // 如果是向上滚动到看不见头部控件,直接返回
77 | // >= -> >
78 | if (offsetY > happenOffsetY) return;
79 |
80 | // 普通 和 即将刷新 的临界点
81 | CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h;
82 | CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h;
83 |
84 | if (self.scrollView.isDragging) { // 如果正在拖拽
85 | self.pullingPercent = pullingPercent;
86 | if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {
87 | // 转为即将刷新状态
88 | self.state = MJRefreshStatePulling;
89 | } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {
90 | // 转为普通状态
91 | self.state = MJRefreshStateIdle;
92 | }
93 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
94 | // 开始刷新
95 | [self beginRefreshing];
96 | } else if (pullingPercent < 1) {
97 | self.pullingPercent = pullingPercent;
98 | }
99 | }
100 |
101 | - (void)setState:(MJRefreshState)state
102 | {
103 | MJRefreshCheckState
104 |
105 | // 根据状态做事情
106 | if (state == MJRefreshStateIdle) {
107 | if (oldState != MJRefreshStateRefreshing) return;
108 |
109 | // 保存刷新时间
110 | [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];
111 | [[NSUserDefaults standardUserDefaults] synchronize];
112 |
113 | // 恢复inset和offset
114 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
115 | self.scrollView.mj_insetT += self.insetTDelta;
116 |
117 | // 自动调整透明度
118 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
119 | } completion:^(BOOL finished) {
120 | self.pullingPercent = 0.0;
121 | }];
122 | } else if (state == MJRefreshStateRefreshing) {
123 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
124 | // 增加滚动区域
125 | CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
126 | self.scrollView.mj_insetT = top;
127 |
128 | // 设置滚动位置
129 | self.scrollView.mj_offsetY = - top;
130 | } completion:^(BOOL finished) {
131 | [self executeRefreshingCallback];
132 | }];
133 | }
134 | }
135 |
136 | - (void)drawRect:(CGRect)rect
137 | {
138 | [super drawRect:rect];
139 |
140 |
141 | }
142 |
143 | #pragma mark - 公共方法
144 | - (void)endRefreshing
145 | {
146 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
147 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
148 | [super endRefreshing];
149 | });
150 | } else {
151 | [super endRefreshing];
152 | }
153 | }
154 |
155 | - (NSDate *)lastUpdatedTime
156 | {
157 | return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey];
158 | }
159 | @end
160 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/UIImage+GIF.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+GIF.m
3 | // LBGIFImage
4 | //
5 | // Created by Laurin Brandner on 06.01.12.
6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import "UIImage+GIF.h"
10 | #import
11 |
12 | @implementation UIImage (GIF)
13 |
14 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data {
15 | if (!data) {
16 | return nil;
17 | }
18 |
19 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
20 |
21 | size_t count = CGImageSourceGetCount(source);
22 |
23 | UIImage *animatedImage;
24 |
25 | if (count <= 1) {
26 | animatedImage = [[UIImage alloc] initWithData:data];
27 | }
28 | else {
29 | NSMutableArray *images = [NSMutableArray array];
30 |
31 | NSTimeInterval duration = 0.0f;
32 |
33 | for (size_t i = 0; i < count; i++) {
34 | CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
35 | if (!image) {
36 | continue;
37 | }
38 |
39 | duration += [self sd_frameDurationAtIndex:i source:source];
40 |
41 | [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
42 |
43 | CGImageRelease(image);
44 | }
45 |
46 | if (!duration) {
47 | duration = (1.0f / 10.0f) * count;
48 | }
49 |
50 | animatedImage = [UIImage animatedImageWithImages:images duration:duration];
51 | }
52 |
53 | CFRelease(source);
54 |
55 | return animatedImage;
56 | }
57 |
58 | + (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
59 | float frameDuration = 0.1f;
60 | CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
61 | NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
62 | NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
63 |
64 | NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
65 | if (delayTimeUnclampedProp) {
66 | frameDuration = [delayTimeUnclampedProp floatValue];
67 | }
68 | else {
69 |
70 | NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
71 | if (delayTimeProp) {
72 | frameDuration = [delayTimeProp floatValue];
73 | }
74 | }
75 |
76 | // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
77 | // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
78 | // a duration of <= 10 ms. See and
79 | // for more information.
80 |
81 | if (frameDuration < 0.011f) {
82 | frameDuration = 0.100f;
83 | }
84 |
85 | CFRelease(cfFrameProperties);
86 | return frameDuration;
87 | }
88 |
89 | + (UIImage *)sd_animatedGIFNamed:(NSString *)name {
90 | CGFloat scale = [UIScreen mainScreen].scale;
91 |
92 | if (scale > 1.0f) {
93 | NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"];
94 |
95 | NSData *data = [NSData dataWithContentsOfFile:retinaPath];
96 |
97 | if (data) {
98 | return [UIImage sd_animatedGIFWithData:data];
99 | }
100 |
101 | NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
102 |
103 | data = [NSData dataWithContentsOfFile:path];
104 |
105 | if (data) {
106 | return [UIImage sd_animatedGIFWithData:data];
107 | }
108 |
109 | return [UIImage imageNamed:name];
110 | }
111 | else {
112 | NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
113 |
114 | NSData *data = [NSData dataWithContentsOfFile:path];
115 |
116 | if (data) {
117 | return [UIImage sd_animatedGIFWithData:data];
118 | }
119 |
120 | return [UIImage imageNamed:name];
121 | }
122 | }
123 |
124 | - (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size {
125 | if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) {
126 | return self;
127 | }
128 |
129 | CGSize scaledSize = size;
130 | CGPoint thumbnailPoint = CGPointZero;
131 |
132 | CGFloat widthFactor = size.width / self.size.width;
133 | CGFloat heightFactor = size.height / self.size.height;
134 | CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor;
135 | scaledSize.width = self.size.width * scaleFactor;
136 | scaledSize.height = self.size.height * scaleFactor;
137 |
138 | if (widthFactor > heightFactor) {
139 | thumbnailPoint.y = (size.height - scaledSize.height) * 0.5;
140 | }
141 | else if (widthFactor < heightFactor) {
142 | thumbnailPoint.x = (size.width - scaledSize.width) * 0.5;
143 | }
144 |
145 | NSMutableArray *scaledImages = [NSMutableArray array];
146 |
147 | for (UIImage *image in self.images) {
148 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
149 |
150 | [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)];
151 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
152 |
153 | [scaledImages addObject:newImage];
154 |
155 | UIGraphicsEndImageContext();
156 | }
157 |
158 | return [UIImage animatedImageWithImages:scaledImages duration:self.duration];
159 | }
160 |
161 | @end
162 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/SDWebImage/MKAnnotationView+WebCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // MKAnnotationView+WebCache.m
3 | // SDWebImage
4 | //
5 | // Created by Olivier Poitrey on 14/03/12.
6 | // Copyright (c) 2012 Dailymotion. All rights reserved.
7 | //
8 |
9 | #import "MKAnnotationView+WebCache.h"
10 | #import "objc/runtime.h"
11 | #import "UIView+WebCacheOperation.h"
12 |
13 | static char imageURLKey;
14 |
15 | @implementation MKAnnotationView (WebCache)
16 |
17 | - (NSURL *)sd_imageURL {
18 | return objc_getAssociatedObject(self, &imageURLKey);
19 | }
20 |
21 | - (void)sd_setImageWithURL:(NSURL *)url {
22 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
23 | }
24 |
25 | - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
26 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
27 | }
28 |
29 | - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
30 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
31 | }
32 |
33 | - (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock {
34 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock];
35 | }
36 |
37 | - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock {
38 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock];
39 | }
40 |
41 | - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock {
42 | [self sd_cancelCurrentImageLoad];
43 |
44 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
45 | self.image = placeholder;
46 |
47 | if (url) {
48 | __weak __typeof(self)wself = self;
49 | id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
50 | if (!wself) return;
51 | dispatch_main_sync_safe(^{
52 | __strong MKAnnotationView *sself = wself;
53 | if (!sself) return;
54 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
55 | completedBlock(image, error, cacheType, url);
56 | return;
57 | } else if (image) {
58 | wself.image = image;
59 | [wself setNeedsLayout];
60 | } else {
61 | if ((options & SDWebImageDelayPlaceholder)) {
62 | wself.image = placeholder;
63 | [wself setNeedsLayout];
64 | }
65 | }
66 | if (completedBlock && finished) {
67 | completedBlock(image, error, cacheType, url);
68 | }
69 | });
70 | }];
71 | [self sd_setImageLoadOperation:operation forKey:@"MKAnnotationViewImage"];
72 | } else {
73 | dispatch_main_async_safe(^{
74 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
75 | if (completedBlock) {
76 | completedBlock(nil, error, SDImageCacheTypeNone, url);
77 | }
78 | });
79 | }
80 | }
81 |
82 | - (void)sd_cancelCurrentImageLoad {
83 | [self sd_cancelImageLoadOperationWithKey:@"MKAnnotationViewImage"];
84 | }
85 |
86 | @end
87 |
88 |
89 | @implementation MKAnnotationView (WebCacheDeprecated)
90 |
91 | - (NSURL *)imageURL {
92 | return [self sd_imageURL];
93 | }
94 |
95 | - (void)setImageWithURL:(NSURL *)url {
96 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
97 | }
98 |
99 | - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
100 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
101 | }
102 |
103 | - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
104 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
105 | }
106 |
107 | - (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock {
108 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
109 | if (completedBlock) {
110 | completedBlock(image, error, cacheType);
111 | }
112 | }];
113 | }
114 |
115 | - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock {
116 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
117 | if (completedBlock) {
118 | completedBlock(image, error, cacheType);
119 | }
120 | }];
121 | }
122 |
123 | - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock {
124 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
125 | if (completedBlock) {
126 | completedBlock(image, error, cacheType);
127 | }
128 | }];
129 | }
130 |
131 | - (void)cancelCurrentImageLoad {
132 | [self sd_cancelCurrentImageLoad];
133 | }
134 |
135 | @end
136 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJExtension/NSObject+MJClass.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJClass.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/8/11.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSObject+MJClass.h"
10 | #import "NSObject+MJCoding.h"
11 | #import "NSObject+MJKeyValue.h"
12 | #import "MJFoundation.h"
13 | #import
14 |
15 | static const char MJAllowedPropertyNamesKey = '\0';
16 | static const char MJIgnoredPropertyNamesKey = '\0';
17 | static const char MJAllowedCodingPropertyNamesKey = '\0';
18 | static const char MJIgnoredCodingPropertyNamesKey = '\0';
19 |
20 | static NSMutableDictionary *allowedPropertyNamesDict_;
21 | static NSMutableDictionary *ignoredPropertyNamesDict_;
22 | static NSMutableDictionary *allowedCodingPropertyNamesDict_;
23 | static NSMutableDictionary *ignoredCodingPropertyNamesDict_;
24 |
25 | @implementation NSObject (MJClass)
26 |
27 | + (void)load
28 | {
29 | allowedPropertyNamesDict_ = [NSMutableDictionary dictionary];
30 | ignoredPropertyNamesDict_ = [NSMutableDictionary dictionary];
31 | allowedCodingPropertyNamesDict_ = [NSMutableDictionary dictionary];
32 | ignoredCodingPropertyNamesDict_ = [NSMutableDictionary dictionary];
33 | }
34 |
35 | + (NSMutableDictionary *)dictForKey:(const void *)key
36 | {
37 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_;
38 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_;
39 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_;
40 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_;
41 | return nil;
42 | }
43 |
44 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration
45 | {
46 | // 1.没有block就直接返回
47 | if (enumeration == nil) return;
48 |
49 | // 2.停止遍历的标记
50 | BOOL stop = NO;
51 |
52 | // 3.当前正在遍历的类
53 | Class c = self;
54 |
55 | // 4.开始遍历每一个类
56 | while (c && !stop) {
57 | // 4.1.执行操作
58 | enumeration(c, &stop);
59 |
60 | // 4.2.获得父类
61 | c = class_getSuperclass(c);
62 |
63 | if ([MJFoundation isClassFromFoundation:c]) break;
64 | }
65 | }
66 |
67 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration
68 | {
69 | // 1.没有block就直接返回
70 | if (enumeration == nil) return;
71 |
72 | // 2.停止遍历的标记
73 | BOOL stop = NO;
74 |
75 | // 3.当前正在遍历的类
76 | Class c = self;
77 |
78 | // 4.开始遍历每一个类
79 | while (c && !stop) {
80 | // 4.1.执行操作
81 | enumeration(c, &stop);
82 |
83 | // 4.2.获得父类
84 | c = class_getSuperclass(c);
85 | }
86 | }
87 |
88 | #pragma mark - 属性黑名单配置
89 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames
90 | {
91 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey];
92 | }
93 |
94 | + (NSMutableArray *)mj_totalIgnoredPropertyNames
95 | {
96 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey];
97 | }
98 |
99 | #pragma mark - 归档属性黑名单配置
100 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames
101 | {
102 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey];
103 | }
104 |
105 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames
106 | {
107 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey];
108 | }
109 |
110 | #pragma mark - 属性白名单配置
111 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames;
112 | {
113 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey];
114 | }
115 |
116 | + (NSMutableArray *)mj_totalAllowedPropertyNames
117 | {
118 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey];
119 | }
120 |
121 | #pragma mark - 归档属性白名单配置
122 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames
123 | {
124 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey];
125 | }
126 |
127 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames
128 | {
129 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey];
130 | }
131 | #pragma mark - block和方法处理:存储block的返回值
132 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key
133 | {
134 | if (block) {
135 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
136 | } else {
137 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
138 | }
139 |
140 | // 清空数据
141 | [[self dictForKey:key] removeAllObjects];
142 | }
143 |
144 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key
145 | {
146 | NSMutableArray *array = [self dictForKey:key][NSStringFromClass(self)];
147 | if (array) return array;
148 |
149 | // 创建、存储
150 | [self dictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array];
151 |
152 | if ([self respondsToSelector:selector]) {
153 | #pragma clang diagnostic push
154 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
155 | NSArray *subArray = [self performSelector:selector];
156 | #pragma clang diagnostic pop
157 | if (subArray) {
158 | [array addObjectsFromArray:subArray];
159 | }
160 | }
161 |
162 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
163 | NSArray *subArray = objc_getAssociatedObject(c, key);
164 | [array addObjectsFromArray:subArray];
165 | }];
166 | return array;
167 | }
168 | @end
169 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJRefresh/Custom/Header/MJRefreshStateHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshStateHeader.h"
10 |
11 | @interface MJRefreshStateHeader()
12 | {
13 | /** 显示上一次刷新时间的label */
14 | __unsafe_unretained UILabel *_lastUpdatedTimeLabel;
15 | /** 显示刷新状态的label */
16 | __unsafe_unretained UILabel *_stateLabel;
17 | }
18 | /** 所有状态对应的文字 */
19 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
20 | @end
21 |
22 | @implementation MJRefreshStateHeader
23 | #pragma mark - 懒加载
24 | - (NSMutableDictionary *)stateTitles
25 | {
26 | if (!_stateTitles) {
27 | self.stateTitles = [NSMutableDictionary dictionary];
28 | }
29 | return _stateTitles;
30 | }
31 |
32 | - (UILabel *)stateLabel
33 | {
34 | if (!_stateLabel) {
35 | [self addSubview:_stateLabel = [UILabel mj_label]];
36 | }
37 | return _stateLabel;
38 | }
39 |
40 | - (UILabel *)lastUpdatedTimeLabel
41 | {
42 | if (!_lastUpdatedTimeLabel) {
43 | [self addSubview:_lastUpdatedTimeLabel = [UILabel mj_label]];
44 | }
45 | return _lastUpdatedTimeLabel;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state
50 | {
51 | if (title == nil) return;
52 | self.stateTitles[@(state)] = title;
53 | self.stateLabel.text = self.stateTitles[@(self.state)];
54 | }
55 |
56 | #pragma mark - 日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。
57 | - (NSCalendar *)currentCalendar {
58 | if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) {
59 | return [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
60 | }
61 | return [NSCalendar currentCalendar];
62 | }
63 |
64 | #pragma mark key的处理
65 | - (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey
66 | {
67 | [super setLastUpdatedTimeKey:lastUpdatedTimeKey];
68 |
69 | // 如果label隐藏了,就不用再处理
70 | if (self.lastUpdatedTimeLabel.hidden) return;
71 |
72 | NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey];
73 |
74 | // 如果有block
75 | if (self.lastUpdatedTimeText) {
76 | self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime);
77 | return;
78 | }
79 |
80 | if (lastUpdatedTime) {
81 | // 1.获得年月日
82 | NSCalendar *calendar = [self currentCalendar];
83 | NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute;
84 | NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime];
85 | NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]];
86 |
87 | // 2.格式化日期
88 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
89 | BOOL isToday = NO;
90 | if ([cmp1 day] == [cmp2 day]) { // 今天
91 | formatter.dateFormat = @" HH:mm";
92 | isToday = YES;
93 | } else if ([cmp1 year] == [cmp2 year]) { // 今年
94 | formatter.dateFormat = @"MM-dd HH:mm";
95 | } else {
96 | formatter.dateFormat = @"yyyy-MM-dd HH:mm";
97 | }
98 | NSString *time = [formatter stringFromDate:lastUpdatedTime];
99 |
100 | // 3.显示日期
101 | self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@%@",
102 | [self localizedStringForKey:MJRefreshHeaderLastTimeText],
103 | isToday ? [self localizedStringForKey:MJRefreshHeaderDateTodayText] : @"",
104 | time];
105 | } else {
106 | self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@",
107 | [self localizedStringForKey:MJRefreshHeaderLastTimeText],
108 | [self localizedStringForKey:MJRefreshHeaderNoneLastDateText]];
109 | }
110 | }
111 |
112 | #pragma mark - 覆盖父类的方法
113 | - (void)prepare
114 | {
115 | [super prepare];
116 |
117 | // 初始化文字
118 | [self setTitle:[self localizedStringForKey:MJRefreshHeaderIdleText] forState:MJRefreshStateIdle];
119 | [self setTitle:[self localizedStringForKey:MJRefreshHeaderPullingText] forState:MJRefreshStatePulling];
120 | [self setTitle:[self localizedStringForKey:MJRefreshHeaderRefreshingText] forState:MJRefreshStateRefreshing];
121 | }
122 |
123 | - (void)placeSubviews
124 | {
125 | [super placeSubviews];
126 |
127 | if (self.stateLabel.hidden) return;
128 |
129 | BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0;
130 |
131 | if (self.lastUpdatedTimeLabel.hidden) {
132 | // 状态
133 | if (noConstrainsOnStatusLabel) self.stateLabel.frame = self.bounds;
134 | } else {
135 | CGFloat stateLabelH = self.mj_h * 0.5;
136 | // 状态
137 | if (noConstrainsOnStatusLabel) {
138 | self.stateLabel.mj_x = 0;
139 | self.stateLabel.mj_y = 0;
140 | self.stateLabel.mj_w = self.mj_w;
141 | self.stateLabel.mj_h = stateLabelH;
142 | }
143 |
144 | // 更新时间
145 | if (self.lastUpdatedTimeLabel.constraints.count == 0) {
146 | self.lastUpdatedTimeLabel.mj_x = 0;
147 | self.lastUpdatedTimeLabel.mj_y = stateLabelH;
148 | self.lastUpdatedTimeLabel.mj_w = self.mj_w;
149 | self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y;
150 | }
151 | }
152 | }
153 |
154 | - (void)setState:(MJRefreshState)state
155 | {
156 | MJRefreshCheckState
157 |
158 | // 设置状态文字
159 | self.stateLabel.text = self.stateTitles[@(state)];
160 |
161 | // 重新设置key(重新显示时间)
162 | self.lastUpdatedTimeKey = self.lastUpdatedTimeKey;
163 | }
164 | @end
165 |
--------------------------------------------------------------------------------
/WaterFallLayout/Lib/MJExtension/MJProperty.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJProperty.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/4/17.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJProperty.h"
10 | #import "MJFoundation.h"
11 | #import "MJExtensionConst.h"
12 | #import
13 |
14 | @interface MJProperty()
15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict;
16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict;
17 | @end
18 |
19 | @implementation MJProperty
20 |
21 | #pragma mark - 懒加载
22 | - (NSMutableDictionary *)propertyKeysDict
23 | {
24 | if (!_propertyKeysDict) {
25 | _propertyKeysDict = [NSMutableDictionary dictionary];
26 | }
27 | return _propertyKeysDict;
28 | }
29 |
30 | - (NSMutableDictionary *)objectClassInArrayDict
31 | {
32 | if (!_objectClassInArrayDict) {
33 | _objectClassInArrayDict = [NSMutableDictionary dictionary];
34 | }
35 | return _objectClassInArrayDict;
36 | }
37 |
38 | #pragma mark - 缓存
39 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property
40 | {
41 | MJProperty *propertyObj = objc_getAssociatedObject(self, property);
42 | if (propertyObj == nil) {
43 | propertyObj = [[self alloc] init];
44 | propertyObj.property = property;
45 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
46 | }
47 | return propertyObj;
48 | }
49 |
50 | #pragma mark - 公共方法
51 | - (void)setProperty:(objc_property_t)property
52 | {
53 | _property = property;
54 |
55 | MJExtensionAssertParamNotNil(property);
56 |
57 | // 1.属性名
58 | _name = @(property_getName(property));
59 |
60 | // 2.成员类型
61 | NSString *attrs = @(property_getAttributes(property));
62 | NSUInteger dotLoc = [attrs rangeOfString:@","].location;
63 | NSString *code = nil;
64 | NSUInteger loc = 1;
65 | if (dotLoc == NSNotFound) { // 没有,
66 | code = [attrs substringFromIndex:loc];
67 | } else {
68 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)];
69 | }
70 | _type = [MJPropertyType cachedTypeWithCode:code];
71 | }
72 |
73 | /**
74 | * 获得成员变量的值
75 | */
76 | - (id)valueForObject:(id)object
77 | {
78 | if (self.type.KVCDisabled) return [NSNull null];
79 | return [object valueForKey:self.name];
80 | }
81 |
82 | /**
83 | * 设置成员变量的值
84 | */
85 | - (void)setValue:(id)value forObject:(id)object
86 | {
87 | if (self.type.KVCDisabled || value == nil) return;
88 | [object setValue:value forKey:self.name];
89 | }
90 |
91 | /**
92 | * 通过字符串key创建对应的keys
93 | */
94 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey
95 | {
96 | if (stringKey.length == 0) return nil;
97 |
98 | NSMutableArray *propertyKeys = [NSMutableArray array];
99 | // 如果有多级映射
100 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."];
101 |
102 | for (NSString *oldKey in oldKeys) {
103 | NSUInteger start = [oldKey rangeOfString:@"["].location;
104 | if (start != NSNotFound) { // 有索引的key
105 | NSString *prefixKey = [oldKey substringToIndex:start];
106 | NSString *indexKey = prefixKey;
107 | if (prefixKey.length) {
108 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
109 | propertyKey.name = prefixKey;
110 | [propertyKeys addObject:propertyKey];
111 |
112 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""];
113 | }
114 |
115 | /** 解析索引 **/
116 | // 元素
117 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"];
118 | for (NSInteger i = 0; i normal2pullingOffsetY) {
57 | // 转为即将刷新状态
58 | self.state = MJRefreshStatePulling;
59 | } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) {
60 | // 转为普通状态
61 | self.state = MJRefreshStateIdle;
62 | }
63 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
64 | // 开始刷新
65 | [self beginRefreshing];
66 | } else if (pullingPercent < 1) {
67 | self.pullingPercent = pullingPercent;
68 | }
69 | }
70 |
71 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
72 | {
73 | [super scrollViewContentSizeDidChange:change];
74 |
75 | // 内容的高度
76 | CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
77 | // 表格的高度
78 | CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
79 | // 设置位置和尺寸
80 | self.mj_y = MAX(contentHeight, scrollHeight);
81 | }
82 |
83 | - (void)setState:(MJRefreshState)state
84 | {
85 | MJRefreshCheckState
86 |
87 | // 根据状态来设置属性
88 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
89 | // 刷新完毕
90 | if (MJRefreshStateRefreshing == oldState) {
91 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
92 | self.scrollView.mj_insetB -= self.lastBottomDelta;
93 |
94 | // 自动调整透明度
95 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
96 | } completion:^(BOOL finished) {
97 | self.pullingPercent = 0.0;
98 | }];
99 | }
100 |
101 | CGFloat deltaH = [self heightForContentBreakView];
102 | // 刚刷新完毕
103 | if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
104 | self.scrollView.mj_offsetY = self.scrollView.mj_offsetY;
105 | }
106 | } else if (state == MJRefreshStateRefreshing) {
107 | // 记录刷新前的数量
108 | self.lastRefreshCount = self.scrollView.mj_totalDataCount;
109 |
110 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
111 | CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom;
112 | CGFloat deltaH = [self heightForContentBreakView];
113 | if (deltaH < 0) { // 如果内容高度小于view的高度
114 | bottom -= deltaH;
115 | }
116 | self.lastBottomDelta = bottom - self.scrollView.mj_insetB;
117 | self.scrollView.mj_insetB = bottom;
118 | self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h;
119 | } completion:^(BOOL finished) {
120 | [self executeRefreshingCallback];
121 | }];
122 | }
123 | }
124 |
125 | #pragma mark - 公共方法
126 | - (void)endRefreshing
127 | {
128 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
129 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
130 | [super endRefreshing];
131 | });
132 | } else {
133 | [super endRefreshing];
134 | }
135 | }
136 |
137 | - (void)noticeNoMoreData
138 | {
139 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
140 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
141 | [super noticeNoMoreData];
142 | });
143 | } else {
144 | [super noticeNoMoreData];
145 | }
146 | }
147 |
148 | #pragma mark - 私有方法
149 | #pragma mark 获得scrollView的内容 超出 view 的高度
150 | - (CGFloat)heightForContentBreakView
151 | {
152 | CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
153 | return self.scrollView.contentSize.height - h;
154 | }
155 |
156 | #pragma mark 刚好看到上拉刷新控件时的contentOffset.y
157 | - (CGFloat)happenOffsetY
158 | {
159 | CGFloat deltaH = [self heightForContentBreakView];
160 | if (deltaH > 0) {
161 | return deltaH - self.scrollViewOriginalInset.top;
162 | } else {
163 | return - self.scrollViewOriginalInset.top;
164 | }
165 | }
166 | @end
167 |
--------------------------------------------------------------------------------