├── .gitattributes
├── GIF
├── 自定义.png
├── 默认样式.png
├── 隐藏提示或动画.png
└── 没有更多数据提示.GIF
├── CollectionViewSideRefresh
├── Assets.xcassets
│ ├── Contents.json
│ ├── loading_01.imageset
│ │ ├── loading_01.png
│ │ └── Contents.json
│ ├── loading_02.imageset
│ │ ├── loading_02.png
│ │ └── Contents.json
│ ├── loading_03.imageset
│ │ ├── loading_03.png
│ │ └── Contents.json
│ ├── loading_04.imageset
│ │ ├── loading_04.png
│ │ └── Contents.json
│ ├── loading_05.imageset
│ │ ├── loading_05.png
│ │ └── Contents.json
│ ├── loading_06.imageset
│ │ ├── loading_06.png
│ │ └── Contents.json
│ ├── loading_07.imageset
│ │ ├── loading_07.png
│ │ └── Contents.json
│ ├── loading_08.imageset
│ │ ├── loading_08.png
│ │ └── Contents.json
│ ├── loading_09.imageset
│ │ ├── loading_09.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── ViewController.h
├── SideRefresh
│ ├── SideRefreshFooter.h
│ ├── SideRefreshHeader.h
│ ├── SideRefreshEmptyFooter.h
│ ├── UICollectionView+SideExtension.h
│ ├── UICollectionView+SideRefresh.h
│ ├── UICollectionView+SideExtension.m
│ ├── SideRefreshHeader.m
│ ├── SideRefreshEmptyFooter.m
│ ├── SideRefresh.h
│ ├── SideRefreshFooter.m
│ ├── UICollectionView+SideRefresh.m
│ └── SideRefresh.m
├── SideRefreshDemo
│ ├── CustomViewController.h
│ ├── DefaultViewController.h
│ ├── HideItemViewController.h
│ ├── EmptyFooterViewController.h
│ ├── MyCollectionViewCell.h
│ ├── MyCollectionViewCell.m
│ ├── DefaultViewController.m
│ ├── HideItemViewController.m
│ ├── MyCollectionViewCell.xib
│ ├── EmptyFooterViewController.m
│ └── CustomViewController.m
├── AppDelegate.h
├── main.m
├── ViewController.m
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
└── AppDelegate.m
├── CollectionViewSideRefresh.xcodeproj
├── xcuserdata
│ └── hcb.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── hcb.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/GIF/自定义.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/GIF/自定义.png
--------------------------------------------------------------------------------
/GIF/默认样式.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/GIF/默认样式.png
--------------------------------------------------------------------------------
/GIF/隐藏提示或动画.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/GIF/隐藏提示或动画.png
--------------------------------------------------------------------------------
/GIF/没有更多数据提示.GIF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/GIF/没有更多数据提示.GIF
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/xcuserdata/hcb.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_01.imageset/loading_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_01.imageset/loading_01.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_02.imageset/loading_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_02.imageset/loading_02.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_03.imageset/loading_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_03.imageset/loading_03.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_04.imageset/loading_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_04.imageset/loading_04.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_05.imageset/loading_05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_05.imageset/loading_05.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_06.imageset/loading_06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_06.imageset/loading_06.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_07.imageset/loading_07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_07.imageset/loading_07.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_08.imageset/loading_08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_08.imageset/loading_08.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_09.imageset/loading_09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh/Assets.xcassets/loading_09.imageset/loading_09.png
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/project.xcworkspace/xcuserdata/hcb.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/CollectionViewSideRefresh/HEAD/CollectionViewSideRefresh.xcodeproj/project.xcworkspace/xcuserdata/hcb.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshFooter.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefresh.h"
10 |
11 | @interface SideRefreshFooter : SideRefresh
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshHeader.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefresh.h"
10 |
11 | @interface SideRefreshHeader : SideRefresh
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/CustomViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // CustomViewController.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CustomViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/DefaultViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultViewController.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DefaultViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/HideItemViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // HideItemViewController.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HideItemViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/EmptyFooterViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyFooterViewController.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/21.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface EmptyFooterViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/MyCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // MyCollectionViewCell.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MyCollectionViewCell : UICollectionViewCell
12 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/MyCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // MyCollectionViewCell.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "MyCollectionViewCell.h"
10 |
11 | @implementation MyCollectionViewCell
12 |
13 | - (void)awakeFromNib {
14 | [super awakeFromNib];
15 | // Initialization code
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_01.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_01.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_02.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_02.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_03.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_03.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_04.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_04.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_05.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_05.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_06.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_06.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_07.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_07.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_08.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_08.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/loading_09.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "loading_09.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshEmptyFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshEmptyFooter.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/20.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | static const CGFloat SideRefreshEmptyFooterWidth = 50.0;
12 |
13 | @interface SideRefreshEmptyFooter : UIView
14 |
15 | @property (nonatomic, copy) NSString *message;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/xcuserdata/hcb.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CollectionViewSideRefresh.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/UICollectionView+SideExtension.h:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+SideExtension.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by dangercheng on 2018/9/12.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UICollectionView (SideExtension)
12 | @property (nonatomic, assign, readonly) UIEdgeInsets side_inset;
13 | @property (nonatomic, assign) CGFloat side_insetL;
14 | @property (nonatomic, assign) CGFloat side_insetR;
15 | @end
16 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/UICollectionView+SideRefresh.h:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+SideRefresh.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "SideRefreshHeader.h"
11 | #import "SideRefreshFooter.h"
12 | #import "SideRefreshEmptyFooter.h"
13 |
14 | @interface UICollectionView (SideRefresh)
15 |
16 | /**
17 | 刷新头
18 | */
19 | @property (nonatomic, strong) SideRefreshFooter *sideRefreshFooter;
20 |
21 | /**
22 | 刷新脚
23 | */
24 | @property (nonatomic, strong) SideRefreshHeader *sideRefreshHeader;
25 |
26 | /**
27 | 是否已显示空刷新脚
28 | */
29 | @property (nonatomic, assign, readonly) BOOL isShowEmptyFooter;
30 |
31 | /**
32 | 显示默认空刷新脚,默认提示:"没有更多数据了"
33 | */
34 | - (void)showEmptyFooter;
35 |
36 | /**
37 | 显示自定义提示内容的空刷新脚
38 |
39 | @param message 提示内容
40 | */
41 | - (void)showEmptyFooterWithMessage:(NSString *)message;
42 |
43 | /**
44 | 隐藏空刷新脚
45 | */
46 | - (void)hideEmptyFooter;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "DefaultViewController.h"
11 | #import "CustomViewController.h"
12 | #import "HideItemViewController.h"
13 | #import "EmptyFooterViewController.h"
14 |
15 | @interface ViewController ()
16 |
17 | @end
18 |
19 | @implementation ViewController
20 |
21 | - (IBAction)clickDefault:(id)sender {
22 | DefaultViewController *defaultController = [self.storyboard instantiateViewControllerWithIdentifier:@"DefaultViewController"];
23 | [self.navigationController pushViewController:defaultController animated:YES];
24 | }
25 |
26 | - (IBAction)clickCustom:(id)sender {
27 | CustomViewController *customController = [self.storyboard instantiateViewControllerWithIdentifier:@"CustomViewController"];
28 | [self.navigationController pushViewController:customController animated:YES];
29 | }
30 |
31 | - (IBAction)clickShowHide:(id)sender {
32 | HideItemViewController *hideItemController = [self.storyboard instantiateViewControllerWithIdentifier:@"HideItemViewController"];
33 | [self.navigationController pushViewController:hideItemController animated:YES];
34 | }
35 | - (IBAction)clickEmptyFooter:(id)sender {
36 | EmptyFooterViewController *emptyFooterViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"EmptyFooterViewController"];
37 | [self.navigationController pushViewController:emptyFooterViewController animated:YES];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/UICollectionView+SideExtension.m:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+SideExtension.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by dangercheng on 2018/9/12.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "UICollectionView+SideExtension.h"
10 |
11 | @implementation UICollectionView (SideExtension)
12 | static BOOL respondsToAdjustedContentInset_;
13 |
14 | + (void)initialize
15 | {
16 | static dispatch_once_t onceToken;
17 | dispatch_once(&onceToken, ^{
18 | respondsToAdjustedContentInset_ = [self instancesRespondToSelector:@selector(adjustedContentInset)];
19 | });
20 | }
21 |
22 | - (UIEdgeInsets)side_inset
23 | {
24 | #ifdef __IPHONE_11_0
25 | if (respondsToAdjustedContentInset_) {
26 | return self.adjustedContentInset;
27 | }
28 | #endif
29 | return self.contentInset;
30 | }
31 |
32 | - (CGFloat)side_insetL
33 | {
34 | return self.side_inset.left;
35 | }
36 |
37 | - (void)setSide_insetL:(CGFloat)side_insetL
38 | {
39 | UIEdgeInsets inset = self.contentInset;
40 | inset.left = side_insetL;
41 | #ifdef __IPHONE_11_0
42 | if (respondsToAdjustedContentInset_) {
43 | inset.left -= (self.adjustedContentInset.left - self.contentInset.left);
44 | }
45 | #endif
46 | self.contentInset = inset;
47 | }
48 |
49 | - (CGFloat)side_insetR {
50 | return self.side_inset.right;
51 | }
52 |
53 | - (void)setSide_insetR:(CGFloat)side_insetR {
54 | UIEdgeInsets inset = self.contentInset;
55 | inset.right = side_insetR;
56 | #ifdef __IPHONE_11_0
57 | if (respondsToAdjustedContentInset_) {
58 | inset.right -= (self.adjustedContentInset.right - self.contentInset.right);
59 | }
60 | #endif
61 | self.contentInset = inset;
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshHeader.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefreshHeader.h"
10 | #import "UICollectionView+SideExtension.h"
11 |
12 | @implementation SideRefreshHeader
13 |
14 | + (instancetype)refreshWithLoadAction:(loadAction)loadActionBlock {
15 | SideRefreshHeader *freshHeader = [[SideRefreshHeader alloc] init];
16 | freshHeader.loadActionBlock = loadActionBlock;
17 | return freshHeader;
18 | }
19 |
20 | - (instancetype)init {
21 | self = [super init];
22 | if(self) {
23 | self.normalMessage = @"右拉刷新...";
24 | self.pullingMessage = @"松开刷新...";
25 | self.loadingMessage = @"刷新中...";
26 | self.hidden = YES;
27 | }
28 | return self;
29 | }
30 |
31 | - (void)updateContentOffsetToNormal {
32 | self.collectionView.pagingEnabled = self.collectionViewPageEnabel;
33 | [UIView animateWithDuration:0.3 animations:^{
34 | [self.collectionView setContentInset:self.originalContentInset];
35 | }];
36 | }
37 |
38 | - (void)updateContentOffsetToLoading {
39 | self.collectionView.pagingEnabled = NO;
40 | CGFloat targetLeft = (SideRefreshWidth + self.originalContentInset.left);
41 | [UIView animateWithDuration:0.3 animations:^{
42 | [self.collectionView setSide_insetL:targetLeft];
43 | }];
44 | }
45 |
46 | - (BOOL)pullToReadyRefresh {
47 | CGFloat offsetX = self.collectionView.contentOffset.x * -1;
48 | offsetX = offsetX - self.collectionView.contentInset.left;
49 | return offsetX > SideRefreshWidth;
50 | }
51 |
52 | - (void)resetRefreshFrame {
53 | CGFloat frameX = (SideRefreshWidth + self.originalContentInset.left) * - 1;
54 | self.frame = CGRectMake(frameX, 0, SideRefreshWidth, self.collectionView.frame.size.height);
55 | self.hidden = NO;
56 | }
57 |
58 | - (void)keepRefreshingOffset {
59 | if(!self.collectionViewPageEnabel) {
60 | return;
61 | }
62 | CGFloat targetX = (SideRefreshWidth + self.originalContentInset.left) * - 1;
63 | [self.collectionView setContentOffset:CGPointMake(targetX, self.collectionView.contentOffset.y) animated:NO];
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshEmptyFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshEmptyFooter.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/20.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefreshEmptyFooter.h"
10 |
11 | @interface SideRefreshEmptyFooter()
12 |
13 | @property (nonatomic, strong) UILabel *messageLabel;
14 |
15 | @property (nonatomic, weak) UICollectionView *collectionView;
16 |
17 | @end
18 |
19 | @implementation SideRefreshEmptyFooter
20 |
21 | - (instancetype)initWithFrame:(CGRect)frame {
22 | self = [super initWithFrame:frame];
23 | if(self) {
24 | [self setupMessageLabel];
25 | }
26 | return self;
27 | }
28 |
29 | - (void)willMoveToSuperview:(UIView *)newSuperview {
30 | [super willMoveToSuperview:newSuperview];
31 | [self.superview removeObserver:self forKeyPath:@"contentSize"];
32 | if([newSuperview isKindOfClass:[UICollectionView class]]) {
33 | self.collectionView = (UICollectionView *)newSuperview;
34 | NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
35 | [self.collectionView addObserver:self forKeyPath:@"contentSize" options:options context:nil];
36 | self.backgroundColor = self.collectionView.backgroundColor;
37 | }
38 | }
39 |
40 | - (void)setMessage:(NSString *)message {
41 | _message = message;
42 | self.messageLabel.text = message;
43 | }
44 |
45 | - (void)setupMessageLabel {
46 | CGFloat labelWidth = 15;
47 | CGFloat labelX = (self.frame.size.width - labelWidth) * 0.5;
48 | _messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelX, 0, labelWidth, self.frame.size.height)];
49 | _messageLabel.numberOfLines = 0;
50 | _messageLabel.font = [UIFont systemFontOfSize:12];
51 | _messageLabel.textColor = [UIColor colorWithRed:102/255.0 green:102/255.0 blue:102/255.0 alpha:1/1.0];
52 | _messageLabel.textAlignment = NSTextAlignmentCenter;
53 | [self addSubview:_messageLabel];
54 | }
55 |
56 | #pragma mark - ContentOffset Observer
57 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
58 | if([keyPath isEqualToString:@"contentSize"]) {
59 | CGFloat frameX = self.collectionView.contentSize.width + self.collectionView.contentInset.right;
60 | self.frame = CGRectMake(frameX, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
61 | }
62 | }
63 | @end
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 一款简单好用的UICollectionView横向滚动刷新控件
2 |
3 | [源码地址:(GitHub)](https://github.com/dangercheng/CollectionViewSideRefresh)
4 |
5 | # 实现功能:
6 |
7 | - **接入简单,不侵入代码**
8 | - **右拉刷新,左拉加载更多**
9 | - **自定义提示文案和加载动画**
10 | - **方便隐藏和显示加载提示文本和动画**
11 | - **支持 ‘没有更多数据’ 提示**
12 |
13 | # 接入方式
14 | 1. 下载源码,将文件夹 "SideRefresh"添加进工程
15 | 2. 引入头文件 #import "UICollectionView+SideRefresh.h"
16 | 3. 创建SideRefreshHeader和SideRefreshFooter并添加给UICollectionView即可
17 |
18 | ## 默认刷新样式
19 | ```
20 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
21 | //执行刷新
22 | }];
23 | self.collectionView.sideRefreshHeader = refreshHeader;
24 |
25 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
26 | //执行加载更多
27 | }];
28 | self.collectionView.sideRefreshFooter = refreshFooter;
29 | ```
30 | 
31 |
32 | ## 自定义刷新提示和图片动画
33 | ```
34 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
35 | //执行刷新
36 | }];
37 | NSMutableArray *loadingImages = [NSMutableArray array];
38 | for(int i = 1; i <= 9; i++) {
39 | UIImage *loadingImg = [UIImage imageNamed:[NSString stringWithFormat:@"loading_0%d", i]];
40 | if(loadingImg) {
41 | [loadingImages addObject:loadingImg];
42 | }
43 | }
44 | refreshHeader.loadingImages = loadingImages;//自定义图片动画
45 | //自定义提示文案
46 | refreshHeader.normalMessage = @"再拉我试试!";
47 | refreshHeader.pullingMessage = @"松开,松开";
48 | refreshHeader.loadingMessage = @"奔跑...";
49 | self.collectionView.sideRefreshHeader = refreshHeader;
50 |
51 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
52 | //执行加载更多
53 | }];
54 | self.collectionView.sideRefreshFooter = refreshFooter;
55 | ```
56 | 
57 | ## 隐藏刷新提示或动画
58 | ```
59 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
60 | //执行刷新
61 | }];
62 | refreshHeader.hideMessage = YES;//隐藏提示
63 | self.collectionView.sideRefreshHeader = refreshHeader;
64 |
65 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
66 | //执行加载更多
67 | }];
68 | refreshFooter.hideIndicator = YES;//隐藏加载动画
69 | self.collectionView.sideRefreshFooter = refreshFooter;
70 | ```
71 | 
72 | ## ‘没有更多数据’ 提示
73 | 显示默认提示
74 | ```
75 | [self.collectionView showEmptyFooter]
76 | ```
77 | 显示自定义提示
78 | ```
79 | [self.collectionView showEmptyFooterWithMessage:@"不要看了,没有了。"];
80 | ```
81 | 
82 |
83 | **PS:隐藏需要手动调用**
84 | ```
85 | [self.collectionView hideEmptyFooter];
86 | ```
87 |
88 | # 实现方式
89 |
90 | - **通过Runtime为UICollectionView添加SideRefreshHeader和SideRefreshFooter属性**
91 | - **观察UICollectionView的contentOffset实现拖动相关逻辑**
92 | - **观察UICollectionView的contentSize来重置刷新视图**
93 | - 更多细节参见源码Demo
94 |
95 | 如果觉得好用欢迎Star [github](https://github.com/dangercheng/CollectionViewSideRefresh), 使用问题请Issue。
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefresh.h:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefresh.h
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //刷新条宽度
12 | static const CGFloat SideRefreshWidth = 50.0;
13 |
14 | /**
15 | 刷新状态
16 |
17 | - SideRefreshStatusNormal: 常规状态
18 | - SideRefreshStatusPulling: 即将刷新
19 | - SideRefreshStatusLoading: 刷新中
20 | */
21 | typedef NS_ENUM(NSUInteger, SideRefreshStatus) {
22 | SideRefreshStatusNormal,
23 | SideRefreshStatusPulling,
24 | SideRefreshStatusLoading,
25 | };
26 |
27 | /**
28 | 刷新回调
29 | */
30 | typedef void(^loadAction)(void);
31 |
32 | @interface SideRefresh : UIView
33 |
34 | /**
35 | 依附的collectionView
36 | */
37 | @property (nonatomic, weak) UICollectionView *collectionView;
38 |
39 | /**
40 | 刷新状态
41 | */
42 | @property (nonatomic, assign) SideRefreshStatus refreshStatus;
43 |
44 | /**
45 | 旧的刷新状态
46 | */
47 | @property (nonatomic, assign) SideRefreshStatus oldRefreshStatus;
48 |
49 | /**
50 | 拉动提示
51 | */
52 | @property (nonatomic, copy) NSString *normalMessage;
53 |
54 | /**
55 | 即将刷新的提示
56 | */
57 | @property (nonatomic, copy) NSString *pullingMessage;
58 |
59 | /**
60 | 加载中提示
61 | */
62 | @property (nonatomic, copy) NSString *loadingMessage;
63 |
64 | /**
65 | 自定义的加载动画图片组
66 | */
67 | @property (nonatomic, strong) NSArray *loadingImages;
68 |
69 | /**
70 | 提示文本
71 | */
72 | @property (nonatomic, strong) UILabel *messageLabel;
73 |
74 | /**
75 | 自定义的加载图片显示视图
76 | */
77 | @property (nonatomic, strong) UIImageView *loadingImageView;
78 |
79 | /**
80 | 默认菊花
81 | */
82 | @property (nonatomic, strong) UIActivityIndicatorView *defaultIndicator;
83 |
84 | /**
85 | 原始的ContentInset
86 | */
87 | @property (nonatomic, assign) UIEdgeInsets originalContentInset;
88 |
89 | /**
90 | 是否支持page
91 | */
92 | @property (nonatomic, assign) BOOL collectionViewPageEnabel;
93 |
94 | /**
95 | 隐藏文本
96 | */
97 | @property (nonatomic, assign) BOOL hideMessage;
98 |
99 | /**
100 | 隐藏加载图片
101 | */
102 | @property (nonatomic, assign) BOOL hideIndicator;
103 |
104 | /**
105 | 刷新执行回调
106 | */
107 | @property (nonatomic, copy) loadAction loadActionBlock;
108 |
109 | /**
110 | 初始化
111 |
112 | @param loadActionBlock 刷新回调
113 | @return 实例
114 | */
115 | + (instancetype)refreshWithLoadAction:(loadAction)loadActionBlock;
116 |
117 | /**
118 | 开始刷新
119 | */
120 | - (void)beginLoading;
121 |
122 | /**
123 | 结束刷新
124 | */
125 | - (void)endLoading;
126 |
127 | /**
128 | 执行刷新回调
129 | */
130 | - (void)startLoadingAction;
131 |
132 | /**
133 | 设置常规状态ContentOffset
134 | */
135 | - (void)updateContentOffsetToNormal;
136 |
137 | /**
138 | 设置刷新状态ContentOffset
139 | */
140 | - (void)updateContentOffsetToLoading;
141 |
142 | /**
143 | 是否到达可刷新状态
144 |
145 | @return YES 是,NO否
146 | */
147 | - (BOOL)pullToReadyRefresh;
148 |
149 | /**
150 | 重置刷新视图位置
151 | */
152 | - (void)resetRefreshFrame;
153 |
154 | /**
155 | 保持刷新状态的offset
156 | */
157 | - (void)keepRefreshingOffset;
158 |
159 | @end
160 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefreshFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefreshFooter.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefreshFooter.h"
10 | #import "UICollectionView+SideRefresh.h"
11 | #import "UICollectionView+SideExtension.h"
12 |
13 | @implementation SideRefreshFooter
14 |
15 | + (instancetype)refreshWithLoadAction:(loadAction)loadActionBlock {
16 | SideRefreshFooter *freshFooter = [[SideRefreshFooter alloc] init];
17 | freshFooter.loadActionBlock = loadActionBlock;
18 | return freshFooter;
19 | }
20 |
21 | - (instancetype)init {
22 | self = [super init];
23 | if(self) {
24 | self.normalMessage = @"左拉加载更多...";
25 | self.pullingMessage = @"松开加载...";
26 | self.loadingMessage = @"加载中...";
27 | self.hidden = YES;
28 | }
29 | return self;
30 | }
31 |
32 | - (void)updateContentOffsetToNormal {
33 | __weak typeof(self) weakSelf = self;
34 | dispatch_async(dispatch_get_main_queue(), ^{
35 | __strong __typeof(self) strongSelf = weakSelf;
36 | if(!strongSelf)return;
37 | [strongSelf.collectionView setSide_insetR:strongSelf.originalContentInset.right];
38 | });
39 | if(self.collectionViewPageEnabel) {
40 | [UIView animateWithDuration:0.3 animations:^{
41 | [self.collectionView setSide_insetR:self.originalContentInset.right];
42 | }];
43 | }
44 | }
45 |
46 | - (void)updateContentOffsetToLoading {
47 | if(self.collectionView.isShowEmptyFooter) {
48 | [self resetRefreshFrame];
49 | return;
50 | }
51 | CGFloat targetRight = self.originalContentInset.right + SideRefreshWidth;
52 | CGFloat showWidth = self.collectionView.frame.size.width - self.originalContentInset.right;
53 | CGFloat spaceWidth = showWidth - self.collectionView.contentSize.width;
54 | [UIView animateWithDuration:0.3 animations:^{
55 | if(spaceWidth > 0) {//应当考虑content显示不完整个CollectionView的情况
56 | [self.collectionView setSide_insetR:(targetRight + spaceWidth)];
57 | } else {
58 | [self.collectionView setSide_insetR:targetRight];
59 | }
60 | }];
61 | }
62 |
63 | - (BOOL)pullToReadyRefresh {
64 | CGFloat offsetX = self.collectionView.contentOffset.x;
65 | CGFloat mostRightOffset = self.collectionView.contentSize.width - self.collectionView.frame.size.width;
66 | if(mostRightOffset < 0) {//考虑Content显示不完整个CollectionView的情况
67 | mostRightOffset = 0;
68 | }
69 | CGFloat targetX = SideRefreshWidth + mostRightOffset;
70 | return offsetX > targetX;
71 | }
72 |
73 | - (void)resetRefreshFrame {
74 | CGFloat frameX = self.collectionView.contentSize.width + self.originalContentInset.right;
75 | self.frame = CGRectMake(frameX, 0, SideRefreshWidth, self.collectionView.frame.size.height);
76 | if(!(self.collectionView.contentSize.width > self.collectionView.frame.size.width)) {
77 | self.frame = CGRectMake(self.collectionView.frame.size.width, 0, SideRefreshWidth, self.collectionView.frame.size.height);
78 | }
79 | self.hidden = NO;
80 | }
81 |
82 | - (void)keepRefreshingOffset {
83 | if(!self.collectionView.pagingEnabled) {
84 | return;
85 | }
86 | CGFloat targetX = SideRefreshWidth + self.originalContentInset.right + self.collectionView.contentSize.width - self.collectionView.frame.size.width;
87 | [self.collectionView setContentOffset:CGPointMake(targetX, self.collectionView.contentOffset.y) animated:NO];
88 | }
89 |
90 | @end
91 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/DefaultViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultViewController.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "DefaultViewController.h"
10 | #import "UICollectionView+SideRefresh.h"
11 | #import "MyCollectionViewCell.h"
12 |
13 | @interface DefaultViewController ()
14 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
15 | @property (nonatomic, assign) CGFloat itemCount;
16 | @end
17 |
18 | @implementation DefaultViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | // Do any additional setup after loading the view, typically from a nib.
23 | self.title = @"默认刷新样式";
24 | self.itemCount = 0;
25 |
26 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
27 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
28 | flowLayout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300.0);
29 | // flowLayout.itemSize = CGSizeMake(50.0, 300.0);
30 | flowLayout.minimumLineSpacing = 0.0;
31 |
32 | self.collectionView.collectionViewLayout = flowLayout;
33 | self.collectionView.dataSource = self;
34 | self.collectionView.pagingEnabled = YES;
35 | [self.collectionView registerNib:[UINib nibWithNibName:@"MyCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"MyCollectionViewCell"];
36 | [self setupRefreshHeaderFoorer];
37 | }
38 |
39 | - (void)viewDidAppear:(BOOL)animated {
40 | [super viewDidAppear:animated];
41 | [self.collectionView.sideRefreshHeader beginLoading];
42 | }
43 |
44 | - (void)setupRefreshHeaderFoorer {
45 | __weak typeof(self) weakSelf = self;
46 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
47 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
48 | __strong __typeof(self) strongSelf = weakSelf;
49 | strongSelf.itemCount = 3;
50 | [strongSelf.collectionView.sideRefreshHeader endLoading];
51 | [strongSelf.collectionView reloadData];
52 | });
53 | }];
54 | self.collectionView.sideRefreshHeader = refreshHeader;
55 |
56 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
57 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
58 | __strong __typeof(self) strongSelf = weakSelf;
59 | strongSelf.itemCount += 3;
60 | [strongSelf.collectionView.sideRefreshFooter endLoading];
61 | [strongSelf.collectionView reloadData];
62 | });
63 | }];
64 | self.collectionView.sideRefreshFooter = refreshFooter;
65 | }
66 |
67 |
68 | - (void)didReceiveMemoryWarning {
69 | [super didReceiveMemoryWarning];
70 | // Dispose of any resources that can be recreated.
71 | }
72 |
73 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
74 | return self.itemCount;
75 | }
76 |
77 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
78 | MyCollectionViewCell *myCollectionCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCollectionViewCell" forIndexPath:indexPath];
79 | myCollectionCell.titleLabel.text = [NSString stringWithFormat:@"NO.%d", (indexPath.row + 1)];
80 | return myCollectionCell;
81 | }
82 |
83 | - (void)dealloc {
84 | NSLog(@"DefaultViewController delloc");
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/HideItemViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // HideItemViewController.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "HideItemViewController.h"
10 | #import "UICollectionView+SideRefresh.h"
11 | #import "MyCollectionViewCell.h"
12 |
13 | @interface HideItemViewController ()
14 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
15 | @property (nonatomic, assign) CGFloat itemCount;
16 |
17 | @end
18 |
19 | @implementation HideItemViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 | // Do any additional setup after loading the view, typically from a nib.
24 | self.title = @"隐藏提示或动画";
25 | self.itemCount = 0;
26 |
27 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
28 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
29 | flowLayout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300.0);
30 | flowLayout.minimumLineSpacing = 0.0;
31 |
32 | self.collectionView.collectionViewLayout = flowLayout;
33 | self.collectionView.dataSource = self;
34 |
35 | [self.collectionView registerNib:[UINib nibWithNibName:@"MyCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"MyCollectionViewCell"];
36 |
37 | [self setupRefreshHeaderFoorer];
38 | }
39 |
40 | - (void)viewDidAppear:(BOOL)animated {
41 | [super viewDidAppear:animated];
42 | [self.collectionView.sideRefreshHeader beginLoading];
43 | }
44 |
45 | - (void)setupRefreshHeaderFoorer {
46 | __weak typeof(self) weakSelf = self;
47 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
48 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
49 | __strong __typeof(self) strongSelf = weakSelf;
50 | strongSelf.itemCount = 3;
51 | [strongSelf.collectionView.sideRefreshHeader endLoading];
52 | [strongSelf.collectionView reloadData];
53 | });
54 | }];
55 | refreshHeader.hideMessage = YES;//隐藏提示
56 | NSMutableArray *loadingImages = [NSMutableArray array];
57 | for(int i = 1; i <= 9; i++) {
58 | UIImage *loadingImg = [UIImage imageNamed:[NSString stringWithFormat:@"loading_0%d", i]];
59 | if(loadingImg) {
60 | [loadingImages addObject:loadingImg];
61 | }
62 | }
63 | refreshHeader.loadingImages = loadingImages;
64 | self.collectionView.sideRefreshHeader = refreshHeader;
65 |
66 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
67 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
68 | __strong __typeof(self) strongSelf = weakSelf;
69 | strongSelf.itemCount += 3;
70 | [strongSelf.collectionView.sideRefreshFooter endLoading];
71 | [strongSelf.collectionView reloadData];
72 | });
73 | }];
74 | refreshFooter.hideIndicator = YES;//隐藏加载动画
75 | refreshFooter.loadingImages = [loadingImages mutableCopy];
76 | self.collectionView.sideRefreshFooter = refreshFooter;
77 | }
78 |
79 |
80 | - (void)didReceiveMemoryWarning {
81 | [super didReceiveMemoryWarning];
82 | // Dispose of any resources that can be recreated.
83 | }
84 |
85 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
86 | return self.itemCount;
87 | }
88 |
89 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
90 | MyCollectionViewCell *myCollectionCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCollectionViewCell" forIndexPath:indexPath];
91 | myCollectionCell.titleLabel.text = [NSString stringWithFormat:@"NO.%ld", (indexPath.row + 1)];
92 | return myCollectionCell;
93 | }
94 |
95 | - (void)dealloc {
96 | NSLog(@"HideItemViewController delloc");
97 | }
98 |
99 | @end
100 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/UICollectionView+SideRefresh.m:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+SideRefresh.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "UICollectionView+SideRefresh.h"
10 | #import
11 |
12 | @interface UICollectionView ()
13 | @property (nonatomic, strong) SideRefreshEmptyFooter *sideRefreshEmptyFooter;
14 | @end
15 |
16 | @implementation UICollectionView (SideRefresh)
17 |
18 | static const NSString *SideRefreshHeaderKey = @"SideRefreshHeaderKey";
19 | static const NSString *SideRefreshFooterKey = @"SideRefreshFooterKey";
20 | static const NSString *SideRefreshEmptyFooterKey = @"SideRefreshEmptyFooterKey";
21 | static const NSString *SideRefreshIsShowEmptyFooterKey = @"SideRefreshIsShowEmptyFooterKey";
22 |
23 | - (void)setSideRefreshFooter:(SideRefreshFooter *)sideRefreshFooter {
24 | if(self.sideRefreshFooter != sideRefreshFooter) {
25 | [self.sideRefreshFooter removeFromSuperview];
26 | [self willChangeValueForKey:@"sideRefreshFooter"];
27 | objc_setAssociatedObject(self, &SideRefreshFooterKey, sideRefreshFooter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
28 | [self didChangeValueForKey:@"sideRefreshFooter"];
29 | [self addSubview:sideRefreshFooter];
30 | }
31 | if(self.sideRefreshEmptyFooter) {
32 | [self.sideRefreshEmptyFooter removeFromSuperview];
33 | }
34 | self.isShowEmptyFooter = NO;
35 | }
36 |
37 | - (SideRefreshFooter *)sideRefreshFooter {
38 | return objc_getAssociatedObject(self, &SideRefreshFooterKey);
39 | }
40 |
41 | - (void)setSideRefreshHeader:(SideRefreshHeader *)sideRefreshHeader {
42 | if(self.sideRefreshHeader != sideRefreshHeader) {
43 | [self.sideRefreshHeader removeFromSuperview];
44 | [self willChangeValueForKey:@"sideRefreshHeader"];
45 | objc_setAssociatedObject(self, &SideRefreshHeaderKey, sideRefreshHeader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
46 | [self didChangeValueForKey:@"sideRefreshHeader"];
47 | [self insertSubview:sideRefreshHeader atIndex:0];
48 | }
49 | }
50 |
51 | - (SideRefreshHeader *)sideRefreshHeader {
52 | return objc_getAssociatedObject(self, &SideRefreshHeaderKey);
53 | }
54 |
55 | - (void)setSideRefreshEmptyFooter:(SideRefreshEmptyFooter *)sideRefreshEmptyFooter {
56 | if(self.sideRefreshEmptyFooter != sideRefreshEmptyFooter) {
57 | [self.sideRefreshEmptyFooter removeFromSuperview];
58 | [self willChangeValueForKey:@"sideRefreshEmptyFooter"];
59 | objc_setAssociatedObject(self, &SideRefreshEmptyFooterKey, sideRefreshEmptyFooter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
60 | [self didChangeValueForKey:@"sideRefreshEmptyFooter"];
61 | [self addSubview:sideRefreshEmptyFooter];
62 | }
63 | }
64 |
65 | - (SideRefreshEmptyFooter *)sideRefreshEmptyFooter {
66 | return objc_getAssociatedObject(self, &SideRefreshEmptyFooterKey);
67 | }
68 |
69 | - (void)setIsShowEmptyFooter:(BOOL)isShowEmptyFooter {
70 | if(self.isShowEmptyFooter != isShowEmptyFooter) {
71 | objc_setAssociatedObject(self, &SideRefreshIsShowEmptyFooterKey, [NSNumber numberWithBool:isShowEmptyFooter], OBJC_ASSOCIATION_ASSIGN);
72 | }
73 | }
74 |
75 | - (BOOL)isShowEmptyFooter {
76 | return [objc_getAssociatedObject(self, &SideRefreshIsShowEmptyFooterKey) boolValue];
77 | }
78 |
79 | - (void)showEmptyFooter {
80 | [self showEmptyFooterWithMessage:nil];
81 | }
82 |
83 | - (void)showEmptyFooterWithMessage:(NSString *)message {
84 | if(!message) {
85 | message = @"没有更多数据了";
86 | }
87 | [self.sideRefreshFooter removeFromSuperview];
88 | CGFloat frameX = self.contentSize.width + self.contentInset.right;
89 | self.sideRefreshEmptyFooter = [[SideRefreshEmptyFooter alloc] initWithFrame:CGRectMake(frameX, 0, SideRefreshEmptyFooterWidth, self.frame.size.height)];
90 | self.sideRefreshEmptyFooter.message = message;
91 | self.isShowEmptyFooter = YES;
92 | }
93 |
94 | - (void)hideEmptyFooter {
95 | [self.sideRefreshEmptyFooter removeFromSuperview];
96 | self.isShowEmptyFooter = NO;
97 | if(self.sideRefreshFooter) {
98 | [self addSubview:self.sideRefreshFooter];
99 | }
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/MyCollectionViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/EmptyFooterViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyFooterViewController.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/21.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "EmptyFooterViewController.h"
10 | #import "UICollectionView+SideRefresh.h"
11 | #import "MyCollectionViewCell.h"
12 |
13 | @interface EmptyFooterViewController ()
14 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
15 |
16 | @property (nonatomic, assign) CGFloat itemCount;
17 | @end
18 |
19 | @implementation EmptyFooterViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 | // Do any additional setup after loading the view, typically from a nib.
24 | self.title = @"没有更多数据-提示";
25 | self.itemCount = 0;
26 |
27 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
28 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
29 | flowLayout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300.0);
30 | flowLayout.minimumLineSpacing = 0.0;
31 |
32 | self.collectionView.collectionViewLayout = flowLayout;
33 | self.collectionView.dataSource = self;
34 | self.collectionView.pagingEnabled = YES;
35 | [self.collectionView registerNib:[UINib nibWithNibName:@"MyCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"MyCollectionViewCell"];
36 |
37 | [self setupRefreshHeaderFoorer];
38 | }
39 |
40 | - (void)viewDidAppear:(BOOL)animated {
41 | [super viewDidAppear:animated];
42 | [self.collectionView.sideRefreshHeader beginLoading];
43 | }
44 |
45 | - (void)setupRefreshHeaderFoorer {
46 | __weak typeof(self) weakSelf = self;
47 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
48 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
49 | __strong __typeof(self) strongSelf = weakSelf;
50 | strongSelf.itemCount = 3;
51 | [strongSelf.collectionView.sideRefreshHeader endLoading];
52 | [strongSelf.collectionView reloadData];
53 | });
54 | }];
55 | refreshHeader.hideMessage = YES;//隐藏提示
56 | NSMutableArray *loadingImages = [NSMutableArray array];
57 | for(int i = 1; i <= 9; i++) {
58 | UIImage *loadingImg = [UIImage imageNamed:[NSString stringWithFormat:@"loading_0%d", i]];
59 | if(loadingImg) {
60 | [loadingImages addObject:loadingImg];
61 | }
62 | }
63 | refreshHeader.loadingImages = loadingImages;
64 | self.collectionView.sideRefreshHeader = refreshHeader;
65 |
66 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
67 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
68 | __strong __typeof(self) strongSelf = weakSelf;
69 | strongSelf.itemCount += 3;
70 | [strongSelf.collectionView.sideRefreshFooter endLoading];
71 | [strongSelf.collectionView reloadData];
72 | if(strongSelf.itemCount == 6) {
73 | [strongSelf.collectionView showEmptyFooter];
74 | // [strongSelf.collectionView showEmptyFooterWithMessage:@"不要看了,没有了。"];
75 | }
76 | });
77 | }];
78 | refreshFooter.hideIndicator = YES;//隐藏加载动画
79 | refreshFooter.loadingImages = [loadingImages mutableCopy];
80 | self.collectionView.sideRefreshFooter = refreshFooter;
81 | }
82 |
83 |
84 | - (void)didReceiveMemoryWarning {
85 | [super didReceiveMemoryWarning];
86 | // Dispose of any resources that can be recreated.
87 | }
88 |
89 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
90 | return self.itemCount;
91 | }
92 |
93 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
94 | MyCollectionViewCell *myCollectionCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCollectionViewCell" forIndexPath:indexPath];
95 | myCollectionCell.titleLabel.text = [NSString stringWithFormat:@"NO.%ld", (indexPath.row + 1)];
96 | return myCollectionCell;
97 | }
98 |
99 | - (void)dealloc {
100 | NSLog(@"EmptyFooterViewController delloc");
101 | }
102 |
103 |
104 | @end
105 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefreshDemo/CustomViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // CustomViewController.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/13.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "CustomViewController.h"
10 | #import "UICollectionView+SideRefresh.h"
11 | #import "MyCollectionViewCell.h"
12 |
13 | @interface CustomViewController ()
14 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
15 | @property (nonatomic, assign) CGFloat itemCount;
16 | @end
17 |
18 | @implementation CustomViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | // Do any additional setup after loading the view, typically from a nib.
23 | self.title = @"自定义";
24 | self.itemCount = 0;
25 |
26 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
27 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
28 | flowLayout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300.0);
29 | flowLayout.minimumLineSpacing = 0.0;
30 |
31 | self.collectionView.collectionViewLayout = flowLayout;
32 | self.collectionView.dataSource = self;
33 | [self.collectionView registerNib:[UINib nibWithNibName:@"MyCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"MyCollectionViewCell"];
34 |
35 | [self setupRefreshHeaderFoorer];
36 | }
37 | - (void)viewDidAppear:(BOOL)animated {
38 | [super viewDidAppear:animated];
39 | [self.collectionView.sideRefreshHeader beginLoading];
40 | }
41 |
42 | - (void)setupRefreshHeaderFoorer {
43 | __weak typeof(self) weakSelf = self;
44 | SideRefreshHeader *refreshHeader = [SideRefreshHeader refreshWithLoadAction:^{
45 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
46 | __strong __typeof(self) strongSelf = weakSelf;
47 | strongSelf.itemCount = 3;
48 | [strongSelf.collectionView.sideRefreshHeader endLoading];
49 | [strongSelf.collectionView reloadData];
50 | });
51 | }];
52 | NSMutableArray *loadingImages = [NSMutableArray array];
53 | for(int i = 1; i <= 9; i++) {
54 | UIImage *loadingImg = [UIImage imageNamed:[NSString stringWithFormat:@"loading_0%d", i]];
55 | if(loadingImg) {
56 | [loadingImages addObject:loadingImg];
57 | }
58 | }
59 | refreshHeader.loadingImages = loadingImages;//自定义图片动画
60 | //自定义提示文案
61 | refreshHeader.normalMessage = @"再拉我试试!";
62 | refreshHeader.pullingMessage = @"松开,松开";
63 | refreshHeader.loadingMessage = @"奔跑...";
64 | self.collectionView.sideRefreshHeader = refreshHeader;
65 |
66 | SideRefreshFooter *refreshFooter = [SideRefreshFooter refreshWithLoadAction:^{
67 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
68 | __strong __typeof(self) strongSelf = weakSelf;
69 | if(!(strongSelf.itemCount > 20)) {
70 | strongSelf.itemCount += 3;
71 | [strongSelf.collectionView.sideRefreshFooter endLoading];
72 | } else {
73 | [strongSelf.collectionView.sideRefreshFooter endLoading];
74 | }
75 | [strongSelf.collectionView reloadData];
76 | });
77 | }];
78 | refreshFooter.loadingImages = [loadingImages mutableCopy];//自定义图片动画
79 | //自定义提示文案
80 | refreshFooter.normalMessage = @"再拉我试试!";
81 | refreshFooter.pullingMessage = @"松开,松开";
82 | refreshFooter.loadingMessage = @"奔跑...";
83 | self.collectionView.sideRefreshFooter = refreshFooter;
84 | }
85 |
86 |
87 | - (void)didReceiveMemoryWarning {
88 | [super didReceiveMemoryWarning];
89 | // Dispose of any resources that can be recreated.
90 | }
91 |
92 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
93 | return self.itemCount;
94 | }
95 |
96 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
97 | MyCollectionViewCell *myCollectionCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCollectionViewCell" forIndexPath:indexPath];
98 | myCollectionCell.titleLabel.text = [NSString stringWithFormat:@"NO.%ld", (indexPath.row + 1)];
99 | return myCollectionCell;
100 | }
101 |
102 | - (void)dealloc {
103 | NSLog(@"CustomViewController delloc");
104 | }
105 |
106 | @end
107 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/SideRefresh/SideRefresh.m:
--------------------------------------------------------------------------------
1 | //
2 | // SideRefresh.m
3 | // CollectionViewSideRefresh
4 | //
5 | // Created by DandJ on 2018/7/11.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "SideRefresh.h"
10 | #import "UICollectionView+SideExtension.h"
11 | #import "UICollectionView+SideRefresh.h"
12 |
13 | @implementation SideRefresh
14 |
15 | - (void)willMoveToSuperview:(UIView *)newSuperview {
16 | [super willMoveToSuperview:newSuperview];
17 | [self removeObserver];
18 | if ([newSuperview isKindOfClass:[UICollectionView class]]) {
19 | self.collectionView = (UICollectionView *)newSuperview;
20 | self.collectionView.alwaysBounceHorizontal = YES;
21 | self.originalContentInset = self.collectionView.side_inset;
22 | self.collectionViewPageEnabel = self.collectionView.pagingEnabled;
23 | [self addObserver];
24 | }
25 | }
26 |
27 | - (void)startLoadingAction {
28 | if(self.loadActionBlock && self.superview) {
29 | self.loadActionBlock();
30 | }
31 | }
32 |
33 | - (void)beginLoading {
34 | self.refreshStatus = SideRefreshStatusLoading;
35 | }
36 |
37 | - (void)endLoading {
38 | self.refreshStatus = SideRefreshStatusNormal;
39 | [self.collectionView hideEmptyFooter];
40 | }
41 |
42 | - (void)removeObserver {
43 | [self.superview removeObserver:self forKeyPath:@"contentOffset"];
44 | [self.superview removeObserver:self forKeyPath:@"contentSize"];
45 | }
46 |
47 | - (void)addObserver {
48 | NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
49 | [self.collectionView addObserver:self forKeyPath:@"contentOffset" options:options context:nil];
50 | [self.collectionView addObserver:self forKeyPath:@"contentSize" options:options context:nil];
51 | }
52 |
53 | #pragma mark - ContentOffset Observer
54 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
55 | if(![self.superview isEqual:self.collectionView]) {
56 | return;
57 | }
58 | if([keyPath isEqualToString:@"contentOffset"]) {
59 | [self handleContentOffsetChanged];
60 | }
61 | else if([keyPath isEqualToString:@"contentSize"]) {
62 | [self handleContentSizeChanged];
63 | }
64 | }
65 |
66 | - (void)handleContentOffsetChanged {
67 | if(self.collectionView.dragging) {
68 | if([self pullToReadyRefresh]) {
69 | if(self.refreshStatus == SideRefreshStatusNormal) {
70 | self.refreshStatus = SideRefreshStatusPulling;
71 | }
72 | } else {
73 | if(self.refreshStatus == SideRefreshStatusPulling) {
74 | self.refreshStatus = SideRefreshStatusNormal;
75 | } else if(self.refreshStatus == SideRefreshStatusLoading) {
76 | // self.collectionView.pagingEnabled = NO;
77 | self.refreshStatus = SideRefreshStatusNormal;
78 | }
79 | }
80 | } else {
81 | if(self.refreshStatus == SideRefreshStatusPulling) {
82 | self.refreshStatus = SideRefreshStatusLoading;
83 | }
84 | else if(self.refreshStatus == SideRefreshStatusLoading) {
85 | [self keepRefreshingOffset];
86 | }
87 | }
88 | [self updateRefreshFrame];
89 | }
90 |
91 | - (void)handleContentSizeChanged {
92 | [self resetRefreshFrame];
93 | }
94 |
95 | - (void)updateRefreshFrame {
96 | if(!self.hidden) {
97 | return;
98 | }
99 | [self resetRefreshFrame];
100 | [self updateRefreshContentFrame];
101 | }
102 |
103 | - (void)updateRefreshContentFrame {
104 | CGFloat indicatorWidth = 20.0;
105 | self.backgroundColor = self.collectionView.backgroundColor;
106 |
107 | CGFloat imgFitY = (self.frame.size.height / 2) - (indicatorWidth / 2);
108 | if(self.hideMessage) {
109 | self.messageLabel.hidden = YES;
110 | } else {
111 | self.messageLabel.frame = CGRectMake(15, 0, 15, self.collectionView.frame.size.height);
112 | NSString *maxLengthMessage = self.normalMessage;
113 | if(self.pullingMessage.length > maxLengthMessage.length) {
114 | maxLengthMessage = self.pullingMessage;
115 | }
116 | if(self.loadingMessage.length > maxLengthMessage.length) {
117 | maxLengthMessage = self.loadingMessage;
118 | }
119 | CGSize messageSize = [maxLengthMessage boundingRectWithSize:CGSizeMake(15, CGFLOAT_MAX)
120 | options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin
121 | attributes:@{NSFontAttributeName:self.messageLabel.font}
122 | context:nil].size;
123 | imgFitY = (self.collectionView.frame.size.height - messageSize.height)/2 - indicatorWidth - 20;
124 | }
125 |
126 | if(self.hideIndicator) {
127 | self.loadingImageView.hidden = YES;
128 | [self.defaultIndicator removeFromSuperview];
129 | } else {
130 | self.loadingImageView.hidden = NO;
131 | if([self.loadingImages count] > 0) {
132 | [self.defaultIndicator removeFromSuperview];
133 | self.loadingImageView.frame = CGRectMake(12, imgFitY, indicatorWidth, indicatorWidth);
134 | self.loadingImageView.animationImages = self.loadingImages;
135 | self.loadingImageView.animationDuration = 0.5;
136 | self.loadingImageView.animationRepeatCount = NSIntegerMax;
137 | self.loadingImageView.image = self.loadingImages.firstObject;
138 | } else {
139 | self.loadingImageView.hidden = YES;
140 | self.defaultIndicator.frame = CGRectMake(12, imgFitY, indicatorWidth, indicatorWidth);
141 | self.defaultIndicator.hidesWhenStopped = NO;
142 | }
143 | }
144 | }
145 |
146 |
147 | #pragma mark - Child Recognize
148 | + (instancetype)refreshWithLoadAction:(loadAction)loadActionBlock {
149 | [self doesNotRecognizeSelector:_cmd];
150 | return nil;
151 | }
152 |
153 | - (void)updateContentOffsetToNormal {
154 | [self doesNotRecognizeSelector:_cmd];
155 | }
156 |
157 |
158 | - (void)updateContentOffsetToLoading {
159 | [self doesNotRecognizeSelector:_cmd];
160 | }
161 |
162 | - (BOOL)pullToReadyRefresh {
163 | [self doesNotRecognizeSelector:_cmd];
164 | return NO;
165 | }
166 |
167 | - (void)resetRefreshFrame {
168 | [self doesNotRecognizeSelector:_cmd];
169 | }
170 |
171 | - (void)keepRefreshingOffset {
172 | [self doesNotRecognizeSelector:_cmd];
173 | }
174 |
175 | #pragma mark - setter & getter
176 | - (void)setRefreshStatus:(SideRefreshStatus)refreshStatus {
177 | _refreshStatus = refreshStatus;
178 | switch (refreshStatus) {
179 | case SideRefreshStatusNormal:
180 | self.messageLabel.text = self.normalMessage;
181 | if(self.oldRefreshStatus == SideRefreshStatusLoading) {
182 | [self updateContentOffsetToNormal];
183 | [self.loadingImageView stopAnimating];
184 | [self.defaultIndicator stopAnimating];
185 | }
186 | break;
187 | case SideRefreshStatusPulling:
188 | self.messageLabel.text = self.pullingMessage;
189 | break;
190 | case SideRefreshStatusLoading:
191 | self.messageLabel.text = self.loadingMessage;
192 | [self updateContentOffsetToLoading];
193 | [self startLoadingAction];
194 | [self.loadingImageView startAnimating];
195 | [self.defaultIndicator startAnimating];
196 | break;
197 | }
198 | self.oldRefreshStatus = refreshStatus;
199 | }
200 |
201 | - (UIImageView *)loadingImageView {
202 | if(!_loadingImageView) {
203 | _loadingImageView = [[UIImageView alloc] init];
204 | [self addSubview:_loadingImageView];
205 | }
206 | return _loadingImageView;
207 | }
208 |
209 | - (UILabel *)messageLabel {
210 | if(!_messageLabel) {
211 | _messageLabel = [[UILabel alloc] init];
212 | _messageLabel.numberOfLines = 0;
213 | _messageLabel.font = [UIFont systemFontOfSize:12];
214 | _messageLabel.textColor = [UIColor colorWithRed:102/255.0 green:102/255.0 blue:102/255.0 alpha:1/1.0];
215 | _messageLabel.textAlignment = NSTextAlignmentCenter;
216 | _messageLabel.text = self.normalMessage;
217 | [self addSubview:_messageLabel];
218 | }
219 | return _messageLabel;
220 | }
221 |
222 | - (UIActivityIndicatorView *)defaultIndicator {
223 | if(!_defaultIndicator) {
224 | _defaultIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
225 | [self addSubview:_defaultIndicator];
226 | }
227 | return _defaultIndicator;
228 | }
229 |
230 | @end
231 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
200 |
207 |
214 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
--------------------------------------------------------------------------------
/CollectionViewSideRefresh.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 532FF95F2101A78800CC3A80 /* SideRefreshEmptyFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 532FF95E2101A78800CC3A80 /* SideRefreshEmptyFooter.m */; };
11 | 532FF9622103297E00CC3A80 /* EmptyFooterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 532FF9612103297E00CC3A80 /* EmptyFooterViewController.m */; };
12 | 534195602148F7D1005EF096 /* UICollectionView+SideExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 5341955F2148F7D1005EF096 /* UICollectionView+SideExtension.m */; };
13 | 53C040F520F60342005FAB25 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C040F420F60342005FAB25 /* AppDelegate.m */; };
14 | 53C040F820F60342005FAB25 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C040F720F60342005FAB25 /* ViewController.m */; };
15 | 53C040FB20F60342005FAB25 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53C040F920F60342005FAB25 /* Main.storyboard */; };
16 | 53C040FD20F60343005FAB25 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53C040FC20F60343005FAB25 /* Assets.xcassets */; };
17 | 53C0410020F60343005FAB25 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53C040FE20F60343005FAB25 /* LaunchScreen.storyboard */; };
18 | 53C0410320F60343005FAB25 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0410220F60343005FAB25 /* main.m */; };
19 | 53C0410C20F60410005FAB25 /* SideRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0410B20F60410005FAB25 /* SideRefreshHeader.m */; };
20 | 53C0410F20F6041F005FAB25 /* SideRefreshFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0410E20F6041F005FAB25 /* SideRefreshFooter.m */; };
21 | 53C0411220F60435005FAB25 /* UICollectionView+SideRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0411120F60435005FAB25 /* UICollectionView+SideRefresh.m */; };
22 | 53C0411520F6065B005FAB25 /* SideRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0411420F6065B005FAB25 /* SideRefresh.m */; };
23 | 53C0412220F88B52005FAB25 /* DefaultViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0412120F88B52005FAB25 /* DefaultViewController.m */; };
24 | 53C0412520F88D13005FAB25 /* CustomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0412420F88D13005FAB25 /* CustomViewController.m */; };
25 | 53C0412920F88D3E005FAB25 /* MyCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0412720F88D3E005FAB25 /* MyCollectionViewCell.m */; };
26 | 53C0412A20F88D3E005FAB25 /* MyCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 53C0412820F88D3E005FAB25 /* MyCollectionViewCell.xib */; };
27 | 53C0412D20F88E05005FAB25 /* HideItemViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C0412C20F88E05005FAB25 /* HideItemViewController.m */; };
28 | /* End PBXBuildFile section */
29 |
30 | /* Begin PBXFileReference section */
31 | 532FF95D2101A78800CC3A80 /* SideRefreshEmptyFooter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SideRefreshEmptyFooter.h; sourceTree = ""; };
32 | 532FF95E2101A78800CC3A80 /* SideRefreshEmptyFooter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SideRefreshEmptyFooter.m; sourceTree = ""; };
33 | 532FF9602103297E00CC3A80 /* EmptyFooterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmptyFooterViewController.h; sourceTree = ""; };
34 | 532FF9612103297E00CC3A80 /* EmptyFooterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EmptyFooterViewController.m; sourceTree = ""; };
35 | 5341955E2148F7D1005EF096 /* UICollectionView+SideExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+SideExtension.h"; sourceTree = ""; };
36 | 5341955F2148F7D1005EF096 /* UICollectionView+SideExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+SideExtension.m"; sourceTree = ""; };
37 | 53C040F020F60342005FAB25 /* CollectionViewSideRefresh.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CollectionViewSideRefresh.app; sourceTree = BUILT_PRODUCTS_DIR; };
38 | 53C040F320F60342005FAB25 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
39 | 53C040F420F60342005FAB25 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
40 | 53C040F620F60342005FAB25 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
41 | 53C040F720F60342005FAB25 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
42 | 53C040FA20F60342005FAB25 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
43 | 53C040FC20F60343005FAB25 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
44 | 53C040FF20F60343005FAB25 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
45 | 53C0410120F60343005FAB25 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
46 | 53C0410220F60343005FAB25 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
47 | 53C0410A20F60410005FAB25 /* SideRefreshHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SideRefreshHeader.h; sourceTree = ""; };
48 | 53C0410B20F60410005FAB25 /* SideRefreshHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SideRefreshHeader.m; sourceTree = ""; };
49 | 53C0410D20F6041F005FAB25 /* SideRefreshFooter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SideRefreshFooter.h; sourceTree = ""; };
50 | 53C0410E20F6041F005FAB25 /* SideRefreshFooter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SideRefreshFooter.m; sourceTree = ""; };
51 | 53C0411020F60435005FAB25 /* UICollectionView+SideRefresh.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+SideRefresh.h"; sourceTree = ""; };
52 | 53C0411120F60435005FAB25 /* UICollectionView+SideRefresh.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+SideRefresh.m"; sourceTree = ""; };
53 | 53C0411320F6065B005FAB25 /* SideRefresh.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SideRefresh.h; sourceTree = ""; };
54 | 53C0411420F6065B005FAB25 /* SideRefresh.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SideRefresh.m; sourceTree = ""; };
55 | 53C0412020F88B52005FAB25 /* DefaultViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DefaultViewController.h; sourceTree = ""; };
56 | 53C0412120F88B52005FAB25 /* DefaultViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DefaultViewController.m; sourceTree = ""; };
57 | 53C0412320F88D13005FAB25 /* CustomViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomViewController.h; sourceTree = ""; };
58 | 53C0412420F88D13005FAB25 /* CustomViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomViewController.m; sourceTree = ""; };
59 | 53C0412620F88D3E005FAB25 /* MyCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyCollectionViewCell.h; sourceTree = ""; };
60 | 53C0412720F88D3E005FAB25 /* MyCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyCollectionViewCell.m; sourceTree = ""; };
61 | 53C0412820F88D3E005FAB25 /* MyCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyCollectionViewCell.xib; sourceTree = ""; };
62 | 53C0412B20F88E05005FAB25 /* HideItemViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HideItemViewController.h; sourceTree = ""; };
63 | 53C0412C20F88E05005FAB25 /* HideItemViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HideItemViewController.m; sourceTree = ""; };
64 | /* End PBXFileReference section */
65 |
66 | /* Begin PBXFrameworksBuildPhase section */
67 | 53C040ED20F60342005FAB25 /* Frameworks */ = {
68 | isa = PBXFrameworksBuildPhase;
69 | buildActionMask = 2147483647;
70 | files = (
71 | );
72 | runOnlyForDeploymentPostprocessing = 0;
73 | };
74 | /* End PBXFrameworksBuildPhase section */
75 |
76 | /* Begin PBXGroup section */
77 | 53C040E720F60342005FAB25 = {
78 | isa = PBXGroup;
79 | children = (
80 | 53C040F220F60342005FAB25 /* CollectionViewSideRefresh */,
81 | 53C040F120F60342005FAB25 /* Products */,
82 | );
83 | sourceTree = "";
84 | };
85 | 53C040F120F60342005FAB25 /* Products */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 53C040F020F60342005FAB25 /* CollectionViewSideRefresh.app */,
89 | );
90 | name = Products;
91 | sourceTree = "";
92 | };
93 | 53C040F220F60342005FAB25 /* CollectionViewSideRefresh */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 53C0410920F60359005FAB25 /* SideRefresh */,
97 | 53C0411C20F88B32005FAB25 /* SideRefreshDemo */,
98 | 53C040F320F60342005FAB25 /* AppDelegate.h */,
99 | 53C040F420F60342005FAB25 /* AppDelegate.m */,
100 | 53C040F620F60342005FAB25 /* ViewController.h */,
101 | 53C040F720F60342005FAB25 /* ViewController.m */,
102 | 53C040F920F60342005FAB25 /* Main.storyboard */,
103 | 53C040FC20F60343005FAB25 /* Assets.xcassets */,
104 | 53C040FE20F60343005FAB25 /* LaunchScreen.storyboard */,
105 | 53C0410120F60343005FAB25 /* Info.plist */,
106 | 53C0410220F60343005FAB25 /* main.m */,
107 | );
108 | path = CollectionViewSideRefresh;
109 | sourceTree = "";
110 | };
111 | 53C0410920F60359005FAB25 /* SideRefresh */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 53C0410A20F60410005FAB25 /* SideRefreshHeader.h */,
115 | 53C0410B20F60410005FAB25 /* SideRefreshHeader.m */,
116 | 53C0410D20F6041F005FAB25 /* SideRefreshFooter.h */,
117 | 53C0410E20F6041F005FAB25 /* SideRefreshFooter.m */,
118 | 532FF95D2101A78800CC3A80 /* SideRefreshEmptyFooter.h */,
119 | 532FF95E2101A78800CC3A80 /* SideRefreshEmptyFooter.m */,
120 | 53C0411020F60435005FAB25 /* UICollectionView+SideRefresh.h */,
121 | 53C0411120F60435005FAB25 /* UICollectionView+SideRefresh.m */,
122 | 5341955E2148F7D1005EF096 /* UICollectionView+SideExtension.h */,
123 | 5341955F2148F7D1005EF096 /* UICollectionView+SideExtension.m */,
124 | 53C0411320F6065B005FAB25 /* SideRefresh.h */,
125 | 53C0411420F6065B005FAB25 /* SideRefresh.m */,
126 | );
127 | path = SideRefresh;
128 | sourceTree = "";
129 | };
130 | 53C0411C20F88B32005FAB25 /* SideRefreshDemo */ = {
131 | isa = PBXGroup;
132 | children = (
133 | 53C0412020F88B52005FAB25 /* DefaultViewController.h */,
134 | 53C0412120F88B52005FAB25 /* DefaultViewController.m */,
135 | 53C0412320F88D13005FAB25 /* CustomViewController.h */,
136 | 53C0412420F88D13005FAB25 /* CustomViewController.m */,
137 | 53C0412B20F88E05005FAB25 /* HideItemViewController.h */,
138 | 53C0412C20F88E05005FAB25 /* HideItemViewController.m */,
139 | 532FF9602103297E00CC3A80 /* EmptyFooterViewController.h */,
140 | 532FF9612103297E00CC3A80 /* EmptyFooterViewController.m */,
141 | 53C0412620F88D3E005FAB25 /* MyCollectionViewCell.h */,
142 | 53C0412720F88D3E005FAB25 /* MyCollectionViewCell.m */,
143 | 53C0412820F88D3E005FAB25 /* MyCollectionViewCell.xib */,
144 | );
145 | path = SideRefreshDemo;
146 | sourceTree = "";
147 | };
148 | /* End PBXGroup section */
149 |
150 | /* Begin PBXNativeTarget section */
151 | 53C040EF20F60342005FAB25 /* CollectionViewSideRefresh */ = {
152 | isa = PBXNativeTarget;
153 | buildConfigurationList = 53C0410620F60343005FAB25 /* Build configuration list for PBXNativeTarget "CollectionViewSideRefresh" */;
154 | buildPhases = (
155 | 53C040EC20F60342005FAB25 /* Sources */,
156 | 53C040ED20F60342005FAB25 /* Frameworks */,
157 | 53C040EE20F60342005FAB25 /* Resources */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = CollectionViewSideRefresh;
164 | productName = CollectionViewSideRefresh;
165 | productReference = 53C040F020F60342005FAB25 /* CollectionViewSideRefresh.app */;
166 | productType = "com.apple.product-type.application";
167 | };
168 | /* End PBXNativeTarget section */
169 |
170 | /* Begin PBXProject section */
171 | 53C040E820F60342005FAB25 /* Project object */ = {
172 | isa = PBXProject;
173 | attributes = {
174 | LastUpgradeCheck = 0930;
175 | ORGANIZATIONNAME = DandJ;
176 | TargetAttributes = {
177 | 53C040EF20F60342005FAB25 = {
178 | CreatedOnToolsVersion = 9.3;
179 | };
180 | };
181 | };
182 | buildConfigurationList = 53C040EB20F60342005FAB25 /* Build configuration list for PBXProject "CollectionViewSideRefresh" */;
183 | compatibilityVersion = "Xcode 9.3";
184 | developmentRegion = en;
185 | hasScannedForEncodings = 0;
186 | knownRegions = (
187 | en,
188 | Base,
189 | );
190 | mainGroup = 53C040E720F60342005FAB25;
191 | productRefGroup = 53C040F120F60342005FAB25 /* Products */;
192 | projectDirPath = "";
193 | projectRoot = "";
194 | targets = (
195 | 53C040EF20F60342005FAB25 /* CollectionViewSideRefresh */,
196 | );
197 | };
198 | /* End PBXProject section */
199 |
200 | /* Begin PBXResourcesBuildPhase section */
201 | 53C040EE20F60342005FAB25 /* Resources */ = {
202 | isa = PBXResourcesBuildPhase;
203 | buildActionMask = 2147483647;
204 | files = (
205 | 53C0410020F60343005FAB25 /* LaunchScreen.storyboard in Resources */,
206 | 53C040FD20F60343005FAB25 /* Assets.xcassets in Resources */,
207 | 53C0412A20F88D3E005FAB25 /* MyCollectionViewCell.xib in Resources */,
208 | 53C040FB20F60342005FAB25 /* Main.storyboard in Resources */,
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | };
212 | /* End PBXResourcesBuildPhase section */
213 |
214 | /* Begin PBXSourcesBuildPhase section */
215 | 53C040EC20F60342005FAB25 /* Sources */ = {
216 | isa = PBXSourcesBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | 53C040F820F60342005FAB25 /* ViewController.m in Sources */,
220 | 532FF9622103297E00CC3A80 /* EmptyFooterViewController.m in Sources */,
221 | 532FF95F2101A78800CC3A80 /* SideRefreshEmptyFooter.m in Sources */,
222 | 53C0412520F88D13005FAB25 /* CustomViewController.m in Sources */,
223 | 53C0410F20F6041F005FAB25 /* SideRefreshFooter.m in Sources */,
224 | 53C0410320F60343005FAB25 /* main.m in Sources */,
225 | 53C0412920F88D3E005FAB25 /* MyCollectionViewCell.m in Sources */,
226 | 53C0411520F6065B005FAB25 /* SideRefresh.m in Sources */,
227 | 53C0411220F60435005FAB25 /* UICollectionView+SideRefresh.m in Sources */,
228 | 53C040F520F60342005FAB25 /* AppDelegate.m in Sources */,
229 | 53C0412220F88B52005FAB25 /* DefaultViewController.m in Sources */,
230 | 53C0410C20F60410005FAB25 /* SideRefreshHeader.m in Sources */,
231 | 534195602148F7D1005EF096 /* UICollectionView+SideExtension.m in Sources */,
232 | 53C0412D20F88E05005FAB25 /* HideItemViewController.m in Sources */,
233 | );
234 | runOnlyForDeploymentPostprocessing = 0;
235 | };
236 | /* End PBXSourcesBuildPhase section */
237 |
238 | /* Begin PBXVariantGroup section */
239 | 53C040F920F60342005FAB25 /* Main.storyboard */ = {
240 | isa = PBXVariantGroup;
241 | children = (
242 | 53C040FA20F60342005FAB25 /* Base */,
243 | );
244 | name = Main.storyboard;
245 | sourceTree = "";
246 | };
247 | 53C040FE20F60343005FAB25 /* LaunchScreen.storyboard */ = {
248 | isa = PBXVariantGroup;
249 | children = (
250 | 53C040FF20F60343005FAB25 /* Base */,
251 | );
252 | name = LaunchScreen.storyboard;
253 | sourceTree = "";
254 | };
255 | /* End PBXVariantGroup section */
256 |
257 | /* Begin XCBuildConfiguration section */
258 | 53C0410420F60343005FAB25 /* Debug */ = {
259 | isa = XCBuildConfiguration;
260 | buildSettings = {
261 | ALWAYS_SEARCH_USER_PATHS = NO;
262 | CLANG_ANALYZER_NONNULL = YES;
263 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
265 | CLANG_CXX_LIBRARY = "libc++";
266 | CLANG_ENABLE_MODULES = YES;
267 | CLANG_ENABLE_OBJC_ARC = YES;
268 | CLANG_ENABLE_OBJC_WEAK = YES;
269 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
270 | CLANG_WARN_BOOL_CONVERSION = YES;
271 | CLANG_WARN_COMMA = YES;
272 | CLANG_WARN_CONSTANT_CONVERSION = YES;
273 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
275 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
276 | CLANG_WARN_EMPTY_BODY = YES;
277 | CLANG_WARN_ENUM_CONVERSION = YES;
278 | CLANG_WARN_INFINITE_RECURSION = YES;
279 | CLANG_WARN_INT_CONVERSION = YES;
280 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
281 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
282 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
284 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
285 | CLANG_WARN_STRICT_PROTOTYPES = YES;
286 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
287 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
288 | CLANG_WARN_UNREACHABLE_CODE = YES;
289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
290 | CODE_SIGN_IDENTITY = "iPhone Developer";
291 | COPY_PHASE_STRIP = NO;
292 | DEBUG_INFORMATION_FORMAT = dwarf;
293 | ENABLE_STRICT_OBJC_MSGSEND = YES;
294 | ENABLE_TESTABILITY = YES;
295 | GCC_C_LANGUAGE_STANDARD = gnu11;
296 | GCC_DYNAMIC_NO_PIC = NO;
297 | GCC_NO_COMMON_BLOCKS = YES;
298 | GCC_OPTIMIZATION_LEVEL = 0;
299 | GCC_PREPROCESSOR_DEFINITIONS = (
300 | "DEBUG=1",
301 | "$(inherited)",
302 | );
303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
305 | GCC_WARN_UNDECLARED_SELECTOR = YES;
306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
307 | GCC_WARN_UNUSED_FUNCTION = YES;
308 | GCC_WARN_UNUSED_VARIABLE = YES;
309 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
310 | MTL_ENABLE_DEBUG_INFO = YES;
311 | ONLY_ACTIVE_ARCH = YES;
312 | SDKROOT = iphoneos;
313 | };
314 | name = Debug;
315 | };
316 | 53C0410520F60343005FAB25 /* Release */ = {
317 | isa = XCBuildConfiguration;
318 | buildSettings = {
319 | ALWAYS_SEARCH_USER_PATHS = NO;
320 | CLANG_ANALYZER_NONNULL = YES;
321 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_ENABLE_OBJC_WEAK = YES;
327 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
328 | CLANG_WARN_BOOL_CONVERSION = YES;
329 | CLANG_WARN_COMMA = YES;
330 | CLANG_WARN_CONSTANT_CONVERSION = YES;
331 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
333 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
334 | CLANG_WARN_EMPTY_BODY = YES;
335 | CLANG_WARN_ENUM_CONVERSION = YES;
336 | CLANG_WARN_INFINITE_RECURSION = YES;
337 | CLANG_WARN_INT_CONVERSION = YES;
338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
339 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
340 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
341 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
343 | CLANG_WARN_STRICT_PROTOTYPES = YES;
344 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
345 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
346 | CLANG_WARN_UNREACHABLE_CODE = YES;
347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
348 | CODE_SIGN_IDENTITY = "iPhone Developer";
349 | COPY_PHASE_STRIP = NO;
350 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
351 | ENABLE_NS_ASSERTIONS = NO;
352 | ENABLE_STRICT_OBJC_MSGSEND = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu11;
354 | GCC_NO_COMMON_BLOCKS = YES;
355 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
356 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
357 | GCC_WARN_UNDECLARED_SELECTOR = YES;
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
359 | GCC_WARN_UNUSED_FUNCTION = YES;
360 | GCC_WARN_UNUSED_VARIABLE = YES;
361 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
362 | MTL_ENABLE_DEBUG_INFO = NO;
363 | SDKROOT = iphoneos;
364 | VALIDATE_PRODUCT = YES;
365 | };
366 | name = Release;
367 | };
368 | 53C0410720F60343005FAB25 /* Debug */ = {
369 | isa = XCBuildConfiguration;
370 | buildSettings = {
371 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
372 | CODE_SIGN_STYLE = Automatic;
373 | DEVELOPMENT_TEAM = 4QA4S2484R;
374 | INFOPLIST_FILE = CollectionViewSideRefresh/Info.plist;
375 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
376 | LD_RUNPATH_SEARCH_PATHS = (
377 | "$(inherited)",
378 | "@executable_path/Frameworks",
379 | );
380 | PRODUCT_BUNDLE_IDENTIFIER = com.DandJ.CollectionViewSideRefresh;
381 | PRODUCT_NAME = "$(TARGET_NAME)";
382 | TARGETED_DEVICE_FAMILY = "1,2";
383 | };
384 | name = Debug;
385 | };
386 | 53C0410820F60343005FAB25 /* Release */ = {
387 | isa = XCBuildConfiguration;
388 | buildSettings = {
389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
390 | CODE_SIGN_STYLE = Automatic;
391 | DEVELOPMENT_TEAM = 4QA4S2484R;
392 | INFOPLIST_FILE = CollectionViewSideRefresh/Info.plist;
393 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
394 | LD_RUNPATH_SEARCH_PATHS = (
395 | "$(inherited)",
396 | "@executable_path/Frameworks",
397 | );
398 | PRODUCT_BUNDLE_IDENTIFIER = com.DandJ.CollectionViewSideRefresh;
399 | PRODUCT_NAME = "$(TARGET_NAME)";
400 | TARGETED_DEVICE_FAMILY = "1,2";
401 | };
402 | name = Release;
403 | };
404 | /* End XCBuildConfiguration section */
405 |
406 | /* Begin XCConfigurationList section */
407 | 53C040EB20F60342005FAB25 /* Build configuration list for PBXProject "CollectionViewSideRefresh" */ = {
408 | isa = XCConfigurationList;
409 | buildConfigurations = (
410 | 53C0410420F60343005FAB25 /* Debug */,
411 | 53C0410520F60343005FAB25 /* Release */,
412 | );
413 | defaultConfigurationIsVisible = 0;
414 | defaultConfigurationName = Release;
415 | };
416 | 53C0410620F60343005FAB25 /* Build configuration list for PBXNativeTarget "CollectionViewSideRefresh" */ = {
417 | isa = XCConfigurationList;
418 | buildConfigurations = (
419 | 53C0410720F60343005FAB25 /* Debug */,
420 | 53C0410820F60343005FAB25 /* Release */,
421 | );
422 | defaultConfigurationIsVisible = 0;
423 | defaultConfigurationName = Release;
424 | };
425 | /* End XCConfigurationList section */
426 | };
427 | rootObject = 53C040E820F60342005FAB25 /* Project object */;
428 | }
429 |
--------------------------------------------------------------------------------