├── .gitignore
├── LICENCE
├── README.md
├── SECollectionViewFlowLayout.podspec
├── SECollectionViewFlowLayout.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── ChrisW.xcuserdatad
│ └── xcschemes
│ ├── SECollectionViewFlowLayout.xcscheme
│ └── xcschememanagement.plist
├── SECollectionViewFlowLayout
├── Base.lproj
│ └── Main.storyboard
├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── PodFiles
│ ├── QBAssetsCollectionCheckmarkView.h
│ ├── QBAssetsCollectionCheckmarkView.m
│ ├── QBAssetsCollectionFooterView.h
│ ├── QBAssetsCollectionFooterView.m
│ ├── QBAssetsCollectionOverlayView.h
│ ├── QBAssetsCollectionOverlayView.m
│ ├── QBAssetsCollectionViewCell.h
│ ├── QBAssetsCollectionViewCell.m
│ ├── QBAssetsCollectionViewController.h
│ ├── QBAssetsCollectionViewController.m
│ ├── QBImagePickerController.h
│ ├── QBImagePickerController.m
│ ├── QBImagePickerGroupCell.h
│ ├── QBImagePickerGroupCell.m
│ ├── QBImagePickerThumbnailView.h
│ ├── QBImagePickerThumbnailView.m
│ ├── SECollectionViewFlowLayout.h
│ └── SECollectionViewFlowLayout.m
├── SEAppDelegate.h
├── SEAppDelegate.m
├── SECollectionViewFlowLayout-Info.plist
├── SECollectionViewFlowLayout-Prefix.pch
├── SEViewController.h
├── SEViewController.m
├── en.lproj
│ └── InfoPlist.strings
└── main.m
└── SEQBImagePickerController.podspec
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | build/*
3 | *.pbxuser
4 | *.xcworkspace
5 | xcuserdata
6 | profile
7 | *.moved-aside
8 | xcuserdata/
9 | project.xcworkspace/
10 | xcshareddata/
11 | *.swp
12 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Christopher Wendel
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SECollectionViewFlowLayout
2 | ==========================
3 |
4 | A flow layout for UICollectionView that implements swiping-to-select gestures.
5 |
6 | ##Functionality
7 | ###Panning to select
8 | Just touch down and pan and select items in your UICollectionView, much easier than tapping each item
9 |
10 |
11 | ###Auto-select rows
12 | If you choose you can enable auto-selecting rows, where if you pan to select an entire row you can continue panning down to select whole rows at a time.
13 |
14 |
15 |
16 | ###Pan to deselect
17 | Along with panning to select collection view cells, you can choose to enable panning to deselect, where if you start panning from a selected cell, the panning will deselect cells.
18 |
19 |
20 |
21 | ###Auto select cells between touches
22 | If you choose you can enable auto selection of cells between a first and second touch. Where all cells between the two touches will be selected.
23 |
24 |
25 |
26 |
27 | ##SEQBImagePickerController
28 | The example use case (as seen in the gifs) is using SECollectionViewFlowLayout in combination with [QBImagePickerController](https://github.com/questbeat/QBImagePickerController) to select multiple photos from a UIImagePickerController clone. You can use this image picker in your project by adding to your podfile:
29 | pod 'SEQBImagePickerController'
30 |
31 | ##SECollectionViewFlowLayout
32 | You can also use SECollectionViewFlowLayout in your project and use it with your own UICollectionView.
33 | pod 'SECollectionViewFlowLayout'
34 |
35 | ##Usage
36 |
37 | When initializing your `UICollectionViewController` using `initWithCollectionViewLayout:`, allocate a new `SECollectionViewFlowLayout`
38 |
39 | ```objc
40 | UICollectionViewController *collectionViewController =
41 | [[UICollectionViewController alloc] initWithCollectionViewLayout:
42 | [SECollectionViewFlowLayout layoutWithAutoSelectRows:YES panToDeselect:YES autoSelectCellsBetweenTouches:YES]];
43 | ```
44 |
45 | ##Contributing
46 | Use [Github issues](https://github.com/cewendel/SECollectionViewFlowLayout/issues) to track bugs and feature requests
47 |
48 | ##Contact
49 |
50 | Chris Wendel
51 | - http://twitter.com/CEWendel
52 | - [chriwend@umich.edu](mailto:chriwend@umich.edu)
53 |
54 | ##Licence
55 |
56 | MIT
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SECollectionViewFlowLayout'
3 | s.version = '0.0.1'
4 | s.author = { 'Chris Wendel' => 'chriwend@umich.edu' }
5 | s.homepage = 'https://github.com/CEWendel/SECollectionViewFlowLayout'
6 | s.summary = 'A flow layout for UICollectionView that implements swiping-to-select gestures'
7 | s.license = 'MIT'
8 | s.source = { :git => 'https://github.com/CEWendel/SECollectionViewFlowLayout.git', :tag => '0.0.1' }
9 | s.source_files = 'SECollectionViewFlowLayout/PodFiles/SECollectionViewFlowLayout.{h,m}'
10 | s.platform = :ios
11 | s.ios.deployment_target = '6.0'
12 | s.requires_arc = true
13 | end
14 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | AF2471FB18B30BAD00614655 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2471FA18B30BAD00614655 /* Foundation.framework */; };
11 | AF2471FD18B30BAD00614655 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2471FC18B30BAD00614655 /* CoreGraphics.framework */; };
12 | AF2471FF18B30BAD00614655 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2471FE18B30BAD00614655 /* UIKit.framework */; };
13 | AF24720518B30BAD00614655 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF24720318B30BAD00614655 /* InfoPlist.strings */; };
14 | AF24720718B30BAD00614655 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24720618B30BAD00614655 /* main.m */; };
15 | AF24720B18B30BAD00614655 /* SEAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24720A18B30BAD00614655 /* SEAppDelegate.m */; };
16 | AF24720E18B30BAD00614655 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF24720C18B30BAD00614655 /* Main.storyboard */; };
17 | AF24721118B30BAD00614655 /* SEViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24721018B30BAD00614655 /* SEViewController.m */; };
18 | AF24721318B30BAD00614655 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF24721218B30BAD00614655 /* Images.xcassets */; };
19 | AF24721A18B30BAD00614655 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF24721918B30BAD00614655 /* XCTest.framework */; };
20 | AF24721B18B30BAD00614655 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2471FA18B30BAD00614655 /* Foundation.framework */; };
21 | AF24721C18B30BAD00614655 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2471FE18B30BAD00614655 /* UIKit.framework */; };
22 | AF24722418B30BAD00614655 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF24722218B30BAD00614655 /* InfoPlist.strings */; };
23 | AF24722618B30BAD00614655 /* SECollectionViewFlowLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24722518B30BAD00614655 /* SECollectionViewFlowLayoutTests.m */; };
24 | AF24724B18B30D1A00614655 /* SECollectionViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24724A18B30D1A00614655 /* SECollectionViewFlowLayout.m */; };
25 | AF24725D18B32D8D00614655 /* QBImagePickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24724D18B32D8D00614655 /* QBImagePickerController.m */; };
26 | AF24725E18B32D8D00614655 /* QBAssetsCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24724E18B32D8D00614655 /* QBAssetsCollectionViewController.m */; };
27 | AF24725F18B32D8D00614655 /* QBAssetsCollectionOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725018B32D8D00614655 /* QBAssetsCollectionOverlayView.m */; };
28 | AF24726018B32D8D00614655 /* QBAssetsCollectionCheckmarkView.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725118B32D8D00614655 /* QBAssetsCollectionCheckmarkView.m */; };
29 | AF24726118B32D8D00614655 /* QBAssetsCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725218B32D8D00614655 /* QBAssetsCollectionViewCell.m */; };
30 | AF24726218B32D8D00614655 /* QBAssetsCollectionFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725518B32D8D00614655 /* QBAssetsCollectionFooterView.m */; };
31 | AF24726318B32D8D00614655 /* QBImagePickerGroupCell.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725A18B32D8D00614655 /* QBImagePickerGroupCell.m */; };
32 | AF24726418B32D8D00614655 /* QBImagePickerThumbnailView.m in Sources */ = {isa = PBXBuildFile; fileRef = AF24725C18B32D8D00614655 /* QBImagePickerThumbnailView.m */; };
33 | /* End PBXBuildFile section */
34 |
35 | /* Begin PBXContainerItemProxy section */
36 | AF24721D18B30BAD00614655 /* PBXContainerItemProxy */ = {
37 | isa = PBXContainerItemProxy;
38 | containerPortal = AF2471EF18B30BAD00614655 /* Project object */;
39 | proxyType = 1;
40 | remoteGlobalIDString = AF2471F618B30BAD00614655;
41 | remoteInfo = SECollectionViewFlowLayout;
42 | };
43 | /* End PBXContainerItemProxy section */
44 |
45 | /* Begin PBXFileReference section */
46 | AF2471F718B30BAD00614655 /* SECollectionViewFlowLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SECollectionViewFlowLayout.app; sourceTree = BUILT_PRODUCTS_DIR; };
47 | AF2471FA18B30BAD00614655 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
48 | AF2471FC18B30BAD00614655 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
49 | AF2471FE18B30BAD00614655 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
50 | AF24720218B30BAD00614655 /* SECollectionViewFlowLayout-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SECollectionViewFlowLayout-Info.plist"; sourceTree = ""; };
51 | AF24720418B30BAD00614655 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
52 | AF24720618B30BAD00614655 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
53 | AF24720818B30BAD00614655 /* SECollectionViewFlowLayout-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SECollectionViewFlowLayout-Prefix.pch"; sourceTree = ""; };
54 | AF24720918B30BAD00614655 /* SEAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SEAppDelegate.h; sourceTree = ""; };
55 | AF24720A18B30BAD00614655 /* SEAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SEAppDelegate.m; sourceTree = ""; };
56 | AF24720D18B30BAD00614655 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
57 | AF24720F18B30BAD00614655 /* SEViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SEViewController.h; sourceTree = ""; };
58 | AF24721018B30BAD00614655 /* SEViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SEViewController.m; sourceTree = ""; };
59 | AF24721218B30BAD00614655 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
60 | AF24721818B30BAD00614655 /* SECollectionViewFlowLayoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SECollectionViewFlowLayoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
61 | AF24721918B30BAD00614655 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
62 | AF24722118B30BAD00614655 /* SECollectionViewFlowLayoutTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SECollectionViewFlowLayoutTests-Info.plist"; sourceTree = ""; };
63 | AF24722318B30BAD00614655 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
64 | AF24722518B30BAD00614655 /* SECollectionViewFlowLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SECollectionViewFlowLayoutTests.m; sourceTree = ""; };
65 | AF24724918B30D1A00614655 /* SECollectionViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SECollectionViewFlowLayout.h; path = PodFiles/SECollectionViewFlowLayout.h; sourceTree = ""; };
66 | AF24724A18B30D1A00614655 /* SECollectionViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SECollectionViewFlowLayout.m; path = PodFiles/SECollectionViewFlowLayout.m; sourceTree = ""; };
67 | AF24724D18B32D8D00614655 /* QBImagePickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBImagePickerController.m; path = PodFiles/QBImagePickerController.m; sourceTree = ""; };
68 | AF24724E18B32D8D00614655 /* QBAssetsCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBAssetsCollectionViewController.m; path = PodFiles/QBAssetsCollectionViewController.m; sourceTree = ""; };
69 | AF24724F18B32D8D00614655 /* QBAssetsCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBAssetsCollectionViewController.h; path = PodFiles/QBAssetsCollectionViewController.h; sourceTree = ""; };
70 | AF24725018B32D8D00614655 /* QBAssetsCollectionOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBAssetsCollectionOverlayView.m; path = PodFiles/QBAssetsCollectionOverlayView.m; sourceTree = ""; };
71 | AF24725118B32D8D00614655 /* QBAssetsCollectionCheckmarkView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBAssetsCollectionCheckmarkView.m; path = PodFiles/QBAssetsCollectionCheckmarkView.m; sourceTree = ""; };
72 | AF24725218B32D8D00614655 /* QBAssetsCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBAssetsCollectionViewCell.m; path = PodFiles/QBAssetsCollectionViewCell.m; sourceTree = ""; };
73 | AF24725318B32D8D00614655 /* QBAssetsCollectionCheckmarkView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBAssetsCollectionCheckmarkView.h; path = PodFiles/QBAssetsCollectionCheckmarkView.h; sourceTree = ""; };
74 | AF24725418B32D8D00614655 /* QBAssetsCollectionFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBAssetsCollectionFooterView.h; path = PodFiles/QBAssetsCollectionFooterView.h; sourceTree = ""; };
75 | AF24725518B32D8D00614655 /* QBAssetsCollectionFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBAssetsCollectionFooterView.m; path = PodFiles/QBAssetsCollectionFooterView.m; sourceTree = ""; };
76 | AF24725618B32D8D00614655 /* QBAssetsCollectionOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBAssetsCollectionOverlayView.h; path = PodFiles/QBAssetsCollectionOverlayView.h; sourceTree = ""; };
77 | AF24725718B32D8D00614655 /* QBAssetsCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBAssetsCollectionViewCell.h; path = PodFiles/QBAssetsCollectionViewCell.h; sourceTree = ""; };
78 | AF24725818B32D8D00614655 /* QBImagePickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBImagePickerController.h; path = PodFiles/QBImagePickerController.h; sourceTree = ""; };
79 | AF24725918B32D8D00614655 /* QBImagePickerGroupCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBImagePickerGroupCell.h; path = PodFiles/QBImagePickerGroupCell.h; sourceTree = ""; };
80 | AF24725A18B32D8D00614655 /* QBImagePickerGroupCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBImagePickerGroupCell.m; path = PodFiles/QBImagePickerGroupCell.m; sourceTree = ""; };
81 | AF24725B18B32D8D00614655 /* QBImagePickerThumbnailView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QBImagePickerThumbnailView.h; path = PodFiles/QBImagePickerThumbnailView.h; sourceTree = ""; };
82 | AF24725C18B32D8D00614655 /* QBImagePickerThumbnailView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QBImagePickerThumbnailView.m; path = PodFiles/QBImagePickerThumbnailView.m; sourceTree = ""; };
83 | /* End PBXFileReference section */
84 |
85 | /* Begin PBXFrameworksBuildPhase section */
86 | AF2471F418B30BAD00614655 /* Frameworks */ = {
87 | isa = PBXFrameworksBuildPhase;
88 | buildActionMask = 2147483647;
89 | files = (
90 | AF2471FD18B30BAD00614655 /* CoreGraphics.framework in Frameworks */,
91 | AF2471FF18B30BAD00614655 /* UIKit.framework in Frameworks */,
92 | AF2471FB18B30BAD00614655 /* Foundation.framework in Frameworks */,
93 | );
94 | runOnlyForDeploymentPostprocessing = 0;
95 | };
96 | AF24721518B30BAD00614655 /* Frameworks */ = {
97 | isa = PBXFrameworksBuildPhase;
98 | buildActionMask = 2147483647;
99 | files = (
100 | AF24721A18B30BAD00614655 /* XCTest.framework in Frameworks */,
101 | AF24721C18B30BAD00614655 /* UIKit.framework in Frameworks */,
102 | AF24721B18B30BAD00614655 /* Foundation.framework in Frameworks */,
103 | );
104 | runOnlyForDeploymentPostprocessing = 0;
105 | };
106 | /* End PBXFrameworksBuildPhase section */
107 |
108 | /* Begin PBXGroup section */
109 | AF2471EE18B30BAD00614655 = {
110 | isa = PBXGroup;
111 | children = (
112 | AF24720018B30BAD00614655 /* SECollectionViewFlowLayout */,
113 | AF24721F18B30BAD00614655 /* SECollectionViewFlowLayoutTests */,
114 | AF2471F918B30BAD00614655 /* Frameworks */,
115 | AF2471F818B30BAD00614655 /* Products */,
116 | );
117 | sourceTree = "";
118 | };
119 | AF2471F818B30BAD00614655 /* Products */ = {
120 | isa = PBXGroup;
121 | children = (
122 | AF2471F718B30BAD00614655 /* SECollectionViewFlowLayout.app */,
123 | AF24721818B30BAD00614655 /* SECollectionViewFlowLayoutTests.xctest */,
124 | );
125 | name = Products;
126 | sourceTree = "";
127 | };
128 | AF2471F918B30BAD00614655 /* Frameworks */ = {
129 | isa = PBXGroup;
130 | children = (
131 | AF2471FA18B30BAD00614655 /* Foundation.framework */,
132 | AF2471FC18B30BAD00614655 /* CoreGraphics.framework */,
133 | AF2471FE18B30BAD00614655 /* UIKit.framework */,
134 | AF24721918B30BAD00614655 /* XCTest.framework */,
135 | );
136 | name = Frameworks;
137 | sourceTree = "";
138 | };
139 | AF24720018B30BAD00614655 /* SECollectionViewFlowLayout */ = {
140 | isa = PBXGroup;
141 | children = (
142 | AF24724C18B32C8400614655 /* PodFiles */,
143 | AF24720918B30BAD00614655 /* SEAppDelegate.h */,
144 | AF24720A18B30BAD00614655 /* SEAppDelegate.m */,
145 | AF24720C18B30BAD00614655 /* Main.storyboard */,
146 | AF24720F18B30BAD00614655 /* SEViewController.h */,
147 | AF24721018B30BAD00614655 /* SEViewController.m */,
148 | AF24721218B30BAD00614655 /* Images.xcassets */,
149 | AF24720118B30BAD00614655 /* Supporting Files */,
150 | );
151 | path = SECollectionViewFlowLayout;
152 | sourceTree = "";
153 | };
154 | AF24720118B30BAD00614655 /* Supporting Files */ = {
155 | isa = PBXGroup;
156 | children = (
157 | AF24720218B30BAD00614655 /* SECollectionViewFlowLayout-Info.plist */,
158 | AF24720318B30BAD00614655 /* InfoPlist.strings */,
159 | AF24720618B30BAD00614655 /* main.m */,
160 | AF24720818B30BAD00614655 /* SECollectionViewFlowLayout-Prefix.pch */,
161 | );
162 | name = "Supporting Files";
163 | sourceTree = "";
164 | };
165 | AF24721F18B30BAD00614655 /* SECollectionViewFlowLayoutTests */ = {
166 | isa = PBXGroup;
167 | children = (
168 | AF24722518B30BAD00614655 /* SECollectionViewFlowLayoutTests.m */,
169 | AF24722018B30BAD00614655 /* Supporting Files */,
170 | );
171 | path = SECollectionViewFlowLayoutTests;
172 | sourceTree = "";
173 | };
174 | AF24722018B30BAD00614655 /* Supporting Files */ = {
175 | isa = PBXGroup;
176 | children = (
177 | AF24722118B30BAD00614655 /* SECollectionViewFlowLayoutTests-Info.plist */,
178 | AF24722218B30BAD00614655 /* InfoPlist.strings */,
179 | );
180 | name = "Supporting Files";
181 | sourceTree = "";
182 | };
183 | AF24722F18B30C2300614655 /* QBImagePickerController */ = {
184 | isa = PBXGroup;
185 | children = (
186 | AF24725818B32D8D00614655 /* QBImagePickerController.h */,
187 | AF24724D18B32D8D00614655 /* QBImagePickerController.m */,
188 | AF24724F18B32D8D00614655 /* QBAssetsCollectionViewController.h */,
189 | AF24724E18B32D8D00614655 /* QBAssetsCollectionViewController.m */,
190 | AF24725618B32D8D00614655 /* QBAssetsCollectionOverlayView.h */,
191 | AF24725018B32D8D00614655 /* QBAssetsCollectionOverlayView.m */,
192 | AF24725318B32D8D00614655 /* QBAssetsCollectionCheckmarkView.h */,
193 | AF24725118B32D8D00614655 /* QBAssetsCollectionCheckmarkView.m */,
194 | AF24725718B32D8D00614655 /* QBAssetsCollectionViewCell.h */,
195 | AF24725218B32D8D00614655 /* QBAssetsCollectionViewCell.m */,
196 | AF24725418B32D8D00614655 /* QBAssetsCollectionFooterView.h */,
197 | AF24725518B32D8D00614655 /* QBAssetsCollectionFooterView.m */,
198 | AF24725918B32D8D00614655 /* QBImagePickerGroupCell.h */,
199 | AF24725A18B32D8D00614655 /* QBImagePickerGroupCell.m */,
200 | AF24725B18B32D8D00614655 /* QBImagePickerThumbnailView.h */,
201 | AF24725C18B32D8D00614655 /* QBImagePickerThumbnailView.m */,
202 | );
203 | name = QBImagePickerController;
204 | sourceTree = "";
205 | };
206 | AF24724C18B32C8400614655 /* PodFiles */ = {
207 | isa = PBXGroup;
208 | children = (
209 | AF24724918B30D1A00614655 /* SECollectionViewFlowLayout.h */,
210 | AF24724A18B30D1A00614655 /* SECollectionViewFlowLayout.m */,
211 | AF24722F18B30C2300614655 /* QBImagePickerController */,
212 | );
213 | name = PodFiles;
214 | sourceTree = "";
215 | };
216 | /* End PBXGroup section */
217 |
218 | /* Begin PBXNativeTarget section */
219 | AF2471F618B30BAD00614655 /* SECollectionViewFlowLayout */ = {
220 | isa = PBXNativeTarget;
221 | buildConfigurationList = AF24722918B30BAD00614655 /* Build configuration list for PBXNativeTarget "SECollectionViewFlowLayout" */;
222 | buildPhases = (
223 | AF2471F318B30BAD00614655 /* Sources */,
224 | AF2471F418B30BAD00614655 /* Frameworks */,
225 | AF2471F518B30BAD00614655 /* Resources */,
226 | );
227 | buildRules = (
228 | );
229 | dependencies = (
230 | );
231 | name = SECollectionViewFlowLayout;
232 | productName = SECollectionViewFlowLayout;
233 | productReference = AF2471F718B30BAD00614655 /* SECollectionViewFlowLayout.app */;
234 | productType = "com.apple.product-type.application";
235 | };
236 | AF24721718B30BAD00614655 /* SECollectionViewFlowLayoutTests */ = {
237 | isa = PBXNativeTarget;
238 | buildConfigurationList = AF24722C18B30BAD00614655 /* Build configuration list for PBXNativeTarget "SECollectionViewFlowLayoutTests" */;
239 | buildPhases = (
240 | AF24721418B30BAD00614655 /* Sources */,
241 | AF24721518B30BAD00614655 /* Frameworks */,
242 | AF24721618B30BAD00614655 /* Resources */,
243 | );
244 | buildRules = (
245 | );
246 | dependencies = (
247 | AF24721E18B30BAD00614655 /* PBXTargetDependency */,
248 | );
249 | name = SECollectionViewFlowLayoutTests;
250 | productName = SECollectionViewFlowLayoutTests;
251 | productReference = AF24721818B30BAD00614655 /* SECollectionViewFlowLayoutTests.xctest */;
252 | productType = "com.apple.product-type.bundle.unit-test";
253 | };
254 | /* End PBXNativeTarget section */
255 |
256 | /* Begin PBXProject section */
257 | AF2471EF18B30BAD00614655 /* Project object */ = {
258 | isa = PBXProject;
259 | attributes = {
260 | CLASSPREFIX = SE;
261 | LastUpgradeCheck = 0500;
262 | ORGANIZATIONNAME = CEWendel;
263 | TargetAttributes = {
264 | AF24721718B30BAD00614655 = {
265 | TestTargetID = AF2471F618B30BAD00614655;
266 | };
267 | };
268 | };
269 | buildConfigurationList = AF2471F218B30BAD00614655 /* Build configuration list for PBXProject "SECollectionViewFlowLayout" */;
270 | compatibilityVersion = "Xcode 3.2";
271 | developmentRegion = English;
272 | hasScannedForEncodings = 0;
273 | knownRegions = (
274 | en,
275 | Base,
276 | );
277 | mainGroup = AF2471EE18B30BAD00614655;
278 | productRefGroup = AF2471F818B30BAD00614655 /* Products */;
279 | projectDirPath = "";
280 | projectRoot = "";
281 | targets = (
282 | AF2471F618B30BAD00614655 /* SECollectionViewFlowLayout */,
283 | AF24721718B30BAD00614655 /* SECollectionViewFlowLayoutTests */,
284 | );
285 | };
286 | /* End PBXProject section */
287 |
288 | /* Begin PBXResourcesBuildPhase section */
289 | AF2471F518B30BAD00614655 /* Resources */ = {
290 | isa = PBXResourcesBuildPhase;
291 | buildActionMask = 2147483647;
292 | files = (
293 | AF24721318B30BAD00614655 /* Images.xcassets in Resources */,
294 | AF24720518B30BAD00614655 /* InfoPlist.strings in Resources */,
295 | AF24720E18B30BAD00614655 /* Main.storyboard in Resources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | AF24721618B30BAD00614655 /* Resources */ = {
300 | isa = PBXResourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | AF24722418B30BAD00614655 /* InfoPlist.strings in Resources */,
304 | );
305 | runOnlyForDeploymentPostprocessing = 0;
306 | };
307 | /* End PBXResourcesBuildPhase section */
308 |
309 | /* Begin PBXSourcesBuildPhase section */
310 | AF2471F318B30BAD00614655 /* Sources */ = {
311 | isa = PBXSourcesBuildPhase;
312 | buildActionMask = 2147483647;
313 | files = (
314 | AF24720B18B30BAD00614655 /* SEAppDelegate.m in Sources */,
315 | AF24726118B32D8D00614655 /* QBAssetsCollectionViewCell.m in Sources */,
316 | AF24721118B30BAD00614655 /* SEViewController.m in Sources */,
317 | AF24726218B32D8D00614655 /* QBAssetsCollectionFooterView.m in Sources */,
318 | AF24726018B32D8D00614655 /* QBAssetsCollectionCheckmarkView.m in Sources */,
319 | AF24725E18B32D8D00614655 /* QBAssetsCollectionViewController.m in Sources */,
320 | AF24725D18B32D8D00614655 /* QBImagePickerController.m in Sources */,
321 | AF24720718B30BAD00614655 /* main.m in Sources */,
322 | AF24724B18B30D1A00614655 /* SECollectionViewFlowLayout.m in Sources */,
323 | AF24725F18B32D8D00614655 /* QBAssetsCollectionOverlayView.m in Sources */,
324 | AF24726418B32D8D00614655 /* QBImagePickerThumbnailView.m in Sources */,
325 | AF24726318B32D8D00614655 /* QBImagePickerGroupCell.m in Sources */,
326 | );
327 | runOnlyForDeploymentPostprocessing = 0;
328 | };
329 | AF24721418B30BAD00614655 /* Sources */ = {
330 | isa = PBXSourcesBuildPhase;
331 | buildActionMask = 2147483647;
332 | files = (
333 | AF24722618B30BAD00614655 /* SECollectionViewFlowLayoutTests.m in Sources */,
334 | );
335 | runOnlyForDeploymentPostprocessing = 0;
336 | };
337 | /* End PBXSourcesBuildPhase section */
338 |
339 | /* Begin PBXTargetDependency section */
340 | AF24721E18B30BAD00614655 /* PBXTargetDependency */ = {
341 | isa = PBXTargetDependency;
342 | target = AF2471F618B30BAD00614655 /* SECollectionViewFlowLayout */;
343 | targetProxy = AF24721D18B30BAD00614655 /* PBXContainerItemProxy */;
344 | };
345 | /* End PBXTargetDependency section */
346 |
347 | /* Begin PBXVariantGroup section */
348 | AF24720318B30BAD00614655 /* InfoPlist.strings */ = {
349 | isa = PBXVariantGroup;
350 | children = (
351 | AF24720418B30BAD00614655 /* en */,
352 | );
353 | name = InfoPlist.strings;
354 | sourceTree = "";
355 | };
356 | AF24720C18B30BAD00614655 /* Main.storyboard */ = {
357 | isa = PBXVariantGroup;
358 | children = (
359 | AF24720D18B30BAD00614655 /* Base */,
360 | );
361 | name = Main.storyboard;
362 | sourceTree = "";
363 | };
364 | AF24722218B30BAD00614655 /* InfoPlist.strings */ = {
365 | isa = PBXVariantGroup;
366 | children = (
367 | AF24722318B30BAD00614655 /* en */,
368 | );
369 | name = InfoPlist.strings;
370 | sourceTree = "";
371 | };
372 | /* End PBXVariantGroup section */
373 |
374 | /* Begin XCBuildConfiguration section */
375 | AF24722718B30BAD00614655 /* Debug */ = {
376 | isa = XCBuildConfiguration;
377 | buildSettings = {
378 | ALWAYS_SEARCH_USER_PATHS = NO;
379 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
381 | CLANG_CXX_LIBRARY = "libc++";
382 | CLANG_ENABLE_MODULES = YES;
383 | CLANG_ENABLE_OBJC_ARC = YES;
384 | CLANG_WARN_BOOL_CONVERSION = YES;
385 | CLANG_WARN_CONSTANT_CONVERSION = YES;
386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
387 | CLANG_WARN_EMPTY_BODY = YES;
388 | CLANG_WARN_ENUM_CONVERSION = YES;
389 | CLANG_WARN_INT_CONVERSION = YES;
390 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
393 | COPY_PHASE_STRIP = NO;
394 | GCC_C_LANGUAGE_STANDARD = gnu99;
395 | GCC_DYNAMIC_NO_PIC = NO;
396 | GCC_OPTIMIZATION_LEVEL = 0;
397 | GCC_PREPROCESSOR_DEFINITIONS = (
398 | "DEBUG=1",
399 | "$(inherited)",
400 | );
401 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
402 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
403 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
404 | GCC_WARN_UNDECLARED_SELECTOR = YES;
405 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
406 | GCC_WARN_UNUSED_FUNCTION = YES;
407 | GCC_WARN_UNUSED_VARIABLE = YES;
408 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
409 | ONLY_ACTIVE_ARCH = YES;
410 | SDKROOT = iphoneos;
411 | };
412 | name = Debug;
413 | };
414 | AF24722818B30BAD00614655 /* Release */ = {
415 | isa = XCBuildConfiguration;
416 | buildSettings = {
417 | ALWAYS_SEARCH_USER_PATHS = NO;
418 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
419 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
420 | CLANG_CXX_LIBRARY = "libc++";
421 | CLANG_ENABLE_MODULES = YES;
422 | CLANG_ENABLE_OBJC_ARC = YES;
423 | CLANG_WARN_BOOL_CONVERSION = YES;
424 | CLANG_WARN_CONSTANT_CONVERSION = YES;
425 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
426 | CLANG_WARN_EMPTY_BODY = YES;
427 | CLANG_WARN_ENUM_CONVERSION = YES;
428 | CLANG_WARN_INT_CONVERSION = YES;
429 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
430 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
431 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
432 | COPY_PHASE_STRIP = YES;
433 | ENABLE_NS_ASSERTIONS = NO;
434 | GCC_C_LANGUAGE_STANDARD = gnu99;
435 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
436 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
437 | GCC_WARN_UNDECLARED_SELECTOR = YES;
438 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
439 | GCC_WARN_UNUSED_FUNCTION = YES;
440 | GCC_WARN_UNUSED_VARIABLE = YES;
441 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
442 | SDKROOT = iphoneos;
443 | VALIDATE_PRODUCT = YES;
444 | };
445 | name = Release;
446 | };
447 | AF24722A18B30BAD00614655 /* Debug */ = {
448 | isa = XCBuildConfiguration;
449 | buildSettings = {
450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
451 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
452 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
453 | GCC_PREFIX_HEADER = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Prefix.pch";
454 | INFOPLIST_FILE = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Info.plist";
455 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
456 | PRODUCT_NAME = "$(TARGET_NAME)";
457 | WRAPPER_EXTENSION = app;
458 | };
459 | name = Debug;
460 | };
461 | AF24722B18B30BAD00614655 /* Release */ = {
462 | isa = XCBuildConfiguration;
463 | buildSettings = {
464 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
465 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
466 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
467 | GCC_PREFIX_HEADER = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Prefix.pch";
468 | INFOPLIST_FILE = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Info.plist";
469 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
470 | PRODUCT_NAME = "$(TARGET_NAME)";
471 | WRAPPER_EXTENSION = app;
472 | };
473 | name = Release;
474 | };
475 | AF24722D18B30BAD00614655 /* Debug */ = {
476 | isa = XCBuildConfiguration;
477 | buildSettings = {
478 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
479 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SECollectionViewFlowLayout.app/SECollectionViewFlowLayout";
480 | FRAMEWORK_SEARCH_PATHS = (
481 | "$(SDKROOT)/Developer/Library/Frameworks",
482 | "$(inherited)",
483 | "$(DEVELOPER_FRAMEWORKS_DIR)",
484 | );
485 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
486 | GCC_PREFIX_HEADER = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Prefix.pch";
487 | GCC_PREPROCESSOR_DEFINITIONS = (
488 | "DEBUG=1",
489 | "$(inherited)",
490 | );
491 | INFOPLIST_FILE = "SECollectionViewFlowLayoutTests/SECollectionViewFlowLayoutTests-Info.plist";
492 | PRODUCT_NAME = "$(TARGET_NAME)";
493 | TEST_HOST = "$(BUNDLE_LOADER)";
494 | WRAPPER_EXTENSION = xctest;
495 | };
496 | name = Debug;
497 | };
498 | AF24722E18B30BAD00614655 /* Release */ = {
499 | isa = XCBuildConfiguration;
500 | buildSettings = {
501 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
502 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SECollectionViewFlowLayout.app/SECollectionViewFlowLayout";
503 | FRAMEWORK_SEARCH_PATHS = (
504 | "$(SDKROOT)/Developer/Library/Frameworks",
505 | "$(inherited)",
506 | "$(DEVELOPER_FRAMEWORKS_DIR)",
507 | );
508 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
509 | GCC_PREFIX_HEADER = "SECollectionViewFlowLayout/SECollectionViewFlowLayout-Prefix.pch";
510 | INFOPLIST_FILE = "SECollectionViewFlowLayoutTests/SECollectionViewFlowLayoutTests-Info.plist";
511 | PRODUCT_NAME = "$(TARGET_NAME)";
512 | TEST_HOST = "$(BUNDLE_LOADER)";
513 | WRAPPER_EXTENSION = xctest;
514 | };
515 | name = Release;
516 | };
517 | /* End XCBuildConfiguration section */
518 |
519 | /* Begin XCConfigurationList section */
520 | AF2471F218B30BAD00614655 /* Build configuration list for PBXProject "SECollectionViewFlowLayout" */ = {
521 | isa = XCConfigurationList;
522 | buildConfigurations = (
523 | AF24722718B30BAD00614655 /* Debug */,
524 | AF24722818B30BAD00614655 /* Release */,
525 | );
526 | defaultConfigurationIsVisible = 0;
527 | defaultConfigurationName = Release;
528 | };
529 | AF24722918B30BAD00614655 /* Build configuration list for PBXNativeTarget "SECollectionViewFlowLayout" */ = {
530 | isa = XCConfigurationList;
531 | buildConfigurations = (
532 | AF24722A18B30BAD00614655 /* Debug */,
533 | AF24722B18B30BAD00614655 /* Release */,
534 | );
535 | defaultConfigurationIsVisible = 0;
536 | defaultConfigurationName = Release;
537 | };
538 | AF24722C18B30BAD00614655 /* Build configuration list for PBXNativeTarget "SECollectionViewFlowLayoutTests" */ = {
539 | isa = XCConfigurationList;
540 | buildConfigurations = (
541 | AF24722D18B30BAD00614655 /* Debug */,
542 | AF24722E18B30BAD00614655 /* Release */,
543 | );
544 | defaultConfigurationIsVisible = 0;
545 | defaultConfigurationName = Release;
546 | };
547 | /* End XCConfigurationList section */
548 | };
549 | rootObject = AF2471EF18B30BAD00614655 /* Project object */;
550 | }
551 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout.xcodeproj/xcuserdata/ChrisW.xcuserdatad/xcschemes/SECollectionViewFlowLayout.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
61 |
62 |
68 |
69 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout.xcodeproj/xcuserdata/ChrisW.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SECollectionViewFlowLayout.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | AF2471F618B30BAD00614655
16 |
17 | primary
18 |
19 |
20 | AF24721718B30BAD00614655
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/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 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "40x40",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "60x60",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "scale" : "2x"
9 | },
10 | {
11 | "orientation" : "portrait",
12 | "idiom" : "iphone",
13 | "subtype" : "retina4",
14 | "extent" : "full-screen",
15 | "minimum-system-version" : "7.0",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionCheckmarkView.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionCheckmarkView.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2014/01/01.
6 | // Copyright (c) 2014年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface QBAssetsCollectionCheckmarkView : UIView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionCheckmarkView.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionCheckmarkView.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2014/01/01.
6 | // Copyright (c) 2014年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBAssetsCollectionCheckmarkView.h"
10 |
11 | @implementation QBAssetsCollectionCheckmarkView
12 |
13 | - (instancetype)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 |
17 | if (self) {
18 | // View settings
19 | self.backgroundColor = [UIColor clearColor];
20 | }
21 |
22 | return self;
23 | }
24 |
25 | - (CGSize)sizeThatFits:(CGSize)size
26 | {
27 | return CGSizeMake(24.0, 24.0);
28 | }
29 |
30 | - (void)drawRect:(CGRect)rect
31 | {
32 | CGContextRef context = UIGraphicsGetCurrentContext();
33 |
34 | // Border
35 | CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
36 | CGContextFillEllipseInRect(context, self.bounds);
37 |
38 | // Body
39 | CGContextSetRGBFillColor(context, 20.0/255.0, 111.0/255.0, 223.0/255.0, 1.0);
40 | CGContextFillEllipseInRect(context, CGRectInset(self.bounds, 1.0, 1.0));
41 |
42 | // Checkmark
43 | CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
44 | CGContextSetLineWidth(context, 1.2);
45 |
46 | CGContextMoveToPoint(context, 6.0, 12.0);
47 | CGContextAddLineToPoint(context, 10.0, 16.0);
48 | CGContextAddLineToPoint(context, 18.0, 8.0);
49 |
50 | CGContextStrokePath(context);
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionFooterView.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionFooterView.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface QBAssetsCollectionFooterView : UICollectionReusableView
12 |
13 | @property (nonatomic, strong, readonly) UILabel *textLabel;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionFooterView.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionFooterView.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBAssetsCollectionFooterView.h"
10 |
11 | @interface QBAssetsCollectionFooterView ()
12 |
13 | @property (nonatomic, strong, readwrite) UILabel *textLabel;
14 |
15 | @end
16 |
17 | @implementation QBAssetsCollectionFooterView
18 |
19 | - (instancetype)initWithFrame:(CGRect)frame
20 | {
21 | self = [super initWithFrame:frame];
22 |
23 | if (self) {
24 | // Create a label
25 | UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
26 | textLabel.font = [UIFont systemFontOfSize:17];
27 | textLabel.textColor = [UIColor blackColor];
28 | textLabel.textAlignment = NSTextAlignmentCenter;
29 |
30 | [self addSubview:textLabel];
31 | self.textLabel = textLabel;
32 | }
33 |
34 | return self;
35 | }
36 |
37 | - (void)layoutSubviews
38 | {
39 | [super layoutSubviews];
40 |
41 | // Layout text label
42 | self.textLabel.frame = CGRectMake(0,
43 | (self.bounds.size.height - 21.0) / 2.0,
44 | self.bounds.size.width,
45 | 21.0);
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionOverlayView.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionOverlayView.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2014/01/01.
6 | // Copyright (c) 2014年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface QBAssetsCollectionOverlayView : UIView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionOverlayView.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionOverlayView.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2014/01/01.
6 | // Copyright (c) 2014年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBAssetsCollectionOverlayView.h"
10 | #import
11 |
12 | // Views
13 | #import "QBAssetsCollectionCheckmarkView.h"
14 |
15 | @interface QBAssetsCollectionOverlayView ()
16 |
17 | @end
18 |
19 | @implementation QBAssetsCollectionOverlayView
20 |
21 | - (instancetype)initWithFrame:(CGRect)frame
22 | {
23 | self = [super initWithFrame:frame];
24 |
25 | if (self) {
26 | // View settings
27 | self.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.5];
28 |
29 | // Create a checkmark view
30 | QBAssetsCollectionCheckmarkView *checkmarkView = [[QBAssetsCollectionCheckmarkView alloc] initWithFrame:CGRectMake(self.bounds.size.width - (4.0 + 24.0), self.bounds.size.height - (4.0 + 24.0), 24.0, 24.0)];
31 | checkmarkView.autoresizingMask = UIViewAutoresizingNone;
32 |
33 | [self addSubview:checkmarkView];
34 | }
35 |
36 | return self;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionViewCell.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface QBAssetsCollectionViewCell : UICollectionViewCell
13 |
14 | @property (nonatomic, strong) ALAsset *asset;
15 | @property (nonatomic, assign) BOOL showsOverlayViewWhenSelected;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionViewCell.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBAssetsCollectionViewCell.h"
10 |
11 | // Views
12 | #import "QBAssetsCollectionOverlayView.h"
13 |
14 | @interface QBAssetsCollectionViewCell ()
15 |
16 | @property (nonatomic, strong) UIImageView *imageView;
17 | @property (nonatomic, strong) QBAssetsCollectionOverlayView *overlayView;
18 |
19 | @end
20 |
21 | @implementation QBAssetsCollectionViewCell
22 |
23 | - (instancetype)initWithFrame:(CGRect)frame
24 | {
25 | self = [super initWithFrame:frame];
26 |
27 | if (self) {
28 | self.showsOverlayViewWhenSelected = YES;
29 |
30 | // Create a image view
31 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
32 | imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
33 |
34 | [self.contentView addSubview:imageView];
35 | self.imageView = imageView;
36 |
37 | self.overlayView = [[QBAssetsCollectionOverlayView alloc] initWithFrame:self.contentView.bounds];
38 | self.overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
39 |
40 | [self.contentView addSubview:self.overlayView];
41 | self.overlayView.alpha = 0.f;
42 | }
43 |
44 | return self;
45 | }
46 |
47 | - (void)setSelected:(BOOL)selected
48 | {
49 | [super setSelected:selected];
50 |
51 | // Show/hide overlay view
52 | if (selected && self.showsOverlayViewWhenSelected) {
53 | // [self hideOverlayView];
54 | [self showOverlayView];
55 | } else {
56 | [self hideOverlayView];
57 | }
58 | }
59 |
60 | - (void)showOverlayView
61 | {
62 | self.overlayView.alpha = 1.f;
63 | }
64 |
65 | - (void)hideOverlayView
66 | {
67 | self.overlayView.alpha = 0.f;
68 | }
69 |
70 |
71 | #pragma mark - Accessors
72 |
73 | - (void)setAsset:(ALAsset *)asset
74 | {
75 | _asset = asset;
76 |
77 | // Update view
78 | self.imageView.image = [UIImage imageWithCGImage:[asset thumbnail]];
79 | }
80 |
81 | @end
82 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionViewController.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | // ViewControllers
13 | #import "QBImagePickerController.h"
14 |
15 | @class QBAssetsCollectionViewController;
16 |
17 | @protocol QBAssetsCollectionViewControllerDelegate
18 |
19 | @optional
20 | - (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didSelectAsset:(ALAsset *)asset;
21 | - (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didDeselectAsset:(ALAsset *)asset;
22 | - (void)assetsCollectionViewControllerDidFinishSelection:(QBAssetsCollectionViewController *)assetsCollectionViewController;
23 |
24 | @end
25 |
26 | @interface QBAssetsCollectionViewController : UICollectionViewController
27 |
28 | @property (nonatomic, weak) QBImagePickerController *imagePickerController;
29 |
30 | @property (nonatomic, weak) id delegate;
31 | @property (nonatomic, strong) ALAssetsGroup *assetsGroup;
32 | @property (nonatomic, assign) QBImagePickerControllerFilterType filterType;
33 | @property (nonatomic, assign) BOOL allowsMultipleSelection;
34 | @property (nonatomic, assign) NSUInteger minimumNumberOfSelection;
35 | @property (nonatomic, assign) NSUInteger maximumNumberOfSelection;
36 |
37 | - (void)selectAssetHavingURL:(NSURL *)URL;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBAssetsCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBAssetsCollectionViewController.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBAssetsCollectionViewController.h"
10 |
11 | // Views
12 | #import "QBAssetsCollectionViewCell.h"
13 | #import "QBAssetsCollectionFooterView.h"
14 |
15 | @interface QBAssetsCollectionViewController ()
16 |
17 | @property (nonatomic, strong) NSMutableArray *assets;
18 |
19 | @property (nonatomic, assign) NSInteger numberOfPhotos;
20 | @property (nonatomic, assign) NSInteger numberOfVideos;
21 |
22 | @end
23 |
24 | @implementation QBAssetsCollectionViewController
25 |
26 | - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
27 | {
28 | self = [super initWithCollectionViewLayout:layout];
29 |
30 | if (self) {
31 | // View settings
32 | self.collectionView.backgroundColor = [UIColor whiteColor];
33 |
34 | // Register cell class
35 | [self.collectionView registerClass:[QBAssetsCollectionViewCell class]
36 | forCellWithReuseIdentifier:@"AssetsCell"];
37 | [self.collectionView registerClass:[QBAssetsCollectionFooterView class]
38 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
39 | withReuseIdentifier:@"FooterView"];
40 | }
41 |
42 | return self;
43 | }
44 |
45 | - (void)viewWillAppear:(BOOL)animated
46 | {
47 | [super viewWillAppear:animated];
48 |
49 | // Scroll to bottom --- iOS 7 differences
50 | CGFloat topInset;
51 | if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) {
52 | topInset = ((self.edgesForExtendedLayout && UIRectEdgeTop) && (self.collectionView.contentInset.top == 0)) ? (20.0 + 44.0) : 0.0;
53 | } else {
54 | topInset = (self.collectionView.contentInset.top == 0) ? (20.0 + 44.0) : 0.0;
55 | }
56 |
57 | [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.collectionViewLayout.collectionViewContentSize.height - self.collectionView.frame.size.height + topInset)
58 | animated:NO];
59 |
60 | // Validation
61 | if (self.allowsMultipleSelection) {
62 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.imagePickerController.selectedAssetURLs.count];
63 | }
64 | }
65 |
66 | #pragma mark - Accessors
67 |
68 | - (void)setFilterType:(QBImagePickerControllerFilterType)filterType
69 | {
70 | _filterType = filterType;
71 |
72 | // Set assets filter
73 | [self.assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(self.filterType)];
74 | }
75 |
76 | - (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup
77 | {
78 | _assetsGroup = assetsGroup;
79 |
80 | // Set title
81 | self.title = [self.assetsGroup valueForProperty:ALAssetsGroupPropertyName];
82 |
83 | // Get the number of photos and videos
84 | [self.assetsGroup setAssetsFilter:[ALAssetsFilter allPhotos]];
85 | self.numberOfPhotos = self.assetsGroup.numberOfAssets;
86 |
87 | [self.assetsGroup setAssetsFilter:[ALAssetsFilter allVideos]];
88 | self.numberOfVideos = self.assetsGroup.numberOfAssets;
89 |
90 | // Set assets filter
91 | [self.assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(self.filterType)];
92 |
93 | // Load assets
94 | self.assets = [NSMutableArray array];
95 |
96 | __weak typeof(self) weakSelf = self;
97 | [self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
98 | if (result) {
99 | [weakSelf.assets addObject:result];
100 | }
101 | }];
102 |
103 | // Update view
104 | [self.collectionView reloadData];
105 | }
106 |
107 | - (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection
108 | {
109 | self.collectionView.allowsMultipleSelection = allowsMultipleSelection;
110 |
111 | // Show/hide done button
112 | if (allowsMultipleSelection) {
113 | UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)];
114 | [self.navigationItem setRightBarButtonItem:doneButton animated:NO];
115 | } else {
116 | [self.navigationItem setRightBarButtonItem:nil animated:NO];
117 | }
118 | }
119 |
120 | - (BOOL)allowsMultipleSelection
121 | {
122 | return self.collectionView.allowsMultipleSelection;
123 | }
124 |
125 |
126 | #pragma mark - Actions
127 |
128 | - (void)done:(id)sender
129 | {
130 | // Delegate
131 | if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewControllerDidFinishSelection:)]) {
132 | [self.delegate assetsCollectionViewControllerDidFinishSelection:self];
133 | }
134 | }
135 |
136 | #pragma mark - Managing Selection
137 |
138 | - (void)selectAssetHavingURL:(NSURL *)URL
139 | {
140 | for (NSInteger i = 0; i < self.assets.count; i++) {
141 | ALAsset *asset = [self.assets objectAtIndex:i];
142 | NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL];
143 |
144 | if ([assetURL isEqual:URL]) {
145 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
146 | [self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
147 |
148 | return;
149 | }
150 | }
151 | }
152 |
153 |
154 | #pragma mark - Validating Selections
155 |
156 | - (BOOL)validateNumberOfSelections:(NSUInteger)numberOfSelections
157 | {
158 | NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection);
159 | BOOL qualifiesMinimumNumberOfSelection = (numberOfSelections >= minimumNumberOfSelection);
160 |
161 | BOOL qualifiesMaximumNumberOfSelection = YES;
162 | if (minimumNumberOfSelection <= self.maximumNumberOfSelection) {
163 | qualifiesMaximumNumberOfSelection = (numberOfSelections <= self.maximumNumberOfSelection);
164 | }
165 |
166 | return (qualifiesMinimumNumberOfSelection && qualifiesMaximumNumberOfSelection);
167 | }
168 |
169 | - (BOOL)validateMaximumNumberOfSelections:(NSUInteger)numberOfSelections
170 | {
171 | NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection);
172 |
173 | if (minimumNumberOfSelection <= self.maximumNumberOfSelection) {
174 | return (numberOfSelections <= self.maximumNumberOfSelection);
175 | }
176 |
177 | return YES;
178 | }
179 |
180 | #pragma mark - UICollectionViewDataSource
181 |
182 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
183 | {
184 | return 1;
185 | }
186 |
187 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
188 | {
189 | return self.assetsGroup.numberOfAssets;
190 | }
191 |
192 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
193 | {
194 | QBAssetsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AssetsCell" forIndexPath:indexPath];
195 | cell.showsOverlayViewWhenSelected = self.allowsMultipleSelection;
196 |
197 | ALAsset *asset = [self.assets objectAtIndex:indexPath.row];
198 | cell.asset = asset;
199 |
200 | return cell;
201 | }
202 |
203 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
204 | {
205 | return CGSizeMake(collectionView.bounds.size.width, 46.0);
206 | }
207 |
208 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
209 | {
210 | if (kind == UICollectionElementKindSectionFooter) {
211 | QBAssetsCollectionFooterView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
212 | withReuseIdentifier:@"FooterView"
213 | forIndexPath:indexPath];
214 |
215 | switch (self.filterType) {
216 | case QBImagePickerControllerFilterTypeNone:
217 | footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"format_photos_and_videos",
218 | @"QBImagePickerController",
219 | nil),
220 | self.numberOfPhotos,
221 | self.numberOfVideos
222 | ];
223 | break;
224 |
225 | case QBImagePickerControllerFilterTypePhotos:
226 | footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"format_photos",
227 | @"QBImagePickerController",
228 | nil),
229 | self.numberOfPhotos
230 | ];
231 | break;
232 |
233 | case QBImagePickerControllerFilterTypeVideos:
234 | footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"format_videos",
235 | @"QBImagePickerController",
236 | nil),
237 | self.numberOfVideos
238 | ];
239 | break;
240 | }
241 |
242 | return footerView;
243 | }
244 |
245 | return nil;
246 | }
247 |
248 |
249 | #pragma mark - UICollectionViewDelegateFlowLayout
250 |
251 | //- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
252 | //{
253 | // if (indexPath.row % 2 == 0) {
254 | // return CGSizeMake(50.5, 50.f);
255 | // }
256 | //
257 | // CGFloat size = (indexPath.row + 1) * 5.f;
258 | //
259 | // return CGSizeMake(size, size);
260 | //}
261 |
262 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
263 | {
264 | return UIEdgeInsetsMake(2, 2, 2, 2);
265 | }
266 |
267 | - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
268 | {
269 | return [self validateMaximumNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count + 1)];
270 | }
271 |
272 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
273 | {
274 | ALAsset *asset = [self.assets objectAtIndex:indexPath.row];
275 |
276 | // Validation
277 | if (self.allowsMultipleSelection) {
278 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count + 1)];
279 | }
280 |
281 | // Delegate
282 | if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewController:didSelectAsset:)]) {
283 | [self.delegate assetsCollectionViewController:self didSelectAsset:asset];
284 | }
285 | }
286 |
287 | - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
288 | {
289 | ALAsset *asset = [self.assets objectAtIndex:indexPath.row];
290 |
291 | // Validation
292 | if (self.allowsMultipleSelection) {
293 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count - 1)];
294 | }
295 |
296 | // Delegate
297 | if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewController:didDeselectAsset:)]) {
298 | [self.delegate assetsCollectionViewController:self didDeselectAsset:asset];
299 | }
300 | }
301 |
302 | @end
303 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerController.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerController.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/30.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef NS_ENUM(NSUInteger, QBImagePickerControllerFilterType) {
13 | QBImagePickerControllerFilterTypeNone,
14 | QBImagePickerControllerFilterTypePhotos,
15 | QBImagePickerControllerFilterTypeVideos
16 | };
17 |
18 | UIKIT_EXTERN ALAssetsFilter * ALAssetsFilterFromQBImagePickerControllerFilterType(QBImagePickerControllerFilterType type);
19 |
20 | @class QBImagePickerController;
21 |
22 | @protocol QBImagePickerControllerDelegate
23 |
24 | @optional
25 | - (void)imagePickerController:(QBImagePickerController *)imagePickerController didSelectAsset:(ALAsset *)asset;
26 | - (void)imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets;
27 | - (void)imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController;
28 |
29 | @end
30 |
31 | @interface QBImagePickerController : UITableViewController
32 |
33 | @property (nonatomic, strong, readonly) ALAssetsLibrary *assetsLibrary;
34 | @property (nonatomic, copy, readonly) NSArray *assetsGroups;
35 | @property (nonatomic, strong, readonly) NSMutableSet *selectedAssetURLs;
36 |
37 | @property (nonatomic, weak) id delegate;
38 | @property (nonatomic, copy) NSArray *groupTypes;
39 | @property (nonatomic, assign) QBImagePickerControllerFilterType filterType;
40 | @property (nonatomic, assign) BOOL showsCancelButton;
41 | @property (nonatomic, assign) BOOL allowsMultipleSelection;
42 | @property (nonatomic, assign) NSUInteger minimumNumberOfSelection;
43 | @property (nonatomic, assign) NSUInteger maximumNumberOfSelection;
44 |
45 | + (BOOL)isAccessible;
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerController.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerController.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/30.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBImagePickerController.h"
10 | #import
11 |
12 | // Views
13 | #import "QBImagePickerGroupCell.h"
14 | #import "SECollectionViewFlowLayout.h"
15 |
16 | // ViewControllers
17 | #import "QBAssetsCollectionViewController.h"
18 |
19 | ALAssetsFilter * ALAssetsFilterFromQBImagePickerControllerFilterType(QBImagePickerControllerFilterType type) {
20 | switch (type) {
21 | case QBImagePickerControllerFilterTypeNone:
22 | return [ALAssetsFilter allAssets];
23 | break;
24 |
25 | case QBImagePickerControllerFilterTypePhotos:
26 | return [ALAssetsFilter allPhotos];
27 | break;
28 |
29 | case QBImagePickerControllerFilterTypeVideos:
30 | return [ALAssetsFilter allVideos];
31 | break;
32 | }
33 | }
34 |
35 | @interface QBImagePickerController ()
36 |
37 | @property (nonatomic, strong, readwrite) ALAssetsLibrary *assetsLibrary;
38 | @property (nonatomic, copy, readwrite) NSArray *assetsGroups;
39 | @property (nonatomic, strong, readwrite) NSMutableSet *selectedAssetURLs;
40 |
41 | @end
42 |
43 | @implementation QBImagePickerController
44 |
45 | + (BOOL)isAccessible
46 | {
47 | return ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] &&
48 | [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]);
49 | }
50 |
51 | - (instancetype)initWithStyle:(UITableViewStyle)style
52 | {
53 | self = [super initWithStyle:UITableViewStylePlain];
54 |
55 | if (self) {
56 | // Property settings
57 | self.selectedAssetURLs = [NSMutableSet set];
58 |
59 | self.groupTypes = @[
60 | @(ALAssetsGroupSavedPhotos),
61 | @(ALAssetsGroupPhotoStream),
62 | @(ALAssetsGroupAlbum)
63 | ];
64 | self.filterType = QBImagePickerControllerFilterTypeNone;
65 | self.showsCancelButton = YES;
66 |
67 | // View settings
68 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
69 |
70 | // Create assets library instance
71 | ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
72 | self.assetsLibrary = assetsLibrary;
73 |
74 | // Register cell classes
75 | [self.tableView registerClass:[QBImagePickerGroupCell class] forCellReuseIdentifier:@"GroupCell"];
76 | }
77 |
78 | return self;
79 | }
80 |
81 | - (void)viewDidLoad
82 | {
83 | [super viewDidLoad];
84 |
85 | // View controller settings
86 | self.title = NSLocalizedStringFromTable(@"title", @"QBImagePickerController", nil);
87 | }
88 |
89 | - (void)viewWillAppear:(BOOL)animated
90 | {
91 | [super viewWillAppear:animated];
92 |
93 | // Load assets groups
94 | [self loadAssetsGroupsWithTypes:self.groupTypes
95 | completion:^(NSArray *assetsGroups) {
96 | self.assetsGroups = assetsGroups;
97 |
98 | [self.tableView reloadData];
99 | }];
100 |
101 | // Validation
102 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count];
103 | }
104 |
105 |
106 | #pragma mark - Accessors
107 |
108 | - (void)setShowsCancelButton:(BOOL)showsCancelButton
109 | {
110 | _showsCancelButton = showsCancelButton;
111 |
112 | // Show/hide cancel button
113 | if (showsCancelButton) {
114 | UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
115 | [self.navigationItem setLeftBarButtonItem:cancelButton animated:NO];
116 | } else {
117 | [self.navigationItem setLeftBarButtonItem:nil animated:NO];
118 | }
119 | }
120 |
121 | - (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection
122 | {
123 | _allowsMultipleSelection = allowsMultipleSelection;
124 |
125 | // Show/hide done button
126 | if (allowsMultipleSelection) {
127 | UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)];
128 | [self.navigationItem setRightBarButtonItem:doneButton animated:NO];
129 | } else {
130 | [self.navigationItem setRightBarButtonItem:nil animated:NO];
131 | }
132 | }
133 |
134 |
135 | #pragma mark - Actions
136 |
137 | - (void)done:(id)sender
138 | {
139 | [self passSelectedAssetsToDelegate];
140 | }
141 |
142 | - (void)cancel:(id)sender
143 | {
144 | // Delegate
145 | if (self.delegate && [self.delegate respondsToSelector:@selector(imagePickerControllerDidCancel:)]) {
146 | [self.delegate imagePickerControllerDidCancel:self];
147 | }
148 | }
149 |
150 |
151 | #pragma mark - Validating Selections
152 |
153 | - (BOOL)validateNumberOfSelections:(NSUInteger)numberOfSelections
154 | {
155 | // Check the number of selected assets
156 | NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection);
157 | BOOL qualifiesMinimumNumberOfSelection = (numberOfSelections >= minimumNumberOfSelection);
158 |
159 | BOOL qualifiesMaximumNumberOfSelection = YES;
160 | if (minimumNumberOfSelection <= self.maximumNumberOfSelection) {
161 | qualifiesMaximumNumberOfSelection = (numberOfSelections <= self.maximumNumberOfSelection);
162 | }
163 |
164 | return (qualifiesMinimumNumberOfSelection && qualifiesMaximumNumberOfSelection);
165 | }
166 |
167 |
168 | #pragma mark - Managing Assets
169 |
170 | - (void)loadAssetsGroupsWithTypes:(NSArray *)types completion:(void (^)(NSArray *assetsGroups))completion
171 | {
172 | __block NSMutableArray *assetsGroups = [NSMutableArray array];
173 | __block NSUInteger numberOfFinishedTypes = 0;
174 |
175 | for (NSNumber *type in types) {
176 | __weak typeof(self) weakSelf = self;
177 |
178 | [self.assetsLibrary enumerateGroupsWithTypes:[type unsignedIntegerValue]
179 | usingBlock:^(ALAssetsGroup *assetsGroup, BOOL *stop) {
180 | if (assetsGroup) {
181 | // Filter the assets group
182 | [assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(weakSelf.filterType)];
183 |
184 | if (assetsGroup.numberOfAssets > 0) {
185 | // Add assets group
186 | [assetsGroups addObject:assetsGroup];
187 | }
188 | } else {
189 | numberOfFinishedTypes++;
190 | }
191 |
192 | // Check if the loading finished
193 | if (numberOfFinishedTypes == types.count) {
194 | // Sort assets groups
195 | NSArray *sortedAssetsGroups = [self sortAssetsGroups:(NSArray *)assetsGroups typesOrder:types];
196 |
197 | // Call completion block
198 | if (completion) {
199 | completion(sortedAssetsGroups);
200 | }
201 | }
202 | } failureBlock:^(NSError *error) {
203 | NSLog(@"Error: %@", [error localizedDescription]);
204 | }];
205 | }
206 | }
207 |
208 | - (NSArray *)sortAssetsGroups:(NSArray *)assetsGroups typesOrder:(NSArray *)typesOrder
209 | {
210 | NSMutableArray *sortedAssetsGroups = [NSMutableArray array];
211 |
212 | for (ALAssetsGroup *assetsGroup in assetsGroups) {
213 | if (sortedAssetsGroups.count == 0) {
214 | [sortedAssetsGroups addObject:assetsGroup];
215 | continue;
216 | }
217 |
218 | ALAssetsGroupType assetsGroupType = [[assetsGroup valueForProperty:ALAssetsGroupPropertyType] unsignedIntegerValue];
219 | NSUInteger indexOfAssetsGroupType = [typesOrder indexOfObject:@(assetsGroupType)];
220 |
221 | for (NSInteger i = 0; i <= sortedAssetsGroups.count; i++) {
222 | if (i == sortedAssetsGroups.count) {
223 | [sortedAssetsGroups addObject:assetsGroup];
224 | break;
225 | }
226 |
227 | ALAssetsGroup *sortedAssetsGroup = [sortedAssetsGroups objectAtIndex:i];
228 | ALAssetsGroupType sortedAssetsGroupType = [[sortedAssetsGroup valueForProperty:ALAssetsGroupPropertyType] unsignedIntegerValue];
229 | NSUInteger indexOfSortedAssetsGroupType = [typesOrder indexOfObject:@(sortedAssetsGroupType)];
230 |
231 | if (indexOfAssetsGroupType < indexOfSortedAssetsGroupType) {
232 | [sortedAssetsGroups insertObject:assetsGroup atIndex:i];
233 | break;
234 | }
235 | }
236 | }
237 |
238 | return [sortedAssetsGroups copy];
239 | }
240 |
241 | - (void)passSelectedAssetsToDelegate
242 | {
243 | // Load assets from URLs
244 | __block NSMutableArray *assets = [NSMutableArray array];
245 |
246 | for (NSURL *selectedAssetURL in self.selectedAssetURLs) {
247 | __weak typeof(self) weakSelf = self;
248 | [self.assetsLibrary assetForURL:selectedAssetURL
249 | resultBlock:^(ALAsset *asset) {
250 | // Add asset
251 | [assets addObject:asset];
252 |
253 | // Check if the loading finished
254 | if (assets.count == weakSelf.selectedAssetURLs.count) {
255 | // Delegate
256 | if (self.delegate && [self.delegate respondsToSelector:@selector(imagePickerController:didSelectAssets:)]) {
257 | [self.delegate imagePickerController:self didSelectAssets:[assets copy]];
258 | }
259 | }
260 | } failureBlock:^(NSError *error) {
261 | NSLog(@"Error: %@", [error localizedDescription]);
262 | }];
263 | }
264 | }
265 |
266 |
267 | #pragma mark - UITableViewDataSource
268 |
269 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
270 | {
271 | return 1;
272 | }
273 |
274 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
275 | {
276 | return self.assetsGroups.count;
277 | }
278 |
279 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
280 | {
281 | QBImagePickerGroupCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupCell" forIndexPath:indexPath];
282 |
283 | ALAssetsGroup *assetsGroup = [self.assetsGroups objectAtIndex:indexPath.row];
284 | cell.assetsGroup = assetsGroup;
285 |
286 | return cell;
287 | }
288 |
289 |
290 | #pragma mark - UITableViewDelegate
291 |
292 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
293 | {
294 | return 86.0;
295 | }
296 |
297 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
298 | {
299 | QBAssetsCollectionViewController *assetsCollectionViewController = [[QBAssetsCollectionViewController alloc] initWithCollectionViewLayout:[SECollectionViewFlowLayout layoutWithAutoSelectRows:YES panToDeselect:YES autoSelectCellsBetweenTouches:YES]];
300 | assetsCollectionViewController.imagePickerController = self;
301 | assetsCollectionViewController.filterType = self.filterType;
302 | assetsCollectionViewController.allowsMultipleSelection = self.allowsMultipleSelection;
303 | assetsCollectionViewController.minimumNumberOfSelection = self.minimumNumberOfSelection;
304 | assetsCollectionViewController.maximumNumberOfSelection = self.maximumNumberOfSelection;
305 |
306 | ALAssetsGroup *assetsGroup = [self.assetsGroups objectAtIndex:indexPath.row];
307 | assetsCollectionViewController.delegate = self;
308 | assetsCollectionViewController.assetsGroup = assetsGroup;
309 |
310 | for (NSURL *assetURL in self.selectedAssetURLs) {
311 | [assetsCollectionViewController selectAssetHavingURL:assetURL];
312 | }
313 |
314 | [self.navigationController pushViewController:assetsCollectionViewController animated:YES];
315 | }
316 |
317 |
318 | #pragma mark - QBAssetsCollectionViewControllerDelegate
319 |
320 | - (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didSelectAsset:(ALAsset *)asset
321 | {
322 | if (self.allowsMultipleSelection) {
323 | // Add asset URL
324 | NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL];
325 | [self.selectedAssetURLs addObject:assetURL];
326 |
327 | // Validation
328 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count];
329 | } else {
330 | // Delegate
331 | if (self.delegate && [self.delegate respondsToSelector:@selector(imagePickerController:didSelectAsset:)]) {
332 | [self.delegate imagePickerController:self didSelectAsset:asset];
333 | }
334 | }
335 | }
336 |
337 | - (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didDeselectAsset:(ALAsset *)asset
338 | {
339 | if (self.allowsMultipleSelection) {
340 | // Remove asset URL
341 | NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL];
342 | [self.selectedAssetURLs removeObject:assetURL];
343 |
344 | // Validation
345 | self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count];
346 | }
347 | }
348 |
349 | - (void)assetsCollectionViewControllerDidFinishSelection:(QBAssetsCollectionViewController *)assetsCollectionViewController
350 | {
351 | [self passSelectedAssetsToDelegate];
352 | }
353 |
354 | @end
355 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerGroupCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerGroupCell.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/30.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface QBImagePickerGroupCell : UITableViewCell
13 |
14 | @property (nonatomic, strong) ALAssetsGroup *assetsGroup;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerGroupCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerGroupCell.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/30.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBImagePickerGroupCell.h"
10 |
11 | // Views
12 | #import "QBImagePickerThumbnailView.h"
13 |
14 | @interface QBImagePickerGroupCell ()
15 |
16 | @property (nonatomic, strong) QBImagePickerThumbnailView *thumbnailView;
17 | @property (nonatomic, strong) UILabel *nameLabel;
18 | @property (nonatomic, strong) UILabel *countLabel;
19 |
20 | @end
21 |
22 | @implementation QBImagePickerGroupCell
23 |
24 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
25 | {
26 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
27 |
28 | if (self) {
29 | // Cell settings
30 | self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
31 |
32 | // Create thumbnail view
33 | QBImagePickerThumbnailView *thumbnailView = [[QBImagePickerThumbnailView alloc] initWithFrame:CGRectMake(8, 4, 70, 74)];
34 | thumbnailView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
35 |
36 | [self.contentView addSubview:thumbnailView];
37 | self.thumbnailView = thumbnailView;
38 |
39 | // Create name label
40 | UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(8 + 70 + 18, 22, 180, 21)];
41 | nameLabel.font = [UIFont systemFontOfSize:17];
42 | nameLabel.textColor = [UIColor blackColor];
43 | nameLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
44 |
45 | [self.contentView addSubview:nameLabel];
46 | self.nameLabel = nameLabel;
47 |
48 | // Create count label
49 | UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(8 + 70 + 18, 46, 180, 15)];
50 | countLabel.font = [UIFont systemFontOfSize:12];
51 | countLabel.textColor = [UIColor blackColor];
52 | countLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
53 |
54 | [self.contentView addSubview:countLabel];
55 | self.countLabel = countLabel;
56 | }
57 |
58 | return self;
59 | }
60 |
61 |
62 | #pragma mark - Accessors
63 |
64 | - (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup
65 | {
66 | _assetsGroup = assetsGroup;
67 |
68 | // Update thumbnail view
69 | self.thumbnailView.assetsGroup = self.assetsGroup;
70 |
71 | // Update label
72 | self.nameLabel.text = [self.assetsGroup valueForProperty:ALAssetsGroupPropertyName];
73 | self.countLabel.text = [NSString stringWithFormat:@"%ld", (long)self.assetsGroup.numberOfAssets];
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerThumbnailView.h:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerThumbnailView.h
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface QBImagePickerThumbnailView : UIView
13 |
14 | @property (nonatomic, strong) ALAssetsGroup *assetsGroup;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/QBImagePickerThumbnailView.m:
--------------------------------------------------------------------------------
1 | //
2 | // QBImagePickerThumbnailView.m
3 | // QBImagePickerController
4 | //
5 | // Created by Tanaka Katsuma on 2013/12/31.
6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved.
7 | //
8 |
9 | #import "QBImagePickerThumbnailView.h"
10 |
11 | @interface QBImagePickerThumbnailView ()
12 |
13 | @property (nonatomic, copy) NSArray *thumbnailImages;
14 | @end
15 |
16 | @implementation QBImagePickerThumbnailView
17 |
18 | - (instancetype)initWithFrame:(CGRect)frame
19 | {
20 | self = [super initWithFrame:frame];
21 |
22 | if (self) {
23 | self.backgroundColor = [UIColor clearColor];
24 | }
25 |
26 | return self;
27 | }
28 |
29 | - (CGSize)sizeThatFits:(CGSize)size
30 | {
31 | return CGSizeMake(70.0, 74.0);
32 | }
33 |
34 | - (void)drawRect:(CGRect)rect
35 | {
36 | [super drawRect:rect];
37 |
38 | CGContextRef context = UIGraphicsGetCurrentContext();
39 | CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
40 |
41 | if (self.thumbnailImages.count == 3) {
42 | UIImage *thumbnailImage = [self.thumbnailImages objectAtIndex:2];
43 |
44 | CGRect thumbnailImageRect = CGRectMake(4.0, 0, 62.0, 62.0);
45 | CGContextFillRect(context, thumbnailImageRect);
46 | [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)];
47 | }
48 |
49 | if (self.thumbnailImages.count >= 2) {
50 | UIImage *thumbnailImage = [self.thumbnailImages objectAtIndex:1];
51 |
52 | CGRect thumbnailImageRect = CGRectMake(2.0, 2.0, 66.0, 66.0);
53 | CGContextFillRect(context, thumbnailImageRect);
54 | [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)];
55 | }
56 |
57 | UIImage *thumbnailImage = [self.thumbnailImages objectAtIndex:0];
58 |
59 | CGRect thumbnailImageRect = CGRectMake(0, 4.0, 70.0, 70.0);
60 | CGContextFillRect(context, thumbnailImageRect);
61 | [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)];
62 | }
63 |
64 |
65 | #pragma mark - Accessors
66 |
67 | - (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup
68 | {
69 | _assetsGroup = assetsGroup;
70 |
71 | // Extract three thumbnail images
72 | NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, MIN(3, assetsGroup.numberOfAssets))];
73 | NSMutableArray *thumbnailImages = [NSMutableArray array];
74 | [assetsGroup enumerateAssetsAtIndexes:indexes
75 | options:0
76 | usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
77 | if (result) {
78 | UIImage *thumbnailImage = [UIImage imageWithCGImage:[result thumbnail]];
79 | [thumbnailImages addObject:thumbnailImage];
80 | }
81 | }];
82 | self.thumbnailImages = [thumbnailImages copy];
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/SECollectionViewFlowLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // SECollectionViewFlowLayout.h
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2014/1/30.
6 | // Copyright (c) 2014 Chris Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SECollectionViewFlowLayout : UICollectionViewFlowLayout
12 |
13 | @property (nonatomic) BOOL panToDeselect; // If set to YES, enables the deselecting of cells by panning around on a touch down
14 | @property (nonatomic) BOOL autoSelectRows; // If set to YES, a pan across a row and then down a column will auto-select all cells in each row as you scroll down (used to easily select a lot of cells rather than panning over every cell)
15 | @property (nonatomic) BOOL autoSelectCellsBetweenTouches; // If set to YES, enables auto-selecting all cells between a first and second selected cell
16 |
17 | + (instancetype)layout;
18 |
19 | + (instancetype)layoutWithAutoSelectRows:(BOOL)autoSelectRows
20 | panToDeselect:(BOOL)panToDeselect
21 | autoSelectCellsBetweenTouches:(BOOL)autoSelectCellsBetweenTouches;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/PodFiles/SECollectionViewFlowLayout.m:
--------------------------------------------------------------------------------
1 | //
2 | // SECollectionViewFlowLayout.m
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2014/1/30.
6 | // Copyright (c) 2014 Chris Wendel. All rights reserved.
7 | //
8 |
9 | #import "SECollectionViewFlowLayout.h"
10 |
11 | static NSString * const kSECollectionViewKeyPath = @"collectionView";
12 |
13 | @interface SECollectionViewFlowLayout ()
14 |
15 | @property (nonatomic, assign, getter = isSelecting) BOOL selecting;
16 |
17 | // Pan gesture states
18 | @property (nonatomic, assign) BOOL selectedRow;
19 | @property (nonatomic, assign) BOOL selectRowCancelled;
20 | @property (nonatomic, assign) BOOL pannedFromFirstColumn;
21 | @property (nonatomic, assign) BOOL pannedFromLastColumn;
22 | @property (nonatomic, assign, getter = isDeselecting) BOOL deselecting;
23 |
24 | // Touch gesture states
25 | @property (nonatomic, strong) NSIndexPath *initialSelectedIndexPath;
26 |
27 | @property (nonatomic, strong) NSIndexPath *previousIndexPath; // Used for auto selecting rows
28 |
29 | @end
30 |
31 | @implementation SECollectionViewFlowLayout
32 |
33 | #pragma mark - Initializers
34 |
35 | + (instancetype)layout
36 | {
37 | return [[self alloc] init];
38 | }
39 |
40 | + (instancetype)layoutWithAutoSelectRows:(BOOL)autoSelectRows
41 | panToDeselect:(BOOL)panToDeselect
42 | autoSelectCellsBetweenTouches:(BOOL)autoSelectCellsBetweenTouches
43 | {
44 | return [[self alloc] initWithAutoSelectRows:autoSelectRows panToDeselect:panToDeselect autoSelectCellsBetweenTouches:autoSelectCellsBetweenTouches];
45 | }
46 |
47 | - (instancetype)initWithAutoSelectRows:(BOOL)autoSelectRows
48 | panToDeselect:(BOOL)panToDeselect
49 | autoSelectCellsBetweenTouches:(BOOL)autoSelectCellsBetweenTouches
50 | {
51 | self = [super init];
52 |
53 | if (self) {
54 | _autoSelectRows = autoSelectRows;
55 | _panToDeselect = panToDeselect;
56 | _autoSelectCellsBetweenTouches = autoSelectCellsBetweenTouches;
57 |
58 | [self initializer];
59 | }
60 |
61 | return self;
62 | }
63 |
64 | - (instancetype)init
65 | {
66 | self = [super init];
67 |
68 | if (self) {
69 | // All default to NO
70 | _panToDeselect = NO;
71 | _autoSelectRows = NO;
72 | _autoSelectCellsBetweenTouches = NO;
73 |
74 | [self initializer];
75 | }
76 |
77 | return self;
78 | }
79 |
80 | - (void)initializer
81 | {
82 | // Pan states
83 | _selectRowCancelled = NO;
84 | _selecting = NO;
85 | _selectedRow = NO;
86 | _pannedFromFirstColumn = NO;
87 | _deselecting = NO;
88 |
89 | // Collection view layout properties
90 | self.minimumLineSpacing = 2.0;
91 | self.minimumInteritemSpacing = 2.0;
92 | self.itemSize = CGSizeMake(75.f, 75.f);
93 |
94 | // KVO
95 | [self addObserver:self forKeyPath:kSECollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil];
96 | }
97 |
98 | - (void)setupCollectionView
99 | {
100 | // Add tap gesture recognizer
101 | UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
102 | [self.collectionView addGestureRecognizer:tapGestureRecognizer];
103 |
104 | // Add pan gesture recognizer
105 | UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
106 | panGestureRecognizer.delegate = self;
107 | [self.collectionView addGestureRecognizer:panGestureRecognizer];
108 | }
109 |
110 | #pragma mark - Gesture handling
111 |
112 | - (void)handleTapGesture:(UITapGestureRecognizer *)tapGestureRecognizer
113 | {
114 | // Get index path at tapped point
115 | CGPoint point = [tapGestureRecognizer locationInView:self.collectionView];
116 | NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
117 |
118 | // Handle tap
119 | if (indexPath) {
120 | UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
121 | if (cell.isSelected) {
122 | [self deselectCellAtIndexPath:indexPath];
123 | } else {
124 | // Check if we should handle auto selecting the cells between two touches
125 | if (self.autoSelectCellsBetweenTouches) {
126 | if (self.initialSelectedIndexPath) {
127 | [self selectAllItemsFromIndexPath:self.initialSelectedIndexPath toIndexPath:indexPath];
128 | self.initialSelectedIndexPath = nil;
129 | } else {
130 | self.initialSelectedIndexPath = indexPath;
131 | }
132 | }
133 |
134 | [self selectCellAtIndexPath:indexPath];
135 | }
136 | }
137 | }
138 |
139 | - (void)handlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer
140 | {
141 | // Get velocity and point of pan
142 | CGPoint velocity = [panGestureRecognizer velocityInView:self.collectionView];
143 | CGPoint point = [panGestureRecognizer locationInView:self.collectionView];
144 |
145 | if (!self.collectionView.isDecelerating) {
146 | // Handle pan
147 | if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
148 | // Reset pan states
149 | self.selecting = NO;
150 | self.selectedRow = NO;
151 | self.selectRowCancelled = NO;
152 | self.pannedFromFirstColumn = NO;
153 | self.pannedFromLastColumn = NO;
154 | self.deselecting = NO;
155 | self.previousIndexPath = nil;
156 | } else {
157 | if (fabs(velocity.x) < fabs(velocity.y) && !self.selecting) {
158 | // Register as scrolling the collection view
159 | self.selecting = NO;
160 | }else {
161 | // Register as selecting the cells, not scrolling the collection view
162 | self.selecting = YES;
163 | NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
164 | if (indexPath) {
165 | UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
166 | if (cell.selected) {
167 | if (self.panToDeselect) {
168 | if (!self.previousIndexPath && ![self.previousIndexPath isEqual:indexPath]) self.deselecting = YES;
169 | if (self.deselecting) [self deselectCellAtIndexPath:indexPath];
170 | }
171 | } else {
172 | if (!self.deselecting) {
173 | [self selectCellAtIndexPath:indexPath];
174 |
175 | if (self.autoSelectRows) [self handleAutoSelectingRowsAtIndexPath:indexPath];
176 | }
177 | }
178 |
179 | // Update previousIndexPath
180 | self.previousIndexPath = indexPath;
181 | }
182 | }
183 | }
184 | }
185 | }
186 |
187 | #pragma mark - Auto select rows helpers
188 |
189 | - (void)handleAutoSelectingRowsAtIndexPath:(NSIndexPath *)indexPath
190 | {
191 | UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
192 |
193 | NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row + 1 inSection:indexPath.section];
194 | UICollectionViewCell *nextCell = [self.collectionView cellForItemAtIndexPath:nextIndexPath];
195 |
196 | // Check if this is the initial pan from the first column
197 | if (!self.previousIndexPath) {
198 | if (cell.frame.origin.x == self.minimumInteritemSpacing) {
199 | // Is the first cell in the row
200 | self.pannedFromFirstColumn = YES;
201 | } else if (!nextCell || nextCell.frame.origin.x < cell.frame.origin.x) {
202 | self.pannedFromLastColumn = YES;
203 | }
204 | }
205 |
206 | // Check to make sure we have not panned to another section
207 | if (abs(self.previousIndexPath.section != indexPath.section)) {
208 | self.selectedRow = NO;
209 | }
210 |
211 | // Figure out if this cell is in the first or last column
212 | BOOL didSelectAllItemsInRow = NO;
213 | if (!nextCell || nextCell.frame.origin.x < cell.frame.origin.x){
214 | if (self.pannedFromFirstColumn) {
215 | didSelectAllItemsInRow = [self didSelectAllItemsInRowWithIndexPath:indexPath];
216 | }
217 | } else if (cell.frame.origin.x == self.minimumInteritemSpacing) {
218 | if (self.pannedFromLastColumn) {
219 | didSelectAllItemsInRow = [self didSelectAllItemsInRowWithIndexPath:indexPath];
220 | }
221 | }
222 |
223 | // Check if we should cancel the row select
224 | if (self.previousIndexPath && !didSelectAllItemsInRow && labs(self.previousIndexPath.row - indexPath.row) > 1) {
225 | self.selectRowCancelled = YES;
226 | }
227 | }
228 |
229 | #pragma mark - Selection helpers
230 |
231 | - (void)selectCellAtIndexPath:(NSIndexPath *)indexPath
232 | {
233 | [self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
234 | [self.collectionView.delegate collectionView:self.collectionView didSelectItemAtIndexPath:indexPath];
235 | }
236 |
237 | - (void)deselectCellAtIndexPath:(NSIndexPath *)indexPath
238 | {
239 | [self.collectionView deselectItemAtIndexPath:indexPath animated:YES];
240 | [self.collectionView.delegate collectionView:self.collectionView didDeselectItemAtIndexPath:indexPath];
241 | }
242 |
243 | - (void)selectAllItemsFromIndexPath:(NSIndexPath *)initialIndexPath toIndexPath:(NSIndexPath *)finalIndexPath
244 | {
245 | if (initialIndexPath.section == finalIndexPath.section) {
246 | // Check if initial and final index paths should be swapped
247 | if (finalIndexPath.row < initialIndexPath.row) {
248 | // Swap them
249 | NSIndexPath *tempFinalIndex = [NSIndexPath indexPathForItem:finalIndexPath.row inSection:finalIndexPath.section];
250 | finalIndexPath = initialIndexPath;
251 | initialIndexPath = tempFinalIndex;
252 | }
253 |
254 | // Select cells
255 | NSIndexPath *indexPath = initialIndexPath;
256 | for (NSInteger i = initialIndexPath.row; i < finalIndexPath.row; i++) {
257 | UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
258 | if (!cell.isSelected) {
259 | [self selectCellAtIndexPath:indexPath];
260 | }
261 | indexPath = [NSIndexPath indexPathForItem:indexPath.row + 1 inSection:indexPath.section];
262 | }
263 | }
264 | }
265 |
266 | - (BOOL)didSelectAllItemsInRowWithIndexPath:(NSIndexPath *)indexPath
267 | {
268 | if (self.selectedRow) {
269 | // A row has been selected, so select all cells in row
270 | if (!self.selectRowCancelled) {
271 | if (self.pannedFromFirstColumn)
272 | [self selectRowFromLastColumnWithIndexPath:indexPath];
273 | else
274 | [self selectRowFromFirstColumnWithIndexPath:indexPath];
275 |
276 | return YES;
277 | }
278 | } else {
279 | self.selectedRow = YES;
280 | }
281 |
282 | return NO;
283 | }
284 |
285 | - (void)selectRowFromFirstColumnWithIndexPath:(NSIndexPath *)indexPath;
286 | {
287 | NSIndexPath *rowIndexPath = indexPath;
288 | UICollectionViewCell *cell;
289 |
290 | // Used for figuring out when we are at the last cell on the column
291 | UICollectionViewCell *nextCell;
292 | NSIndexPath *nextIndexPath;
293 |
294 | // Loop through the cells on this row (from left to right)
295 | do {
296 | rowIndexPath = [NSIndexPath indexPathForItem:rowIndexPath.row + 1 inSection:rowIndexPath.section];
297 | cell = [self.collectionView cellForItemAtIndexPath:rowIndexPath];
298 | if (!cell.isSelected) [self selectCellAtIndexPath:rowIndexPath];
299 |
300 | nextIndexPath = [NSIndexPath indexPathForItem:rowIndexPath.row + 1 inSection:rowIndexPath.section];
301 | nextCell = [self.collectionView cellForItemAtIndexPath:nextIndexPath];
302 | } while (nextCell.frame.origin.x > cell.frame.origin.x);
303 | }
304 |
305 | - (void)selectRowFromLastColumnWithIndexPath:(NSIndexPath *)indexPath
306 | {
307 | NSIndexPath *rowIndexPath = indexPath;
308 | UICollectionViewCell *cell;
309 |
310 | // Loop through the cells on this row (from right to left)
311 | do {
312 | rowIndexPath = [NSIndexPath indexPathForItem:rowIndexPath.row - 1 inSection:rowIndexPath.section];
313 | cell = [self.collectionView cellForItemAtIndexPath:rowIndexPath];
314 | if (!cell.isSelected) [self selectCellAtIndexPath:rowIndexPath];
315 | } while (cell.frame.origin.x != self.minimumInteritemSpacing); // TODO
316 | }
317 |
318 | #pragma mark - UIGestureRecognizerDelegate
319 |
320 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
321 | {
322 | return YES;
323 | }
324 |
325 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
326 | {
327 | BOOL recognizeSimultaneously = !self.isSelecting;
328 | return recognizeSimultaneously;
329 | }
330 |
331 | #pragma mark - Key-Value Observing methods
332 |
333 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
334 | {
335 | if ([keyPath isEqualToString:kSECollectionViewKeyPath]) {
336 | if (self.collectionView != nil) {
337 | [self setupCollectionView];
338 | }
339 | }
340 | }
341 |
342 | #pragma mark - Dealloc
343 |
344 | - (void)dealloc
345 | {
346 | [self removeObserver:self forKeyPath:kSECollectionViewKeyPath];
347 | }
348 |
349 | @end
350 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SEAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // SEAppDelegate.h
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2/17/14.
6 | // Copyright (c) 2014 CEWendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SEAppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SEAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // SEAppDelegate.m
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2/17/14.
6 | // Copyright (c) 2014 CEWendel. All rights reserved.
7 | //
8 |
9 | #import "SEAppDelegate.h"
10 |
11 | @implementation SEAppDelegate
12 |
13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
14 | {
15 | // Override point for customization after application launch.
16 | return YES;
17 | }
18 |
19 | - (void)applicationWillResignActive:(UIApplication *)application
20 | {
21 | // 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.
22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
23 | }
24 |
25 | - (void)applicationDidEnterBackground:(UIApplication *)application
26 | {
27 | // 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.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | - (void)applicationWillEnterForeground:(UIApplication *)application
32 | {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | - (void)applicationDidBecomeActive:(UIApplication *)application
37 | {
38 | // 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.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application
42 | {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SECollectionViewFlowLayout-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | com.CEWendel.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0
25 | LSRequiresIPhoneOS
26 |
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SECollectionViewFlowLayout-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_5_0
10 | #warning "This project uses features only available in iOS SDK 5.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | #import
15 | #import
16 | #endif
17 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SEViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SEViewController.h
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2/17/14.
6 | // Copyright (c) 2014 CEWendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "QBImagePickerController.h"
12 |
13 | @interface SEViewController : UITableViewController
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/SEViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SEViewController.m
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2/17/14.
6 | // Copyright (c) 2014 CEWendel. All rights reserved.
7 | //
8 |
9 | #import "SEViewController.h"
10 | #import
11 |
12 | @implementation SEViewController
13 |
14 | - (void)viewDidLoad
15 | {
16 | [super viewDidLoad];
17 |
18 | if (![QBImagePickerController isAccessible]) {
19 | NSLog(@"Error: Source is not accessible.");
20 | }
21 | }
22 |
23 |
24 | #pragma mark - UITableViewDelegate
25 |
26 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
27 | {
28 | QBImagePickerController *imagePickerController = [[QBImagePickerController alloc] init];
29 | imagePickerController.delegate = self;
30 | imagePickerController.allowsMultipleSelection = (indexPath.section == 1);
31 |
32 | if (indexPath.section == 1) {
33 | switch (indexPath.row) {
34 | case 1:
35 | imagePickerController.minimumNumberOfSelection = 3;
36 | break;
37 |
38 | case 2:
39 | imagePickerController.maximumNumberOfSelection = 6;
40 | break;
41 |
42 | case 3:
43 | imagePickerController.minimumNumberOfSelection = 3;
44 | imagePickerController.maximumNumberOfSelection = 6;
45 | break;
46 |
47 | default:
48 | break;
49 | }
50 | }
51 |
52 | if (indexPath.section == 0 && indexPath.row == 1) {
53 | [self.navigationController pushViewController:imagePickerController animated:YES];
54 | } else {
55 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController];
56 | [self presentViewController:navigationController animated:YES completion:NULL];
57 | }
58 | }
59 |
60 | - (void)dismissImagePickerController
61 | {
62 | if (self.presentedViewController) {
63 | [self dismissViewControllerAnimated:YES completion:NULL];
64 | } else {
65 | [self.navigationController popToViewController:self animated:YES];
66 | }
67 | }
68 |
69 |
70 | #pragma mark - QBImagePickerControllerDelegate
71 |
72 | - (void)imagePickerController:(QBImagePickerController *)imagePickerController didSelectAsset:(ALAsset *)asset
73 | {
74 | NSLog(@"*** imagePickerController:didSelectAsset:");
75 | NSLog(@"%@", asset);
76 |
77 | [self dismissImagePickerController];
78 | }
79 |
80 | - (void)imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
81 | {
82 | NSLog(@"*** imagePickerController:didSelectAssets:");
83 | NSLog(@"%@", assets);
84 |
85 | [self dismissImagePickerController];
86 | }
87 |
88 | - (void)imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController
89 | {
90 | NSLog(@"*** imagePickerControllerDidCancel:");
91 |
92 | [self dismissImagePickerController];
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/SECollectionViewFlowLayout/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SECollectionViewFlowLayout
4 | //
5 | // Created by Chris Wendel on 2/17/14.
6 | // Copyright (c) 2014 CEWendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "SEAppDelegate.h"
12 |
13 | int main(int argc, char * argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SEAppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SEQBImagePickerController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SEQBImagePickerController'
3 | s.version = '0.0.1'
4 | s.author = { 'Chris Wendel' => 'chriwend@umich.edu' }
5 | s.homepage = 'https://github.com/CEWendel/SECollectionViewFlowLayout'
6 | s.summary = 'A clone of UIImagePickerController with multiple selection support that supports swipe-to-select'
7 | s.license = 'MIT'
8 | s.source = { :git => 'https://github.com/CEWendel/SECollectionViewFlowLayout.git', :tag => '0.0.1' }
9 | s.source_files = 'SECollectionViewFlowLayout/PodFiles/*.{h,m}'
10 | s.platform = :ios
11 | s.ios.deployment_target = '6.0'
12 | s.requires_arc = true
13 | end
14 |
--------------------------------------------------------------------------------