├── .gitignore ├── CollectionViewAnimations.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Cell Animations.xcscheme │ └── Sticky Headers.xcscheme ├── CollectionViewAnimations.xcworkspace └── contents.xcworkspacedata ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj ├── SnapKit │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── Constraint.swift │ │ ├── ConstraintAttributes.swift │ │ ├── ConstraintDescription.swift │ │ ├── ConstraintItem.swift │ │ ├── ConstraintMaker.swift │ │ ├── ConstraintRelation.swift │ │ ├── Debugging.swift │ │ ├── EdgeInsets.swift │ │ ├── LayoutConstraint.swift │ │ ├── SnapKit.swift │ │ ├── View+SnapKit.swift │ │ └── ViewController+SnapKit.swift └── Target Support Files │ ├── Pods-Cell Animations │ ├── Info.plist │ ├── Pods-Cell Animations-acknowledgements.markdown │ ├── Pods-Cell Animations-acknowledgements.plist │ ├── Pods-Cell Animations-dummy.m │ ├── Pods-Cell Animations-frameworks.sh │ ├── Pods-Cell Animations-resources.sh │ ├── Pods-Cell Animations-umbrella.h │ ├── Pods-Cell Animations.debug.xcconfig │ ├── Pods-Cell Animations.modulemap │ └── Pods-Cell Animations.release.xcconfig │ ├── Pods-Sticky Headers │ ├── Info.plist │ ├── Pods-Sticky Headers-acknowledgements.markdown │ ├── Pods-Sticky Headers-acknowledgements.plist │ ├── Pods-Sticky Headers-dummy.m │ ├── Pods-Sticky Headers-frameworks.sh │ ├── Pods-Sticky Headers-resources.sh │ ├── Pods-Sticky Headers-umbrella.h │ ├── Pods-Sticky Headers.debug.xcconfig │ ├── Pods-Sticky Headers.modulemap │ └── Pods-Sticky Headers.release.xcconfig │ └── SnapKit │ ├── Info.plist │ ├── SnapKit-dummy.m │ ├── SnapKit-prefix.pch │ ├── SnapKit-umbrella.h │ ├── SnapKit.modulemap │ └── SnapKit.xcconfig ├── README.md └── Source ├── Cell Animations ├── Layout.swift └── ViewController.swift ├── Common ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Cell.swift ├── Info.plist ├── Number.swift └── UIColor+CVA.swift └── Sticky Headers ├── InvalidationContext.swift ├── Layout.swift └── ViewController.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Pods/ 30 | 31 | # Carthage 32 | # 33 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 34 | # Carthage/Checkouts 35 | 36 | Carthage/Build 37 | -------------------------------------------------------------------------------- /CollectionViewAnimations.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 21BD27A4EFCB4B4589CEE050 /* Pods_Cell_Animations.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18B0C738F122481738B46FB2 /* Pods_Cell_Animations.framework */; }; 11 | 2B6C62ACADAE1B9AF11C22A3 /* Pods_Sticky_Headers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7CA380166A1B64BC147E879 /* Pods_Sticky_Headers.framework */; }; 12 | 4C61C6F41C13C03C00205047 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C61C6F21C13C03C00205047 /* Layout.swift */; }; 13 | 4C61C6F51C13C03C00205047 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C61C6F31C13C03C00205047 /* ViewController.swift */; }; 14 | 4C61C6FC1C14BAFD00205047 /* InvalidationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C61C6F91C14BAFD00205047 /* InvalidationContext.swift */; }; 15 | 4C61C6FD1C14BAFD00205047 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C61C6FA1C14BAFD00205047 /* Layout.swift */; }; 16 | 4C61C6FE1C14BAFD00205047 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C61C6FB1C14BAFD00205047 /* ViewController.swift */; }; 17 | 4CAAD8D11C13BBFB004ADF5C /* Number.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8BA1C13BB8E004ADF5C /* Number.swift */; }; 18 | 4CAAD8D21C13BBFB004ADF5C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B51C13BB8E004ADF5C /* AppDelegate.swift */; }; 19 | 4CAAD8D41C13BBFB004ADF5C /* UIColor+CVA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8BB1C13BB8E004ADF5C /* UIColor+CVA.swift */; }; 20 | 4CAAD8D51C13BBFB004ADF5C /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B91C13BB8E004ADF5C /* Cell.swift */; }; 21 | 4CAAD8DB1C13BBFB004ADF5C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B71C13BB8E004ADF5C /* LaunchScreen.storyboard */; }; 22 | 4CAAD8DC1C13BBFB004ADF5C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B61C13BB8E004ADF5C /* Assets.xcassets */; }; 23 | 4CAAD8E71C13BC09004ADF5C /* Number.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8BA1C13BB8E004ADF5C /* Number.swift */; }; 24 | 4CAAD8E81C13BC09004ADF5C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B51C13BB8E004ADF5C /* AppDelegate.swift */; }; 25 | 4CAAD8EA1C13BC09004ADF5C /* UIColor+CVA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8BB1C13BB8E004ADF5C /* UIColor+CVA.swift */; }; 26 | 4CAAD8EB1C13BC09004ADF5C /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B91C13BB8E004ADF5C /* Cell.swift */; }; 27 | 4CAAD8F11C13BC09004ADF5C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B71C13BB8E004ADF5C /* LaunchScreen.storyboard */; }; 28 | 4CAAD8F21C13BC09004ADF5C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CAAD8B61C13BB8E004ADF5C /* Assets.xcassets */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 18B0C738F122481738B46FB2 /* Pods_Cell_Animations.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Cell_Animations.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 3D02D92C334A4346F057CF17 /* Pods-Sticky Headers with Cell Animations.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sticky Headers with Cell Animations.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sticky Headers with Cell Animations/Pods-Sticky Headers with Cell Animations.debug.xcconfig"; sourceTree = ""; }; 34 | 47B2BD7A0A542694484B4E20 /* Pods-Cell Animations.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cell Animations.release.xcconfig"; path = "Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations.release.xcconfig"; sourceTree = ""; }; 35 | 4C61C6F21C13C03C00205047 /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; 36 | 4C61C6F31C13C03C00205047 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 37 | 4C61C6F91C14BAFD00205047 /* InvalidationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvalidationContext.swift; sourceTree = ""; }; 38 | 4C61C6FA1C14BAFD00205047 /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; 39 | 4C61C6FB1C14BAFD00205047 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | 4CAAD8B51C13BB8E004ADF5C /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 4CAAD8B61C13BB8E004ADF5C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 4CAAD8B81C13BB8E004ADF5C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 43 | 4CAAD8B91C13BB8E004ADF5C /* Cell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = ""; }; 44 | 4CAAD8BA1C13BB8E004ADF5C /* Number.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Number.swift; sourceTree = ""; }; 45 | 4CAAD8BB1C13BB8E004ADF5C /* UIColor+CVA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+CVA.swift"; sourceTree = ""; }; 46 | 4CAAD8E31C13BBFB004ADF5C /* Sticky Headers.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sticky Headers.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 4CAAD8F91C13BC09004ADF5C /* Cell Animations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cell Animations.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 4CAAD8FA1C13BC5A004ADF5C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 5591CAF8B39756F2FC1CCB37 /* Pods-Cell Animations.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cell Animations.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations.debug.xcconfig"; sourceTree = ""; }; 50 | A30CAC018488481DF5AFC43B /* Pods-Sticky Headers with Cell Animations.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sticky Headers with Cell Animations.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sticky Headers with Cell Animations/Pods-Sticky Headers with Cell Animations.release.xcconfig"; sourceTree = ""; }; 51 | A7CA380166A1B64BC147E879 /* Pods_Sticky_Headers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sticky_Headers.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | BE31EDBDB05B712895C68A2E /* Pods_Sticky_Headers_with_Cell_Animations.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sticky_Headers_with_Cell_Animations.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | C8153AFA79F917E6EB3AD340 /* Pods-Sticky Headers.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sticky Headers.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers.debug.xcconfig"; sourceTree = ""; }; 54 | DF9379A0B0164F1C9C597A22 /* Pods-Sticky Headers.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sticky Headers.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers.release.xcconfig"; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 4CAAD8D81C13BBFB004ADF5C /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 2B6C62ACADAE1B9AF11C22A3 /* Pods_Sticky_Headers.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 4CAAD8EE1C13BC09004ADF5C /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | 21BD27A4EFCB4B4589CEE050 /* Pods_Cell_Animations.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 394EBE2D4B16F61C2875E2ED /* Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 18B0C738F122481738B46FB2 /* Pods_Cell_Animations.framework */, 81 | A7CA380166A1B64BC147E879 /* Pods_Sticky_Headers.framework */, 82 | BE31EDBDB05B712895C68A2E /* Pods_Sticky_Headers_with_Cell_Animations.framework */, 83 | ); 84 | name = Frameworks; 85 | sourceTree = ""; 86 | }; 87 | 4C79D3391BE3145000E4EB66 = { 88 | isa = PBXGroup; 89 | children = ( 90 | 4CAAD8B21C13BB8E004ADF5C /* Source */, 91 | 394EBE2D4B16F61C2875E2ED /* Frameworks */, 92 | F3C5EDA6F5AB089E3D40F11B /* Pods */, 93 | 4C79D3431BE3145000E4EB66 /* Products */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 4C79D3431BE3145000E4EB66 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4CAAD8E31C13BBFB004ADF5C /* Sticky Headers.app */, 101 | 4CAAD8F91C13BC09004ADF5C /* Cell Animations.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 4CAAD8B21C13BB8E004ADF5C /* Source */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 4CAAD8B41C13BB8E004ADF5C /* Common */, 110 | 4CAAD8B31C13BB8E004ADF5C /* Cell Animations */, 111 | 4CAAD8BC1C13BB8E004ADF5C /* Sticky Headers */, 112 | ); 113 | path = Source; 114 | sourceTree = ""; 115 | }; 116 | 4CAAD8B31C13BB8E004ADF5C /* Cell Animations */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 4C61C6F21C13C03C00205047 /* Layout.swift */, 120 | 4C61C6F31C13C03C00205047 /* ViewController.swift */, 121 | ); 122 | path = "Cell Animations"; 123 | sourceTree = ""; 124 | }; 125 | 4CAAD8B41C13BB8E004ADF5C /* Common */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 4CAAD8B51C13BB8E004ADF5C /* AppDelegate.swift */, 129 | 4CAAD8B91C13BB8E004ADF5C /* Cell.swift */, 130 | 4CAAD8BA1C13BB8E004ADF5C /* Number.swift */, 131 | 4CAAD8BB1C13BB8E004ADF5C /* UIColor+CVA.swift */, 132 | 4CAAD8CD1C13BBB8004ADF5C /* Support */, 133 | ); 134 | path = Common; 135 | sourceTree = ""; 136 | }; 137 | 4CAAD8BC1C13BB8E004ADF5C /* Sticky Headers */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 4C61C6F91C14BAFD00205047 /* InvalidationContext.swift */, 141 | 4C61C6FA1C14BAFD00205047 /* Layout.swift */, 142 | 4C61C6FB1C14BAFD00205047 /* ViewController.swift */, 143 | ); 144 | path = "Sticky Headers"; 145 | sourceTree = ""; 146 | }; 147 | 4CAAD8CD1C13BBB8004ADF5C /* Support */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 4CAAD8B61C13BB8E004ADF5C /* Assets.xcassets */, 151 | 4CAAD8B71C13BB8E004ADF5C /* LaunchScreen.storyboard */, 152 | 4CAAD8FA1C13BC5A004ADF5C /* Info.plist */, 153 | ); 154 | name = Support; 155 | sourceTree = ""; 156 | }; 157 | F3C5EDA6F5AB089E3D40F11B /* Pods */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 5591CAF8B39756F2FC1CCB37 /* Pods-Cell Animations.debug.xcconfig */, 161 | 47B2BD7A0A542694484B4E20 /* Pods-Cell Animations.release.xcconfig */, 162 | C8153AFA79F917E6EB3AD340 /* Pods-Sticky Headers.debug.xcconfig */, 163 | DF9379A0B0164F1C9C597A22 /* Pods-Sticky Headers.release.xcconfig */, 164 | 3D02D92C334A4346F057CF17 /* Pods-Sticky Headers with Cell Animations.debug.xcconfig */, 165 | A30CAC018488481DF5AFC43B /* Pods-Sticky Headers with Cell Animations.release.xcconfig */, 166 | ); 167 | name = Pods; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | 4CAAD8CE1C13BBFB004ADF5C /* Sticky Headers */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 4CAAD8E01C13BBFB004ADF5C /* Build configuration list for PBXNativeTarget "Sticky Headers" */; 176 | buildPhases = ( 177 | 4CAAD8CF1C13BBFB004ADF5C /* Check Pods Manifest.lock */, 178 | 4CAAD8D01C13BBFB004ADF5C /* Sources */, 179 | 4CAAD8D81C13BBFB004ADF5C /* Frameworks */, 180 | 4CAAD8DA1C13BBFB004ADF5C /* Resources */, 181 | 4CAAD8DE1C13BBFB004ADF5C /* Embed Pods Frameworks */, 182 | 4CAAD8DF1C13BBFB004ADF5C /* Copy Pods Resources */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | ); 188 | name = "Sticky Headers"; 189 | productName = CollectionViewAnimations; 190 | productReference = 4CAAD8E31C13BBFB004ADF5C /* Sticky Headers.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | 4CAAD8E41C13BC09004ADF5C /* Cell Animations */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 4CAAD8F61C13BC09004ADF5C /* Build configuration list for PBXNativeTarget "Cell Animations" */; 196 | buildPhases = ( 197 | 4CAAD8E51C13BC09004ADF5C /* Check Pods Manifest.lock */, 198 | 4CAAD8E61C13BC09004ADF5C /* Sources */, 199 | 4CAAD8EE1C13BC09004ADF5C /* Frameworks */, 200 | 4CAAD8F01C13BC09004ADF5C /* Resources */, 201 | 4CAAD8F41C13BC09004ADF5C /* Embed Pods Frameworks */, 202 | 4CAAD8F51C13BC09004ADF5C /* Copy Pods Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | ); 208 | name = "Cell Animations"; 209 | productName = CollectionViewAnimations; 210 | productReference = 4CAAD8F91C13BC09004ADF5C /* Cell Animations.app */; 211 | productType = "com.apple.product-type.application"; 212 | }; 213 | /* End PBXNativeTarget section */ 214 | 215 | /* Begin PBXProject section */ 216 | 4C79D33A1BE3145000E4EB66 /* Project object */ = { 217 | isa = PBXProject; 218 | attributes = { 219 | LastSwiftUpdateCheck = 0710; 220 | LastUpgradeCheck = 0710; 221 | ORGANIZATIONNAME = Noondev; 222 | }; 223 | buildConfigurationList = 4C79D33D1BE3145000E4EB66 /* Build configuration list for PBXProject "CollectionViewAnimations" */; 224 | compatibilityVersion = "Xcode 3.2"; 225 | developmentRegion = English; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = 4C79D3391BE3145000E4EB66; 232 | productRefGroup = 4C79D3431BE3145000E4EB66 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 4CAAD8E41C13BC09004ADF5C /* Cell Animations */, 237 | 4CAAD8CE1C13BBFB004ADF5C /* Sticky Headers */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | 4CAAD8DA1C13BBFB004ADF5C /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 4CAAD8DB1C13BBFB004ADF5C /* LaunchScreen.storyboard in Resources */, 248 | 4CAAD8DC1C13BBFB004ADF5C /* Assets.xcassets in Resources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | 4CAAD8F01C13BC09004ADF5C /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 4CAAD8F11C13BC09004ADF5C /* LaunchScreen.storyboard in Resources */, 257 | 4CAAD8F21C13BC09004ADF5C /* Assets.xcassets in Resources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXResourcesBuildPhase section */ 262 | 263 | /* Begin PBXShellScriptBuildPhase section */ 264 | 4CAAD8CF1C13BBFB004ADF5C /* Check Pods Manifest.lock */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | ); 271 | name = "Check Pods Manifest.lock"; 272 | outputPaths = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | shellPath = /bin/sh; 276 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 277 | showEnvVarsInLog = 0; 278 | }; 279 | 4CAAD8DE1C13BBFB004ADF5C /* Embed Pods Frameworks */ = { 280 | isa = PBXShellScriptBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | ); 284 | inputPaths = ( 285 | ); 286 | name = "Embed Pods Frameworks"; 287 | outputPaths = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | shellPath = /bin/sh; 291 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-frameworks.sh\"\n"; 292 | showEnvVarsInLog = 0; 293 | }; 294 | 4CAAD8DF1C13BBFB004ADF5C /* Copy Pods Resources */ = { 295 | isa = PBXShellScriptBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | inputPaths = ( 300 | ); 301 | name = "Copy Pods Resources"; 302 | outputPaths = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | shellPath = /bin/sh; 306 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-resources.sh\"\n"; 307 | showEnvVarsInLog = 0; 308 | }; 309 | 4CAAD8E51C13BC09004ADF5C /* Check Pods Manifest.lock */ = { 310 | isa = PBXShellScriptBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | ); 314 | inputPaths = ( 315 | ); 316 | name = "Check Pods Manifest.lock"; 317 | outputPaths = ( 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | shellPath = /bin/sh; 321 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 322 | showEnvVarsInLog = 0; 323 | }; 324 | 4CAAD8F41C13BC09004ADF5C /* Embed Pods Frameworks */ = { 325 | isa = PBXShellScriptBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | inputPaths = ( 330 | ); 331 | name = "Embed Pods Frameworks"; 332 | outputPaths = ( 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | shellPath = /bin/sh; 336 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-frameworks.sh\"\n"; 337 | showEnvVarsInLog = 0; 338 | }; 339 | 4CAAD8F51C13BC09004ADF5C /* Copy Pods Resources */ = { 340 | isa = PBXShellScriptBuildPhase; 341 | buildActionMask = 2147483647; 342 | files = ( 343 | ); 344 | inputPaths = ( 345 | ); 346 | name = "Copy Pods Resources"; 347 | outputPaths = ( 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | shellPath = /bin/sh; 351 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-resources.sh\"\n"; 352 | showEnvVarsInLog = 0; 353 | }; 354 | /* End PBXShellScriptBuildPhase section */ 355 | 356 | /* Begin PBXSourcesBuildPhase section */ 357 | 4CAAD8D01C13BBFB004ADF5C /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 4CAAD8D11C13BBFB004ADF5C /* Number.swift in Sources */, 362 | 4CAAD8D21C13BBFB004ADF5C /* AppDelegate.swift in Sources */, 363 | 4CAAD8D41C13BBFB004ADF5C /* UIColor+CVA.swift in Sources */, 364 | 4CAAD8D51C13BBFB004ADF5C /* Cell.swift in Sources */, 365 | 4C61C6FE1C14BAFD00205047 /* ViewController.swift in Sources */, 366 | 4C61C6FC1C14BAFD00205047 /* InvalidationContext.swift in Sources */, 367 | 4C61C6FD1C14BAFD00205047 /* Layout.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | 4CAAD8E61C13BC09004ADF5C /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 4CAAD8E71C13BC09004ADF5C /* Number.swift in Sources */, 376 | 4CAAD8E81C13BC09004ADF5C /* AppDelegate.swift in Sources */, 377 | 4C61C6F51C13C03C00205047 /* ViewController.swift in Sources */, 378 | 4CAAD8EA1C13BC09004ADF5C /* UIColor+CVA.swift in Sources */, 379 | 4CAAD8EB1C13BC09004ADF5C /* Cell.swift in Sources */, 380 | 4C61C6F41C13C03C00205047 /* Layout.swift in Sources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | /* End PBXSourcesBuildPhase section */ 385 | 386 | /* Begin PBXVariantGroup section */ 387 | 4CAAD8B71C13BB8E004ADF5C /* LaunchScreen.storyboard */ = { 388 | isa = PBXVariantGroup; 389 | children = ( 390 | 4CAAD8B81C13BB8E004ADF5C /* Base */, 391 | ); 392 | name = LaunchScreen.storyboard; 393 | sourceTree = ""; 394 | }; 395 | /* End PBXVariantGroup section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | 4C79D3521BE3145000E4EB66 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_WARN_BOOL_CONVERSION = YES; 407 | CLANG_WARN_CONSTANT_CONVERSION = YES; 408 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 409 | CLANG_WARN_EMPTY_BODY = YES; 410 | CLANG_WARN_ENUM_CONVERSION = YES; 411 | CLANG_WARN_INT_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 439 | }; 440 | name = Debug; 441 | }; 442 | 4C79D3531BE3145000E4EB66 /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 447 | CLANG_CXX_LIBRARY = "libc++"; 448 | CLANG_ENABLE_MODULES = YES; 449 | CLANG_ENABLE_OBJC_ARC = YES; 450 | CLANG_WARN_BOOL_CONVERSION = YES; 451 | CLANG_WARN_CONSTANT_CONVERSION = YES; 452 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INT_CONVERSION = YES; 456 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 457 | CLANG_WARN_UNREACHABLE_CODE = YES; 458 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 459 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 460 | COPY_PHASE_STRIP = NO; 461 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 462 | ENABLE_NS_ASSERTIONS = NO; 463 | ENABLE_STRICT_OBJC_MSGSEND = YES; 464 | GCC_C_LANGUAGE_STANDARD = gnu99; 465 | GCC_NO_COMMON_BLOCKS = YES; 466 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 467 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 468 | GCC_WARN_UNDECLARED_SELECTOR = YES; 469 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 470 | GCC_WARN_UNUSED_FUNCTION = YES; 471 | GCC_WARN_UNUSED_VARIABLE = YES; 472 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 473 | MTL_ENABLE_DEBUG_INFO = NO; 474 | SDKROOT = iphoneos; 475 | VALIDATE_PRODUCT = YES; 476 | }; 477 | name = Release; 478 | }; 479 | 4CAAD8E11C13BBFB004ADF5C /* Debug */ = { 480 | isa = XCBuildConfiguration; 481 | baseConfigurationReference = C8153AFA79F917E6EB3AD340 /* Pods-Sticky Headers.debug.xcconfig */; 482 | buildSettings = { 483 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 484 | INFOPLIST_FILE = Source/Common/Info.plist; 485 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 486 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 487 | MACOSX_DEPLOYMENT_TARGET = 10.9; 488 | PRODUCT_BUNDLE_IDENTIFIER = com.noondev.StickyHeaders; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | TARGETED_DEVICE_FAMILY = "1,2"; 491 | TVOS_DEPLOYMENT_TARGET = 9.0; 492 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 493 | }; 494 | name = Debug; 495 | }; 496 | 4CAAD8E21C13BBFB004ADF5C /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | baseConfigurationReference = DF9379A0B0164F1C9C597A22 /* Pods-Sticky Headers.release.xcconfig */; 499 | buildSettings = { 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | INFOPLIST_FILE = Source/Common/Info.plist; 502 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 504 | MACOSX_DEPLOYMENT_TARGET = 10.9; 505 | PRODUCT_BUNDLE_IDENTIFIER = com.noondev.StickyHeaders; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | TARGETED_DEVICE_FAMILY = "1,2"; 508 | TVOS_DEPLOYMENT_TARGET = 9.0; 509 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 510 | }; 511 | name = Release; 512 | }; 513 | 4CAAD8F71C13BC09004ADF5C /* Debug */ = { 514 | isa = XCBuildConfiguration; 515 | baseConfigurationReference = 5591CAF8B39756F2FC1CCB37 /* Pods-Cell Animations.debug.xcconfig */; 516 | buildSettings = { 517 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 518 | INFOPLIST_FILE = Source/Common/Info.plist; 519 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 521 | MACOSX_DEPLOYMENT_TARGET = 10.9; 522 | PRODUCT_BUNDLE_IDENTIFIER = com.noondev.CellAnimations; 523 | PRODUCT_NAME = "$(TARGET_NAME)"; 524 | TARGETED_DEVICE_FAMILY = "1,2"; 525 | TVOS_DEPLOYMENT_TARGET = 9.0; 526 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 527 | }; 528 | name = Debug; 529 | }; 530 | 4CAAD8F81C13BC09004ADF5C /* Release */ = { 531 | isa = XCBuildConfiguration; 532 | baseConfigurationReference = 47B2BD7A0A542694484B4E20 /* Pods-Cell Animations.release.xcconfig */; 533 | buildSettings = { 534 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 535 | INFOPLIST_FILE = Source/Common/Info.plist; 536 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 537 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 538 | MACOSX_DEPLOYMENT_TARGET = 10.9; 539 | PRODUCT_BUNDLE_IDENTIFIER = com.noondev.CellAnimations; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | TARGETED_DEVICE_FAMILY = "1,2"; 542 | TVOS_DEPLOYMENT_TARGET = 9.0; 543 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 544 | }; 545 | name = Release; 546 | }; 547 | /* End XCBuildConfiguration section */ 548 | 549 | /* Begin XCConfigurationList section */ 550 | 4C79D33D1BE3145000E4EB66 /* Build configuration list for PBXProject "CollectionViewAnimations" */ = { 551 | isa = XCConfigurationList; 552 | buildConfigurations = ( 553 | 4C79D3521BE3145000E4EB66 /* Debug */, 554 | 4C79D3531BE3145000E4EB66 /* Release */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 4CAAD8E01C13BBFB004ADF5C /* Build configuration list for PBXNativeTarget "Sticky Headers" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 4CAAD8E11C13BBFB004ADF5C /* Debug */, 563 | 4CAAD8E21C13BBFB004ADF5C /* Release */, 564 | ); 565 | defaultConfigurationIsVisible = 0; 566 | defaultConfigurationName = Release; 567 | }; 568 | 4CAAD8F61C13BC09004ADF5C /* Build configuration list for PBXNativeTarget "Cell Animations" */ = { 569 | isa = XCConfigurationList; 570 | buildConfigurations = ( 571 | 4CAAD8F71C13BC09004ADF5C /* Debug */, 572 | 4CAAD8F81C13BC09004ADF5C /* Release */, 573 | ); 574 | defaultConfigurationIsVisible = 0; 575 | defaultConfigurationName = Release; 576 | }; 577 | /* End XCConfigurationList section */ 578 | }; 579 | rootObject = 4C79D33A1BE3145000E4EB66 /* Project object */; 580 | } 581 | -------------------------------------------------------------------------------- /CollectionViewAnimations.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CollectionViewAnimations.xcodeproj/xcshareddata/xcschemes/Cell Animations.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CollectionViewAnimations.xcodeproj/xcshareddata/xcschemes/Sticky Headers.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CollectionViewAnimations.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Christian Noon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.4' 2 | use_frameworks! 3 | 4 | target 'Cell Animations' do 5 | pod 'SnapKit', '~> 0.18' 6 | end 7 | 8 | target 'Sticky Headers' do 9 | pod 'SnapKit', '~> 0.18' 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SnapKit (0.18.0) 3 | 4 | DEPENDENCIES: 5 | - SnapKit (~> 0.18) 6 | 7 | SPEC CHECKSUMS: 8 | SnapKit: e1f090a6d47b1dd4bd89b73f72b6746a7835ef3c 9 | 10 | COCOAPODS: 0.39.0 11 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SnapKit (0.18.0) 3 | 4 | DEPENDENCIES: 5 | - SnapKit (~> 0.18) 6 | 7 | SPEC CHECKSUMS: 8 | SnapKit: e1f090a6d47b1dd4bd89b73f72b6746a7835ef3c 9 | 10 | COCOAPODS: 0.39.0 11 | -------------------------------------------------------------------------------- /Pods/SnapKit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 | -------------------------------------------------------------------------------- /Pods/SnapKit/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | SnapKit is a DSL to make Auto Layout easy on both iOS and OS X. 4 | 5 | [![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit) 6 | [![Cocoapods Compatible](https://img.shields.io/cocoapods/v/SnapKit.svg)](https://img.shields.io/cocoapods/v/SnapKit.svg) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | 9 | ```swift 10 | import SnapKit 11 | 12 | class MyViewController: UIViewController { 13 | 14 | lazy var box = UIView() 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | self.view.addSubview(box) 20 | box.snp_makeConstraints { (make) -> Void in 21 | make.width.height.equalTo(50) 22 | make.center.equalTo(self.view) 23 | } 24 | } 25 | 26 | } 27 | ``` 28 | 29 | ## Resources 30 | 31 | * [Documentation](http://snapkit.io/docs/) 32 | * [F.A.Q.](http://snapkit.io/faq/) 33 | * [Legacy Platforms (iOS 7.0, OS X 10.9)](http://snapkit.io/legacy-platforms/) 34 | 35 | ## License 36 | 37 | MIT license. See the `LICENSE` file for details. 38 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/Constraint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to expose API's for a Constraint 32 | */ 33 | public class Constraint { 34 | 35 | public func install() -> [LayoutConstraint] { fatalError("Must be implemented by Concrete subclass.") } 36 | public func uninstall() -> Void { fatalError("Must be implemented by Concrete subclass.") } 37 | public func activate() -> Void { fatalError("Must be implemented by Concrete subclass.") } 38 | public func deactivate() -> Void { fatalError("Must be implemented by Concrete subclass.") } 39 | 40 | public func updateOffset(amount: Float) -> Void { fatalError("Must be implemented by Concrete subclass.") } 41 | public func updateOffset(amount: Double) -> Void { fatalError("Must be implemented by Concrete subclass.") } 42 | public func updateOffset(amount: CGFloat) -> Void { fatalError("Must be implemented by Concrete subclass.") } 43 | public func updateOffset(amount: Int) -> Void { fatalError("Must be implemented by Concrete subclass.") } 44 | public func updateOffset(amount: UInt) -> Void { fatalError("Must be implemented by Concrete subclass.") } 45 | public func updateOffset(amount: CGPoint) -> Void { fatalError("Must be implemented by Concrete subclass.") } 46 | public func updateOffset(amount: CGSize) -> Void { fatalError("Must be implemented by Concrete subclass.") } 47 | public func updateOffset(amount: EdgeInsets) -> Void { fatalError("Must be implemented by Concrete subclass.") } 48 | 49 | public func updateInsets(amount: EdgeInsets) -> Void { fatalError("Must be implemented by Concrete subclass.") } 50 | 51 | public func updatePriority(priority: Float) -> Void { fatalError("Must be implemented by Concrete subclass.") } 52 | public func updatePriority(priority: Double) -> Void { fatalError("Must be implemented by Concrete subclass.") } 53 | public func updatePriority(priority: CGFloat) -> Void { fatalError("Must be implemented by Concrete subclass.") } 54 | public func updatePriority(priority: UInt) -> Void { fatalError("Must be implemented by Concrete subclass.") } 55 | public func updatePriority(priority: Int) -> Void { fatalError("Must be implemented by Concrete subclass.") } 56 | public func updatePriorityRequired() -> Void { fatalError("Must be implemented by Concrete subclass.") } 57 | public func updatePriorityHigh() -> Void { fatalError("Must be implemented by Concrete subclass.") } 58 | public func updatePriorityMedium() -> Void { fatalError("Must be implemented by Concrete subclass.") } 59 | public func updatePriorityLow() -> Void { fatalError("Must be implemented by Concrete subclass.") } 60 | 61 | internal var makerFile: String = "Unknown" 62 | internal var makerLine: UInt = 0 63 | 64 | } 65 | 66 | /** 67 | Used internally to implement a ConcreteConstraint 68 | */ 69 | internal class ConcreteConstraint: Constraint { 70 | 71 | internal override func updateOffset(amount: Float) -> Void { 72 | self.constant = amount 73 | } 74 | internal override func updateOffset(amount: Double) -> Void { 75 | self.updateOffset(Float(amount)) 76 | } 77 | internal override func updateOffset(amount: CGFloat) -> Void { 78 | self.updateOffset(Float(amount)) 79 | } 80 | internal override func updateOffset(amount: Int) -> Void { 81 | self.updateOffset(Float(amount)) 82 | } 83 | internal override func updateOffset(amount: UInt) -> Void { 84 | self.updateOffset(Float(amount)) 85 | } 86 | internal override func updateOffset(amount: CGPoint) -> Void { 87 | self.constant = amount 88 | } 89 | internal override func updateOffset(amount: CGSize) -> Void { 90 | self.constant = amount 91 | } 92 | internal override func updateOffset(amount: EdgeInsets) -> Void { 93 | self.constant = amount 94 | } 95 | 96 | internal override func updateInsets(amount: EdgeInsets) -> Void { 97 | self.constant = EdgeInsets(top: amount.top, left: amount.left, bottom: -amount.bottom, right: -amount.right) 98 | } 99 | 100 | internal override func updatePriority(priority: Float) -> Void { 101 | self.priority = priority 102 | } 103 | internal override func updatePriority(priority: Double) -> Void { 104 | self.updatePriority(Float(priority)) 105 | } 106 | internal override func updatePriority(priority: CGFloat) -> Void { 107 | self.updatePriority(Float(priority)) 108 | } 109 | internal override func updatePriority(priority: UInt) -> Void { 110 | self.updatePriority(Float(priority)) 111 | } 112 | internal override func updatePriority(priority: Int) -> Void { 113 | self.updatePriority(Float(priority)) 114 | } 115 | internal override func updatePriorityRequired() -> Void { 116 | self.updatePriority(Float(1000.0)) 117 | } 118 | internal override func updatePriorityHigh() -> Void { 119 | self.updatePriority(Float(750.0)) 120 | } 121 | internal override func updatePriorityMedium() -> Void { 122 | #if os(iOS) || os(tvOS) 123 | self.updatePriority(Float(500.0)) 124 | #else 125 | self.updatePriority(Float(501.0)) 126 | #endif 127 | } 128 | internal override func updatePriorityLow() -> Void { 129 | self.updatePriority(Float(250.0)) 130 | } 131 | 132 | internal override func install() -> [LayoutConstraint] { 133 | return self.installOnView(updateExisting: false, file: self.makerFile, line: self.makerLine) 134 | } 135 | 136 | internal override func uninstall() -> Void { 137 | self.uninstallFromView() 138 | } 139 | 140 | internal override func activate() -> Void { 141 | guard self.installInfo != nil else { 142 | self.install() 143 | return 144 | } 145 | #if SNAPKIT_DEPLOYMENT_LEGACY 146 | guard #available(iOS 8.0, OSX 10.10, *) else { 147 | self.install() 148 | return 149 | } 150 | #endif 151 | let layoutConstraints = self.installInfo!.layoutConstraints.allObjects as! [LayoutConstraint] 152 | if layoutConstraints.count > 0 { 153 | NSLayoutConstraint.activateConstraints(layoutConstraints) 154 | } 155 | } 156 | 157 | internal override func deactivate() -> Void { 158 | guard self.installInfo != nil else { 159 | return 160 | } 161 | #if SNAPKIT_DEPLOYMENT_LEGACY 162 | guard #available(iOS 8.0, OSX 10.10, *) else { 163 | return 164 | } 165 | #endif 166 | let layoutConstraints = self.installInfo!.layoutConstraints.allObjects as! [LayoutConstraint] 167 | if layoutConstraints.count > 0 { 168 | NSLayoutConstraint.deactivateConstraints(layoutConstraints) 169 | } 170 | } 171 | 172 | private let fromItem: ConstraintItem 173 | private let toItem: ConstraintItem 174 | private let relation: ConstraintRelation 175 | private let multiplier: Float 176 | private var constant: Any { 177 | didSet { 178 | if let installInfo = self.installInfo { 179 | for layoutConstraint in installInfo.layoutConstraints.allObjects as! [LayoutConstraint] { 180 | let attribute = (layoutConstraint.secondAttribute == .NotAnAttribute) ? layoutConstraint.firstAttribute : layoutConstraint.secondAttribute 181 | layoutConstraint.constant = attribute.snp_constantForValue(self.constant) 182 | } 183 | } 184 | } 185 | } 186 | private var priority: Float { 187 | didSet { 188 | if let installInfo = self.installInfo { 189 | for layoutConstraint in installInfo.layoutConstraints.allObjects as! [LayoutConstraint] { 190 | layoutConstraint.priority = self.priority 191 | } 192 | } 193 | } 194 | } 195 | 196 | private var installInfo: ConcreteConstraintInstallInfo? = nil 197 | 198 | internal init(fromItem: ConstraintItem, toItem: ConstraintItem, relation: ConstraintRelation, constant: Any, multiplier: Float, priority: Float) { 199 | self.fromItem = fromItem 200 | self.toItem = toItem 201 | self.relation = relation 202 | self.constant = constant 203 | self.multiplier = multiplier 204 | self.priority = priority 205 | } 206 | 207 | internal func installOnView(updateExisting updateExisting: Bool = false, file: String? = nil, line: UInt? = nil) -> [LayoutConstraint] { 208 | var installOnView: View? = nil 209 | if self.toItem.view != nil { 210 | installOnView = closestCommonSuperviewFromView(self.fromItem.view, toView: self.toItem.view) 211 | if installOnView == nil { 212 | NSException(name: "Cannot Install Constraint", reason: "No common superview between views (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise() 213 | return [] 214 | } 215 | } else { 216 | 217 | if self.fromItem.attributes.isSubsetOf(ConstraintAttributes.Width.union(.Height)) { 218 | installOnView = self.fromItem.view 219 | } else { 220 | installOnView = self.fromItem.view?.superview 221 | if installOnView == nil { 222 | NSException(name: "Cannot Install Constraint", reason: "Missing superview (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise() 223 | return [] 224 | } 225 | } 226 | } 227 | 228 | if let installedOnView = self.installInfo?.view { 229 | if installedOnView != installOnView { 230 | NSException(name: "Cannot Install Constraint", reason: "Already installed on different view. (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise() 231 | return [] 232 | } 233 | return self.installInfo?.layoutConstraints.allObjects as? [LayoutConstraint] ?? [] 234 | } 235 | 236 | var newLayoutConstraints = [LayoutConstraint]() 237 | let layoutFromAttributes = self.fromItem.attributes.layoutAttributes 238 | let layoutToAttributes = self.toItem.attributes.layoutAttributes 239 | 240 | // get layout from 241 | let layoutFrom: View? = self.fromItem.view 242 | 243 | // get layout relation 244 | let layoutRelation: NSLayoutRelation = self.relation.layoutRelation 245 | 246 | for layoutFromAttribute in layoutFromAttributes { 247 | // get layout to attribute 248 | let layoutToAttribute = (layoutToAttributes.count > 0) ? layoutToAttributes[0] : layoutFromAttribute 249 | 250 | // get layout constant 251 | let layoutConstant: CGFloat = layoutToAttribute.snp_constantForValue(self.constant) 252 | 253 | // get layout to 254 | #if os(iOS) || os(tvOS) 255 | var layoutTo: AnyObject? = self.toItem.view ?? self.toItem.layoutSupport 256 | #else 257 | var layoutTo: AnyObject? = self.toItem.view 258 | #endif 259 | if layoutTo == nil && layoutToAttribute != .Width && layoutToAttribute != .Height { 260 | layoutTo = installOnView 261 | } 262 | 263 | // create layout constraint 264 | let layoutConstraint = LayoutConstraint( 265 | item: layoutFrom!, 266 | attribute: layoutFromAttribute, 267 | relatedBy: layoutRelation, 268 | toItem: layoutTo, 269 | attribute: layoutToAttribute, 270 | multiplier: CGFloat(self.multiplier), 271 | constant: layoutConstant) 272 | 273 | // set priority 274 | layoutConstraint.priority = self.priority 275 | 276 | // set constraint 277 | layoutConstraint.snp_constraint = self 278 | 279 | newLayoutConstraints.append(layoutConstraint) 280 | } 281 | 282 | // special logic for updating 283 | if updateExisting { 284 | // get existing constraints for this view 285 | let existingLayoutConstraints = layoutFrom!.snp_installedLayoutConstraints.reverse() 286 | 287 | // array that will contain only new layout constraints to keep 288 | var newLayoutConstraintsToKeep = [LayoutConstraint]() 289 | 290 | // begin looping 291 | for layoutConstraint in newLayoutConstraints { 292 | // layout constraint that should be updated 293 | var updateLayoutConstraint: LayoutConstraint? = nil 294 | 295 | // loop through existing and check for match 296 | for existingLayoutConstraint in existingLayoutConstraints { 297 | if existingLayoutConstraint == layoutConstraint { 298 | updateLayoutConstraint = existingLayoutConstraint 299 | break 300 | } 301 | } 302 | 303 | // if we have existing one lets just update the constant 304 | if updateLayoutConstraint != nil { 305 | updateLayoutConstraint!.constant = layoutConstraint.constant 306 | } 307 | // otherwise add this layout constraint to new keep list 308 | else { 309 | newLayoutConstraintsToKeep.append(layoutConstraint) 310 | } 311 | } 312 | 313 | // set constraints to only new ones 314 | newLayoutConstraints = newLayoutConstraintsToKeep 315 | } 316 | 317 | // add constraints 318 | #if SNAPKIT_DEPLOYMENT_LEGACY && !os(OSX) 319 | if #available(iOS 8.0, *) { 320 | NSLayoutConstraint.activateConstraints(newLayoutConstraints) 321 | } else { 322 | installOnView!.addConstraints(newLayoutConstraints) 323 | } 324 | #else 325 | NSLayoutConstraint.activateConstraints(newLayoutConstraints) 326 | #endif 327 | 328 | // set install info 329 | self.installInfo = ConcreteConstraintInstallInfo(view: installOnView, layoutConstraints: NSHashTable.weakObjectsHashTable()) 330 | 331 | // store which layout constraints are installed for this constraint 332 | for layoutConstraint in newLayoutConstraints { 333 | self.installInfo!.layoutConstraints.addObject(layoutConstraint) 334 | } 335 | 336 | // store the layout constraints against the layout from view 337 | layoutFrom!.snp_installedLayoutConstraints += newLayoutConstraints 338 | 339 | // return the new constraints 340 | return newLayoutConstraints 341 | } 342 | 343 | internal func uninstallFromView() { 344 | if let installInfo = self.installInfo, 345 | let installedLayoutConstraints = installInfo.layoutConstraints.allObjects as? [LayoutConstraint] { 346 | 347 | if installedLayoutConstraints.count > 0 { 348 | // remove the constraints from the UIView's storage 349 | #if SNAPKIT_DEPLOYMENT_LEGACY && !os(OSX) 350 | if #available(iOS 8.0, *) { 351 | NSLayoutConstraint.deactivateConstraints(installedLayoutConstraints) 352 | } else if let installedOnView = installInfo.view { 353 | installedOnView.removeConstraints(installedLayoutConstraints) 354 | } 355 | #else 356 | NSLayoutConstraint.deactivateConstraints(installedLayoutConstraints) 357 | #endif 358 | 359 | // remove the constraints from the from item view 360 | if let fromView = self.fromItem.view { 361 | fromView.snp_installedLayoutConstraints = fromView.snp_installedLayoutConstraints.filter { 362 | return !installedLayoutConstraints.contains($0) 363 | } 364 | } 365 | } 366 | 367 | } 368 | self.installInfo = nil 369 | } 370 | 371 | } 372 | 373 | private struct ConcreteConstraintInstallInfo { 374 | 375 | weak var view: View? = nil 376 | let layoutConstraints: NSHashTable 377 | 378 | } 379 | 380 | private extension NSLayoutAttribute { 381 | 382 | private func snp_constantForValue(value: Any?) -> CGFloat { 383 | // Float 384 | if let float = value as? Float { 385 | return CGFloat(float) 386 | } 387 | // Double 388 | else if let double = value as? Double { 389 | return CGFloat(double) 390 | } 391 | // UInt 392 | else if let int = value as? Int { 393 | return CGFloat(int) 394 | } 395 | // Int 396 | else if let uint = value as? UInt { 397 | return CGFloat(uint) 398 | } 399 | // CGFloat 400 | else if let float = value as? CGFloat { 401 | return float 402 | } 403 | // CGSize 404 | else if let size = value as? CGSize { 405 | if self == .Width { 406 | return size.width 407 | } else if self == .Height { 408 | return size.height 409 | } 410 | } 411 | // CGPoint 412 | else if let point = value as? CGPoint { 413 | #if os(iOS) || os(tvOS) 414 | switch self { 415 | case .Left, .CenterX, .LeftMargin, .CenterXWithinMargins: return point.x 416 | case .Top, .CenterY, .TopMargin, .CenterYWithinMargins, .Baseline, .FirstBaseline: return point.y 417 | case .Right, .RightMargin: return point.x 418 | case .Bottom, .BottomMargin: return point.y 419 | case .Leading, .LeadingMargin: return point.x 420 | case .Trailing, .TrailingMargin: return point.x 421 | case .Width, .Height, .NotAnAttribute: return CGFloat(0) 422 | } 423 | #else 424 | switch self { 425 | case .Left, .CenterX: return point.x 426 | case .Top, .CenterY, .Baseline: return point.y 427 | case .Right: return point.x 428 | case .Bottom: return point.y 429 | case .Leading: return point.x 430 | case .Trailing: return point.x 431 | case .Width, .Height, .NotAnAttribute: return CGFloat(0) 432 | case .FirstBaseline: return point.y 433 | } 434 | #endif 435 | } 436 | // EdgeInsets 437 | else if let insets = value as? EdgeInsets { 438 | #if os(iOS) || os(tvOS) 439 | switch self { 440 | case .Left, .CenterX, .LeftMargin, .CenterXWithinMargins: return insets.left 441 | case .Top, .CenterY, .TopMargin, .CenterYWithinMargins, .Baseline, .FirstBaseline: return insets.top 442 | case .Right, .RightMargin: return insets.right 443 | case .Bottom, .BottomMargin: return insets.bottom 444 | case .Leading, .LeadingMargin: return (Config.interfaceLayoutDirection == .LeftToRight) ? insets.left : -insets.right 445 | case .Trailing, .TrailingMargin: return (Config.interfaceLayoutDirection == .LeftToRight) ? insets.right : -insets.left 446 | case .Width, .Height, .NotAnAttribute: return CGFloat(0) 447 | } 448 | #else 449 | switch self { 450 | case .Left, .CenterX: return insets.left 451 | case .Top, .CenterY, .Baseline: return insets.top 452 | case .Right: return insets.right 453 | case .Bottom: return insets.bottom 454 | case .Leading: return (Config.interfaceLayoutDirection == .LeftToRight) ? insets.left : -insets.right 455 | case .Trailing: return (Config.interfaceLayoutDirection == .LeftToRight) ? insets.right : -insets.left 456 | case .Width, .Height, .NotAnAttribute: return CGFloat(0) 457 | case .FirstBaseline: return insets.bottom 458 | } 459 | #endif 460 | } 461 | 462 | return CGFloat(0); 463 | } 464 | } 465 | 466 | private func closestCommonSuperviewFromView(fromView: View?, toView: View?) -> View? { 467 | var views = Set() 468 | var fromView = fromView 469 | var toView = toView 470 | repeat { 471 | if let view = toView { 472 | if views.contains(view) { 473 | return view 474 | } 475 | views.insert(view) 476 | toView = view.superview 477 | } 478 | if let view = fromView { 479 | if views.contains(view) { 480 | return view 481 | } 482 | views.insert(view) 483 | fromView = view.superview 484 | } 485 | } while (fromView != nil || toView != nil) 486 | 487 | return nil 488 | } 489 | 490 | private func ==(left: ConcreteConstraint, right: ConcreteConstraint) -> Bool { 491 | return (left.fromItem == right.fromItem && 492 | left.toItem == right.toItem && 493 | left.relation == right.relation && 494 | left.multiplier == right.multiplier && 495 | left.priority == right.priority) 496 | } 497 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/ConstraintAttributes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to define `NSLayoutAttributes` in a more concise and composite manner 32 | */ 33 | internal struct ConstraintAttributes: OptionSetType, BooleanType { 34 | 35 | internal init(rawValue: UInt) { 36 | self.rawValue = rawValue 37 | } 38 | internal init(_ rawValue: UInt) { 39 | self.init(rawValue: rawValue) 40 | } 41 | internal init(nilLiteral: ()) { 42 | self.rawValue = 0 43 | } 44 | 45 | internal private(set) var rawValue: UInt 46 | internal static var allZeros: ConstraintAttributes { return self.init(0) } 47 | internal static func convertFromNilLiteral() -> ConstraintAttributes { return self.init(0) } 48 | internal var boolValue: Bool { return self.rawValue != 0 } 49 | 50 | internal func toRaw() -> UInt { return self.rawValue } 51 | internal static func fromRaw(raw: UInt) -> ConstraintAttributes? { return self.init(raw) } 52 | internal static func fromMask(raw: UInt) -> ConstraintAttributes { return self.init(raw) } 53 | 54 | // normal 55 | 56 | internal static var None: ConstraintAttributes { return self.init(0) } 57 | internal static var Left: ConstraintAttributes { return self.init(1) } 58 | internal static var Top: ConstraintAttributes { return self.init(2) } 59 | internal static var Right: ConstraintAttributes { return self.init(4) } 60 | internal static var Bottom: ConstraintAttributes { return self.init(8) } 61 | internal static var Leading: ConstraintAttributes { return self.init(16) } 62 | internal static var Trailing: ConstraintAttributes { return self.init(32) } 63 | internal static var Width: ConstraintAttributes { return self.init(64) } 64 | internal static var Height: ConstraintAttributes { return self.init(128) } 65 | internal static var CenterX: ConstraintAttributes { return self.init(256) } 66 | internal static var CenterY: ConstraintAttributes { return self.init(512) } 67 | internal static var Baseline: ConstraintAttributes { return self.init(1024) } 68 | 69 | @available(iOS 8.0, *) 70 | internal static var FirstBaseline: ConstraintAttributes { return self.init(2048) } 71 | @available(iOS 8.0, *) 72 | internal static var LeftMargin: ConstraintAttributes { return self.init(4096) } 73 | @available(iOS 8.0, *) 74 | internal static var RightMargin: ConstraintAttributes { return self.init(8192) } 75 | @available(iOS 8.0, *) 76 | internal static var TopMargin: ConstraintAttributes { return self.init(16384) } 77 | @available(iOS 8.0, *) 78 | internal static var BottomMargin: ConstraintAttributes { return self.init(32768) } 79 | @available(iOS 8.0, *) 80 | internal static var LeadingMargin: ConstraintAttributes { return self.init(65536) } 81 | @available(iOS 8.0, *) 82 | internal static var TrailingMargin: ConstraintAttributes { return self.init(131072) } 83 | @available(iOS 8.0, *) 84 | internal static var CenterXWithinMargins: ConstraintAttributes { return self.init(262144) } 85 | @available(iOS 8.0, *) 86 | internal static var CenterYWithinMargins: ConstraintAttributes { return self.init(524288) } 87 | 88 | // aggregates 89 | 90 | internal static var Edges: ConstraintAttributes { return self.init(15) } 91 | internal static var Size: ConstraintAttributes { return self.init(192) } 92 | internal static var Center: ConstraintAttributes { return self.init(768) } 93 | 94 | @available(iOS 8.0, *) 95 | internal static var Margins: ConstraintAttributes { return self.init(61440) } 96 | 97 | @available(iOS 8.0, *) 98 | internal static var CenterWithinMargins: ConstraintAttributes { return self.init(786432) } 99 | 100 | internal var layoutAttributes:[NSLayoutAttribute] { 101 | var attrs = [NSLayoutAttribute]() 102 | if (self.contains(ConstraintAttributes.Left)) { 103 | attrs.append(.Left) 104 | } 105 | if (self.contains(ConstraintAttributes.Top)) { 106 | attrs.append(.Top) 107 | } 108 | if (self.contains(ConstraintAttributes.Right)) { 109 | attrs.append(.Right) 110 | } 111 | if (self.contains(ConstraintAttributes.Bottom)) { 112 | attrs.append(.Bottom) 113 | } 114 | if (self.contains(ConstraintAttributes.Leading)) { 115 | attrs.append(.Leading) 116 | } 117 | if (self.contains(ConstraintAttributes.Trailing)) { 118 | attrs.append(.Trailing) 119 | } 120 | if (self.contains(ConstraintAttributes.Width)) { 121 | attrs.append(.Width) 122 | } 123 | if (self.contains(ConstraintAttributes.Height)) { 124 | attrs.append(.Height) 125 | } 126 | if (self.contains(ConstraintAttributes.CenterX)) { 127 | attrs.append(.CenterX) 128 | } 129 | if (self.contains(ConstraintAttributes.CenterY)) { 130 | attrs.append(.CenterY) 131 | } 132 | if (self.contains(ConstraintAttributes.Baseline)) { 133 | attrs.append(.Baseline) 134 | } 135 | 136 | #if os(iOS) || os(tvOS) 137 | #if SNAPKIT_DEPLOYMENT_LEGACY 138 | guard #available(iOS 8.0, *) else { 139 | return attrs 140 | } 141 | #endif 142 | if (self.contains(ConstraintAttributes.FirstBaseline)) { 143 | attrs.append(.FirstBaseline) 144 | } 145 | if (self.contains(ConstraintAttributes.LeftMargin)) { 146 | attrs.append(.LeftMargin) 147 | } 148 | if (self.contains(ConstraintAttributes.RightMargin)) { 149 | attrs.append(.RightMargin) 150 | } 151 | if (self.contains(ConstraintAttributes.TopMargin)) { 152 | attrs.append(.TopMargin) 153 | } 154 | if (self.contains(ConstraintAttributes.BottomMargin)) { 155 | attrs.append(.BottomMargin) 156 | } 157 | if (self.contains(ConstraintAttributes.LeadingMargin)) { 158 | attrs.append(.LeadingMargin) 159 | } 160 | if (self.contains(ConstraintAttributes.TrailingMargin)) { 161 | attrs.append(.TrailingMargin) 162 | } 163 | if (self.contains(ConstraintAttributes.CenterXWithinMargins)) { 164 | attrs.append(.CenterXWithinMargins) 165 | } 166 | if (self.contains(ConstraintAttributes.CenterYWithinMargins)) { 167 | attrs.append(.CenterYWithinMargins) 168 | } 169 | #endif 170 | 171 | return attrs 172 | } 173 | } 174 | internal func +=(inout left: ConstraintAttributes, right: ConstraintAttributes) { 175 | left.unionInPlace(right) 176 | } 177 | internal func -=(inout left: ConstraintAttributes, right: ConstraintAttributes) { 178 | left.subtractInPlace(right) 179 | } 180 | internal func ==(left: ConstraintAttributes, right: ConstraintAttributes) -> Bool { 181 | return left.rawValue == right.rawValue 182 | } 183 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/ConstraintItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to assist in building a constraint 32 | */ 33 | public class ConstraintItem { 34 | 35 | internal init(object: AnyObject?, attributes: ConstraintAttributes) { 36 | self.object = object 37 | self.attributes = attributes 38 | } 39 | 40 | internal weak var object: AnyObject? 41 | internal var attributes: ConstraintAttributes 42 | 43 | internal var view: View? { 44 | return self.object as? View 45 | } 46 | 47 | @available(iOS 7.0, *) 48 | internal var layoutSupport: LayoutSupport? { 49 | return self.object as? LayoutSupport 50 | } 51 | } 52 | 53 | 54 | internal func ==(left: ConstraintItem, right: ConstraintItem) -> Bool { 55 | if left.object == nil { 56 | return false 57 | } 58 | if right.object == nil { 59 | return false 60 | } 61 | if left.object !== right.object { 62 | return false 63 | } 64 | if left.attributes != right.attributes { 65 | return false 66 | } 67 | return true 68 | } -------------------------------------------------------------------------------- /Pods/SnapKit/Source/ConstraintMaker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to make constraints 32 | */ 33 | public class ConstraintMaker { 34 | 35 | /// left edge 36 | public var left: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Left) } 37 | 38 | /// top edge 39 | public var top: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Top) } 40 | 41 | /// right edge 42 | public var right: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Right) } 43 | 44 | /// bottom edge 45 | public var bottom: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Bottom) } 46 | 47 | /// leading edge 48 | public var leading: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Leading) } 49 | 50 | /// trailing edge 51 | public var trailing: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Trailing) } 52 | 53 | /// width dimension 54 | public var width: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Width) } 55 | 56 | /// height dimension 57 | public var height: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Height) } 58 | 59 | /// centerX dimension 60 | public var centerX: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.CenterX) } 61 | 62 | /// centerY dimension 63 | public var centerY: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.CenterY) } 64 | 65 | /// baseline position 66 | public var baseline: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Baseline) } 67 | 68 | /// firse baseline position 69 | @available(iOS 8.0, *) 70 | public var firstBaseline: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.FirstBaseline) } 71 | 72 | /// left margin 73 | @available(iOS 8.0, *) 74 | public var leftMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.LeftMargin) } 75 | 76 | /// right margin 77 | @available(iOS 8.0, *) 78 | public var rightMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.RightMargin) } 79 | 80 | /// top margin 81 | @available(iOS 8.0, *) 82 | public var topMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.TopMargin) } 83 | 84 | /// bottom margin 85 | @available(iOS 8.0, *) 86 | public var bottomMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.BottomMargin) } 87 | 88 | /// leading margin 89 | @available(iOS 8.0, *) 90 | public var leadingMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.LeadingMargin) } 91 | 92 | /// trailing margin 93 | @available(iOS 8.0, *) 94 | public var trailingMargin: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.TrailingMargin) } 95 | 96 | /// centerX within margins 97 | @available(iOS 8.0, *) 98 | public var centerXWithinMargins: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.CenterXWithinMargins) } 99 | 100 | /// centerY within margins 101 | @available(iOS 8.0, *) 102 | public var centerYWithinMargins: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.CenterYWithinMargins) } 103 | 104 | /// top + left + bottom + right edges 105 | public var edges: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Edges) } 106 | 107 | /// width + height dimensions 108 | public var size: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Size) } 109 | 110 | // centerX + centerY positions 111 | public var center: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Center) } 112 | 113 | // top + left + bottom + right margins 114 | @available(iOS 8.0, *) 115 | public var margins: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.Margins) } 116 | 117 | // centerX + centerY within margins 118 | @available(iOS 8.0, *) 119 | public var centerWithinMargins: ConstraintDescriptionExtendable { return self.makeConstraintDescription(ConstraintAttributes.CenterWithinMargins) } 120 | 121 | internal init(view: View, file: String, line: UInt) { 122 | self.view = view 123 | self.file = file 124 | self.line = line 125 | } 126 | 127 | internal let file: String 128 | internal let line: UInt 129 | internal let view: View 130 | internal var constraintDescriptions = [ConstraintDescription]() 131 | 132 | internal func makeConstraintDescription(attributes: ConstraintAttributes) -> ConstraintDescription { 133 | let item = ConstraintItem(object: self.view, attributes: attributes) 134 | let constraintDescription = ConstraintDescription(fromItem: item) 135 | self.constraintDescriptions.append(constraintDescription) 136 | return constraintDescription 137 | } 138 | 139 | internal class func prepareConstraints(view view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] { 140 | let maker = ConstraintMaker(view: view, file: file, line: line) 141 | closure(make: maker) 142 | 143 | let constraints = maker.constraintDescriptions.map { $0.constraint } 144 | for constraint in constraints { 145 | constraint.makerFile = maker.file 146 | constraint.makerLine = maker.line 147 | } 148 | return constraints 149 | } 150 | 151 | internal class func makeConstraints(view view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) { 152 | view.translatesAutoresizingMaskIntoConstraints = false 153 | let maker = ConstraintMaker(view: view, file: file, line: line) 154 | closure(make: maker) 155 | 156 | let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint } 157 | for constraint in constraints { 158 | constraint.makerFile = maker.file 159 | constraint.makerLine = maker.line 160 | constraint.installOnView(updateExisting: false) 161 | } 162 | } 163 | 164 | internal class func remakeConstraints(view view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) { 165 | view.translatesAutoresizingMaskIntoConstraints = false 166 | let maker = ConstraintMaker(view: view, file: file, line: line) 167 | closure(make: maker) 168 | 169 | self.removeConstraints(view: view) 170 | let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint } 171 | for constraint in constraints { 172 | constraint.makerFile = maker.file 173 | constraint.makerLine = maker.line 174 | constraint.installOnView(updateExisting: false) 175 | } 176 | } 177 | 178 | internal class func updateConstraints(view view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) { 179 | view.translatesAutoresizingMaskIntoConstraints = false 180 | let maker = ConstraintMaker(view: view, file: file, line: line) 181 | closure(make: maker) 182 | 183 | let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint} 184 | for constraint in constraints { 185 | constraint.makerFile = maker.file 186 | constraint.makerLine = maker.line 187 | constraint.installOnView(updateExisting: true) 188 | } 189 | } 190 | 191 | internal class func removeConstraints(view view: View) { 192 | for existingLayoutConstraint in view.snp_installedLayoutConstraints { 193 | existingLayoutConstraint.snp_constraint?.uninstall() 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/ConstraintRelation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to define `NSLayoutRelation` 32 | */ 33 | internal enum ConstraintRelation: Int { 34 | case Equal = 1, LessThanOrEqualTo, GreaterThanOrEqualTo 35 | 36 | internal var layoutRelation: NSLayoutRelation { 37 | get { 38 | switch(self) { 39 | case .LessThanOrEqualTo: 40 | return .LessThanOrEqual 41 | case .GreaterThanOrEqualTo: 42 | return .GreaterThanOrEqual 43 | default: 44 | return .Equal 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Pods/SnapKit/Source/Debugging.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to allow adding a snp_label to a View for debugging purposes 32 | */ 33 | public extension View { 34 | 35 | public var snp_label: String? { 36 | get { 37 | return objc_getAssociatedObject(self, &labelKey) as? String 38 | } 39 | set { 40 | objc_setAssociatedObject(self, &labelKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC) 41 | } 42 | } 43 | 44 | } 45 | 46 | /** 47 | Used to allow adding a snp_label to a LayoutConstraint for debugging purposes 48 | */ 49 | public extension LayoutConstraint { 50 | 51 | public var snp_label: String? { 52 | get { 53 | return objc_getAssociatedObject(self, &labelKey) as? String 54 | } 55 | set { 56 | objc_setAssociatedObject(self, &labelKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC) 57 | } 58 | } 59 | 60 | override public var description: String { 61 | var description = "<" 62 | 63 | description += descriptionForObject(self) 64 | 65 | description += " \(descriptionForObject(self.firstItem))" 66 | if self.firstAttribute != .NotAnAttribute { 67 | description += ".\(self.firstAttribute.snp_description)" 68 | } 69 | 70 | description += " \(self.relation.snp_description)" 71 | 72 | if let secondItem: AnyObject = self.secondItem { 73 | description += " \(descriptionForObject(secondItem))" 74 | } 75 | 76 | if self.secondAttribute != .NotAnAttribute { 77 | description += ".\(self.secondAttribute.snp_description)" 78 | } 79 | 80 | if self.multiplier != 1.0 { 81 | description += " * \(self.multiplier)" 82 | } 83 | 84 | if self.secondAttribute == .NotAnAttribute { 85 | description += " \(self.constant)" 86 | } else { 87 | if self.constant > 0.0 { 88 | description += " + \(self.constant)" 89 | } else if self.constant < 0.0 { 90 | description += " - \(CGFloat.abs(self.constant))" 91 | } 92 | } 93 | 94 | if self.priority != 1000.0 { 95 | description += " ^\(self.priority)" 96 | } 97 | 98 | description += ">" 99 | 100 | return description 101 | } 102 | 103 | internal var snp_makerFile: String? { 104 | return self.snp_constraint?.makerFile 105 | } 106 | 107 | internal var snp_makerLine: UInt? { 108 | return self.snp_constraint?.makerLine 109 | } 110 | 111 | } 112 | 113 | private var labelKey = "" 114 | 115 | private func descriptionForObject(object: AnyObject) -> String { 116 | let pointerDescription = NSString(format: "%p", ObjectIdentifier(object).uintValue) 117 | var desc = "" 118 | 119 | desc += object.dynamicType.description() 120 | 121 | if let object = object as? View { 122 | desc += ":\(object.snp_label ?? pointerDescription)" 123 | } else if let object = object as? LayoutConstraint { 124 | desc += ":\(object.snp_label ?? pointerDescription)" 125 | } else { 126 | desc += ":\(pointerDescription)" 127 | } 128 | 129 | if let object = object as? LayoutConstraint, let file = object.snp_makerFile, let line = object.snp_makerLine { 130 | desc += "@\(file)#\(line)" 131 | } 132 | 133 | desc += "" 134 | return desc 135 | } 136 | 137 | private extension NSLayoutRelation { 138 | 139 | private var snp_description: String { 140 | switch self { 141 | case .Equal: return "==" 142 | case .GreaterThanOrEqual: return ">=" 143 | case .LessThanOrEqual: return "<=" 144 | } 145 | } 146 | 147 | } 148 | 149 | private extension NSLayoutAttribute { 150 | 151 | private var snp_description: String { 152 | #if os(iOS) || os(tvOS) 153 | switch self { 154 | case .NotAnAttribute: return "notAnAttribute" 155 | case .Top: return "top" 156 | case .Left: return "left" 157 | case .Bottom: return "bottom" 158 | case .Right: return "right" 159 | case .Leading: return "leading" 160 | case .Trailing: return "trailing" 161 | case .Width: return "width" 162 | case .Height: return "height" 163 | case .CenterX: return "centerX" 164 | case .CenterY: return "centerY" 165 | case .Baseline: return "baseline" 166 | case .FirstBaseline: return "firstBaseline" 167 | case .TopMargin: return "topMargin" 168 | case .LeftMargin: return "leftMargin" 169 | case .BottomMargin: return "bottomMargin" 170 | case .RightMargin: return "rightMargin" 171 | case .LeadingMargin: return "leadingMargin" 172 | case .TrailingMargin: return "trailingMargin" 173 | case .CenterXWithinMargins: return "centerXWithinMargins" 174 | case .CenterYWithinMargins: return "centerYWithinMargins" 175 | } 176 | #else 177 | switch self { 178 | case .NotAnAttribute: return "notAnAttribute" 179 | case .Top: return "top" 180 | case .Left: return "left" 181 | case .Bottom: return "bottom" 182 | case .Right: return "right" 183 | case .Leading: return "leading" 184 | case .Trailing: return "trailing" 185 | case .Width: return "width" 186 | case .Height: return "height" 187 | case .CenterX: return "centerX" 188 | case .CenterY: return "centerY" 189 | case .Baseline: return "baseline" 190 | default: return "default" 191 | } 192 | #endif 193 | 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/EdgeInsets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | public typealias EdgeInsets = UIEdgeInsets 27 | public func EdgeInsetsMake(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) -> EdgeInsets { 28 | return EdgeInsets(top: top, left: left, bottom: bottom, right: right) 29 | } 30 | public let EdgeInsetsZero = EdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 31 | #else 32 | import AppKit 33 | public typealias EdgeInsets = NSEdgeInsets 34 | public func EdgeInsetsMake(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) -> EdgeInsets { 35 | return EdgeInsets(top: top, left: left, bottom: bottom, right: right) 36 | } 37 | public let EdgeInsetsZero = EdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 38 | #endif 39 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/LayoutConstraint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | /** 31 | Used to add extra information to the actual `NSLayoutConstraint`'s that will UIKit/AppKit will utilize 32 | */ 33 | public class LayoutConstraint: NSLayoutConstraint { 34 | 35 | internal var snp_constraint: Constraint? = nil 36 | 37 | } 38 | 39 | internal func ==(left: LayoutConstraint, right: LayoutConstraint) -> Bool { 40 | if left.firstItem !== right.firstItem { 41 | return false 42 | } 43 | if left.secondItem !== right.secondItem { 44 | return false 45 | } 46 | if left.firstAttribute != right.firstAttribute { 47 | return false 48 | } 49 | if left.secondAttribute != right.secondAttribute { 50 | return false 51 | } 52 | if left.relation != right.relation { 53 | return false 54 | } 55 | if left.priority != right.priority { 56 | return false 57 | } 58 | if left.multiplier != right.multiplier { 59 | return false 60 | } 61 | return true 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/SnapKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | public typealias InterfaceLayoutDirection = UIUserInterfaceLayoutDirection 27 | public typealias LayoutSupport = UILayoutSupport 28 | #else 29 | import AppKit 30 | public typealias InterfaceLayoutDirection = NSUserInterfaceLayoutDirection 31 | public class LayoutSupport {} 32 | #endif 33 | 34 | /** 35 | Used to configure different parts of SnapKit 36 | */ 37 | public struct Config { 38 | 39 | /// The interface layout direction 40 | public static var interfaceLayoutDirection = InterfaceLayoutDirection.LeftToRight 41 | 42 | } -------------------------------------------------------------------------------- /Pods/SnapKit/Source/View+SnapKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | public typealias View = UIView 27 | #else 28 | import AppKit 29 | public typealias View = NSView 30 | #endif 31 | 32 | /** 33 | Used to expose public API on views 34 | */ 35 | public extension View { 36 | 37 | /// left edge 38 | public var snp_left: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Left) } 39 | 40 | /// top edge 41 | public var snp_top: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Top) } 42 | 43 | /// right edge 44 | public var snp_right: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Right) } 45 | 46 | /// bottom edge 47 | public var snp_bottom: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Bottom) } 48 | 49 | /// leading edge 50 | public var snp_leading: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Leading) } 51 | 52 | /// trailing edge 53 | public var snp_trailing: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Trailing) } 54 | 55 | /// width dimension 56 | public var snp_width: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Width) } 57 | 58 | /// height dimension 59 | public var snp_height: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Height) } 60 | 61 | /// centerX position 62 | public var snp_centerX: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterX) } 63 | 64 | /// centerY position 65 | public var snp_centerY: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterY) } 66 | 67 | /// baseline position 68 | public var snp_baseline: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Baseline) } 69 | 70 | /// first baseline position 71 | @available(iOS 8.0, *) 72 | public var snp_firstBaseline: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.FirstBaseline) } 73 | 74 | /// left margin 75 | @available(iOS 8.0, *) 76 | public var snp_leftMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.LeftMargin) } 77 | 78 | /// right margin 79 | @available(iOS 8.0, *) 80 | public var snp_rightMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.RightMargin) } 81 | 82 | /// top margin 83 | @available(iOS 8.0, *) 84 | public var snp_topMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.TopMargin) } 85 | 86 | /// bottom margin 87 | @available(iOS 8.0, *) 88 | public var snp_bottomMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.BottomMargin) } 89 | 90 | /// leading margin 91 | @available(iOS 8.0, *) 92 | public var snp_leadingMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.LeadingMargin) } 93 | 94 | /// trailing margin 95 | @available(iOS 8.0, *) 96 | public var snp_trailingMargin: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.TrailingMargin) } 97 | 98 | /// centerX within margins 99 | @available(iOS 8.0, *) 100 | public var snp_centerXWithinMargins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterXWithinMargins) } 101 | 102 | /// centerY within margins 103 | @available(iOS 8.0, *) 104 | public var snp_centerYWithinMargins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterYWithinMargins) } 105 | 106 | // top + left + bottom + right edges 107 | public var snp_edges: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Edges) } 108 | 109 | // width + height dimensions 110 | public var snp_size: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Size) } 111 | 112 | // centerX + centerY positions 113 | public var snp_center: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Center) } 114 | 115 | // top + left + bottom + right margins 116 | @available(iOS 8.0, *) 117 | public var snp_margins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.Margins) } 118 | 119 | // centerX + centerY within margins 120 | @available(iOS 8.0, *) 121 | public var snp_centerWithinMargins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterWithinMargins) } 122 | 123 | /** 124 | Prepares constraints with a `ConstraintMaker` and returns the made constraints but does not install them. 125 | 126 | :param: closure that will be passed the `ConstraintMaker` to make the constraints with 127 | 128 | :returns: the constraints made 129 | */ 130 | public func snp_prepareConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] { 131 | return ConstraintMaker.prepareConstraints(view: self, file: file, line: line, closure: closure) 132 | } 133 | 134 | /** 135 | Makes constraints with a `ConstraintMaker` and installs them along side any previous made constraints. 136 | 137 | :param: closure that will be passed the `ConstraintMaker` to make the constraints with 138 | */ 139 | public func snp_makeConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void { 140 | ConstraintMaker.makeConstraints(view: self, file: file, line: line, closure: closure) 141 | } 142 | 143 | /** 144 | Updates constraints with a `ConstraintMaker` that will replace existing constraints that match and install new ones. 145 | 146 | For constraints to match only the constant can be updated. 147 | 148 | :param: closure that will be passed the `ConstraintMaker` to update the constraints with 149 | */ 150 | public func snp_updateConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void { 151 | ConstraintMaker.updateConstraints(view: self, file: file, line: line, closure: closure) 152 | } 153 | 154 | /** 155 | Remakes constraints with a `ConstraintMaker` that will first remove all previously made constraints and make and install new ones. 156 | 157 | :param: closure that will be passed the `ConstraintMaker` to remake the constraints with 158 | */ 159 | public func snp_remakeConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void { 160 | ConstraintMaker.remakeConstraints(view: self, file: file, line: line, closure: closure) 161 | } 162 | 163 | /** 164 | Removes all previously made constraints. 165 | */ 166 | public func snp_removeConstraints() { 167 | ConstraintMaker.removeConstraints(view: self) 168 | } 169 | 170 | internal var snp_installedLayoutConstraints: [LayoutConstraint] { 171 | get { 172 | if let constraints = objc_getAssociatedObject(self, &installedLayoutConstraintsKey) as? [LayoutConstraint] { 173 | return constraints 174 | } 175 | return [] 176 | } 177 | set { 178 | objc_setAssociatedObject(self, &installedLayoutConstraintsKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) 179 | } 180 | } 181 | } 182 | 183 | private var installedLayoutConstraintsKey = "" 184 | -------------------------------------------------------------------------------- /Pods/SnapKit/Source/ViewController+SnapKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-2015 SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | 27 | /** 28 | Used to expose public API on view controllers 29 | */ 30 | public extension UIViewController { 31 | 32 | /// top layout guide top 33 | public var snp_topLayoutGuideTop: ConstraintItem { return ConstraintItem(object: self.topLayoutGuide, attributes: ConstraintAttributes.Top) } 34 | 35 | /// top layout guide bottom 36 | public var snp_topLayoutGuideBottom: ConstraintItem { return ConstraintItem(object: self.topLayoutGuide, attributes: ConstraintAttributes.Bottom) } 37 | 38 | /// bottom layout guide top 39 | public var snp_bottomLayoutGuideTop: ConstraintItem { return ConstraintItem(object: self.bottomLayoutGuide, attributes: ConstraintAttributes.Top) } 40 | 41 | /// bottom layout guide bottom 42 | public var snp_bottomLayoutGuideBottom: ConstraintItem { return ConstraintItem(object: self.bottomLayoutGuide, attributes: ConstraintAttributes.Bottom) } 43 | 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SnapKit 5 | 6 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | SnapKit 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Cell_Animations : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Cell_Animations 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "Pods-Cell Animations/SnapKit.framework" 88 | fi 89 | if [[ "$CONFIGURATION" == "Release" ]]; then 90 | install_framework "Pods-Cell Animations/SnapKit.framework" 91 | fi 92 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY="$(cd "${1%/*}" && pwd)" 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 63 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 64 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 65 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 66 | fi 67 | rm -f "$RESOURCES_TO_COPY" 68 | 69 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 70 | then 71 | case "${TARGETED_DEVICE_FAMILY}" in 72 | 1,2) 73 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 74 | ;; 75 | 1) 76 | TARGET_DEVICE_ARGS="--target-device iphone" 77 | ;; 78 | 2) 79 | TARGET_DEVICE_ARGS="--target-device ipad" 80 | ;; 81 | *) 82 | TARGET_DEVICE_ARGS="--target-device mac" 83 | ;; 84 | esac 85 | 86 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 87 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 88 | while read line; do 89 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 90 | XCASSET_FILES+=("$line") 91 | fi 92 | done <<<"$OTHER_XCASSETS" 93 | 94 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 95 | fi 96 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_Cell_AnimationsVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_Cell_AnimationsVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SnapKit.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SnapKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Cell Animations 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Cell_Animations { 2 | umbrella header "Pods-Cell Animations-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Cell Animations/Pods-Cell Animations.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SnapKit.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SnapKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Cell Animations 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SnapKit 5 | 6 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | SnapKit 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Sticky_Headers : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Sticky_Headers 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "Pods-Sticky Headers/SnapKit.framework" 88 | fi 89 | if [[ "$CONFIGURATION" == "Release" ]]; then 90 | install_framework "Pods-Sticky Headers/SnapKit.framework" 91 | fi 92 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY="$(cd "${1%/*}" && pwd)" 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 63 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 64 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 65 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 66 | fi 67 | rm -f "$RESOURCES_TO_COPY" 68 | 69 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 70 | then 71 | case "${TARGETED_DEVICE_FAMILY}" in 72 | 1,2) 73 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 74 | ;; 75 | 1) 76 | TARGET_DEVICE_ARGS="--target-device iphone" 77 | ;; 78 | 2) 79 | TARGET_DEVICE_ARGS="--target-device ipad" 80 | ;; 81 | *) 82 | TARGET_DEVICE_ARGS="--target-device mac" 83 | ;; 84 | esac 85 | 86 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 87 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 88 | while read line; do 89 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 90 | XCASSET_FILES+=("$line") 91 | fi 92 | done <<<"$OTHER_XCASSETS" 93 | 94 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 95 | fi 96 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_Sticky_HeadersVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_Sticky_HeadersVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SnapKit.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SnapKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Sticky Headers 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Sticky_Headers { 2 | umbrella header "Pods-Sticky Headers-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Sticky Headers/Pods-Sticky Headers.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SnapKit.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SnapKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Sticky Headers 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.18.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/SnapKit-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SnapKit : NSObject 3 | @end 4 | @implementation PodsDummy_SnapKit 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/SnapKit-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/SnapKit-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double SnapKitVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char SnapKitVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/SnapKit.modulemap: -------------------------------------------------------------------------------- 1 | framework module SnapKit { 2 | umbrella header "SnapKit-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SnapKit/SnapKit.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SnapKit" "${PODS_ROOT}/Headers/Public" 3 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 4 | PODS_ROOT = ${SRCROOT} 5 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collection View Animations 2 | 3 | Sample project demonstrating how to expand / collapse collection view cells using `UIView` animation closures in addition to sticky headers. 4 | 5 | ## Features 6 | 7 | - [X] Collection View with Custom Layout 8 | - [X] Custom Layout Attributes supporting UIView Cell Content Size Animations 9 | - [X] Content Offset Scroll Behavior while Animating 10 | - [X] Sticky Section Headers in Collection View 11 | - [X] Efficient Sticky Header Performance using Invalidation Context 12 | 13 | ## Description 14 | 15 | Collection views are very powerful, but can be cumbersome as well. When initially trying to figure out how to expand / collapse a cell in a `UICollectionView`, I was very surprised to find so little documentation around using a typical `UIView.animationWithDuration` closure to control the animation. The majority of documentation I found was using either the [setCollectionViewLayout(_:animated:)](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/#//apple_ref/occ/instm/UICollectionView/setCollectionViewLayout:animated:) API or the [performBatchUpdates(_:completion:)](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/#//apple_ref/occ/instm/UICollectionView/performBatchUpdates:completion:) API. However, neither of these approaches give you full control of the actual animation. I simply wanted to be able to use my own animation closures to control the behavior of the cells using a custom `UICollectionViewLayout`. 16 | 17 | After much investigation, I was able to find a solution leveraging all the `UICollectionViewLayout` APIs. You can animate the cells using a typical `UIView.animationWithDuration` closure to control all aspects of the animation in combination with the custom layout. You can even control the `contentOffset` of the `UICollectionView` while animating. Once I could control the animation, I needed to add sticky headers into the collection view as well. That proved to be MUCH more difficult and required a custom [UICollectionViewLayoutInvalidationContext](https://developer.apple.com/library/tvos/documentation/UIKit/Reference/UICollectionViewLayoutInvalidationContext_class/index.html). 18 | 19 | This sample project was created to demonstrate how to set up a fully custom [UICollectionViewLayout](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewLayout_class/) to perform custom cell animations as well as demonstrate how to efficiently implement sticky section headers. Hopefully this will help someone else when facing the same challenges. 20 | 21 | --- 22 | 23 | ## Apps 24 | 25 | The sample project contains two different applications, each demonstrating different functionality alongside a custom layout. 26 | 27 | ### Cell Animations 28 | 29 | The `Cell Animations` app target demonstrates how to implement a custom `UICollectionViewLayout` in a way that can support custom cell expand and collapse animations. Each time a cell is tapped, it is expanded and brought fully on-screen if it is collapsed, and is collapsed if it is expanded. 30 | 31 | ```swift 32 | func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 33 | layout.selectedCellIndexPath = layout.selectedCellIndexPath == indexPath ? nil : indexPath 34 | 35 | UIView.animateWithDuration( 36 | 0.4, 37 | delay: 0.0, 38 | usingSpringWithDamping: 1.0, 39 | initialSpringVelocity: 0.0, 40 | options: UIViewAnimationOptions(), 41 | animations: { 42 | self.layout.invalidateLayout() 43 | self.collectionView.layoutIfNeeded() 44 | }, 45 | completion: nil 46 | ) 47 | } 48 | ``` 49 | 50 | > Try modifying the values to customize the feel of the animation. This gives you the ultimate flexibility to control all aspects of the animation in any way you wish. 51 | 52 | #### Preparing the Layout 53 | 54 | The first step to implementing a custom `UICollectionViewLayout` is to compute the attributes for all cells that need to be displayed in the collection view. 55 | 56 | ```swift 57 | override func prepareLayout() { 58 | super.prepareLayout() 59 | 60 | previousAttributes = currentAttributes 61 | 62 | contentSize = CGSizeZero 63 | currentAttributes = [] 64 | 65 | if let collectionView = collectionView { 66 | let itemCount = collectionView.numberOfItemsInSection(0) 67 | let width = collectionView.bounds.size.width 68 | var y: CGFloat = 0 69 | 70 | for itemIndex in 0.. The trick here is that the `previousAttributes` are being stored. You'll see why that's important here in a bit. 93 | 94 | #### Invalidating the Layout 95 | 96 | For this particular collection view, we only want to invalidate the layout if the new bounds rect has a different size. We can ignore all origin changes in the bounds because this collection view doesn't need to react to them. This is very important from a performance standpoint. 97 | 98 | ```swift 99 | override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { 100 | if let oldBounds = collectionView?.bounds where !CGSizeEqualToSize(oldBounds.size, newBounds.size) { 101 | return true 102 | } 103 | 104 | return false 105 | } 106 | ``` 107 | 108 | > You should never invalidate the layout unless you absolutely have to. Invalidating the layout will cause your entire layout to be recalculated which can have serious performance implications. 109 | 110 | #### Layout Attributes 111 | 112 | The next step to implementing the custom layout is to override both of the following methods: 113 | 114 | ```swift 115 | override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 116 | return currentAttributes.filter { CGRectIntersectsRect(rect, $0.frame) } 117 | } 118 | 119 | override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 120 | return currentAttributes[indexPath.item] 121 | } 122 | ``` 123 | 124 | The first method is used by the collection view to query the layout for all attributes within a given rect. This is generally used to query for all the visible cell layout attributes. Therefore, this implementation simply filters the current attributes that intersect the specified rect. Since the attributes were already computed in the `prepareLayout` method, this implementation is very straightforward. 125 | 126 | The second method is used by the collection view to get the attributes for a given index path. Again, since this information was computed in the `prepareLayout` method, we only need to return the layout attributes for the specified index path. 127 | 128 | ##### Initial Layout Attributes 129 | 130 | Overriding the initial layout attributes API is where things start to get interesting. 131 | 132 | ```swift 133 | override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes { 134 | return previousAttributes[itemIndexPath.item] 135 | } 136 | ``` 137 | 138 | This is why the `previousAttributes` are stored in the `prepareLayout` method. When the collection view runs an animation, it needs to know the initial attributes and the layout attributes for the end of the animation. By default, Apple provides initial attributes that result in a fade-in animation. If you want to have fine-grained control over the animation, you need to provide the initial layout attributes for each cell to tell the collection view exactly how the animation should occur. 139 | 140 | > To see this behavior in action, comment out the `initialLayoutAttributesForAppearingItemAtIndexPath` method in the `Cell Animations` app and watch what happens. It isn't pretty... 141 | 142 | ##### Final Layout Attributes 143 | 144 | Overriding the final layout attributes API is just as important as the initial layout attributes, but is slightly easier. 145 | 146 | ```swift 147 | override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 148 | return layoutAttributesForItemAtIndexPath(itemIndexPath) 149 | } 150 | ``` 151 | 152 | By default, Apple will fade-out a cell that is disappearing. Since this is not the desired behavior, you need to provide the layout attributes for this case. For the `Cell Animations` app, the cell should animate to the final position without changing the alpha value. To accomplish this, the current layout attributes for the specified index path can be returned. 153 | 154 | > Try commenting out this method in the `Cell Animations` app to observe how this affects the animations. 155 | 156 | #### Content Offset 157 | 158 | Now that the cells are expanding and collapsing, we need to be able to scroll the collection view during the animation to make sure the cell is completely on-screen when expanded. Thankfully, Apple has a way for us to override the default scrolling behavior in these situations. 159 | 160 | ```swift 161 | override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint { 162 | guard let selectedCellIndexPath = selectedCellIndexPath else { return proposedContentOffset } 163 | 164 | var finalContentOffset = proposedContentOffset 165 | 166 | if let frame = layoutAttributesForItemAtIndexPath(selectedCellIndexPath)?.frame { 167 | let collectionViewHeight = collectionView?.bounds.size.height ?? 0 168 | 169 | let collectionViewTop = proposedContentOffset.y 170 | let collectionViewBottom = collectionViewTop + collectionViewHeight 171 | 172 | let cellTop = frame.origin.y 173 | let cellBottom = cellTop + frame.size.height 174 | 175 | if cellBottom > collectionViewBottom { 176 | finalContentOffset = CGPointMake(0.0, collectionViewTop + (cellBottom - collectionViewBottom)) 177 | } else if cellTop < collectionViewTop { 178 | finalContentOffset = CGPointMake(0.0, collectionViewTop - (collectionViewTop - cellTop)) 179 | } 180 | } 181 | 182 | return finalContentOffset 183 | } 184 | ``` 185 | 186 | Overriding this method can seem complicated, but it really isn't. Apple provides you a `proposedContentOffset` which is where the collection view will be scrolled to if you don't modify it. Then you just need to modify the offset values if needed. In this implementation, the offset is not overridden if there is not a selected cell. If there is a selected cell, then the content offset is adjusted if the cell is only partially visible on either the top or bottom of the screen. This results in the collection view smoothly scrolling alongside the expansion animation. 187 | 188 | > To see what happens without this implementation, comment out this method and give it a try. You'll see that the cells expand as expected, but the collection view will not make the effort to make sure the expanded cell is fully on-screen. 189 | 190 | ### Sticky Headers 191 | 192 | The goal of the second application (Sticky Headers) was to use a custom layout that could control cell animations in addition to sticky section header cells for each section. While this at first seemed like it would be a simple extension to the `Cell Animations` codebase, it required a much more in-depth understanding of the collection view layout invalidation process. 193 | 194 | > Sticky Headers refers to the behavior of table view section headers that stick to the top of the table view until bumped off by the next section header. 195 | 196 | #### Preparing Content Cell and Section Attributes 197 | 198 | Now that both content cells and section header cells need to be displayed in the collection view, the layout attributes need to be computed for both. In this example, content cells will be represented using regular cells while section headers will be displayed using supplementary views. 199 | 200 | > For more details on each cell type, please refer to the [Cell.swift](https://github.com/cnoon/CollectionViewAnimations/blob/master/Source/Common/Cell.swift) implementation and the [UICollectionView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/) documentation. 201 | 202 | ```swift 203 | override func prepareLayout() { 204 | super.prepareLayout() 205 | 206 | prepareContentCellAttributes() 207 | prepareSectionHeaderAttributes() 208 | } 209 | ``` 210 | 211 | You'll notice that the `prepareLayout` implementation performs two different steps. The first is to compute the layout attributes for all the content cells. This works exactly as the previous implementation in the `Cell Animations` app with the exception of computing the section limits. 212 | 213 | The `prepareSectionHeaderAttributes` implementation works more-or-less the same as the content cell implementation, but is instead generating supplementary view layout attributes with a custom `zIndex`. This is to make sure the section headers are always displayed ontop of the content cells. 214 | 215 | > The reason these are split into two different methods will be more apparent in the invalidation section. 216 | 217 | #### Layout Attributes 218 | 219 | Returning the layout attributes for the section headers works exactly the same as the layout attributes for the content cells, just with slightly different APIs. Apple uses the same fade-in / fade-out behavior for supplementary views as for regular cells. Since that is not the desired behavior, the previous attributes are stored in the `prepareLayout` implementation and the initial and final methods are overridden the same was as the content cells. 220 | 221 | ```swift 222 | override func initialLayoutAttributesForAppearingSupplementaryElementOfKind( 223 | elementKind: String, 224 | atIndexPath elementIndexPath: NSIndexPath) 225 | -> UICollectionViewLayoutAttributes? 226 | { 227 | return previousSectionAttributes[elementIndexPath.section] 228 | } 229 | 230 | override func layoutAttributesForSupplementaryViewOfKind( 231 | elementKind: String, 232 | atIndexPath indexPath: NSIndexPath) 233 | -> UICollectionViewLayoutAttributes? 234 | { 235 | return currentSectionAttributes[indexPath.section] 236 | } 237 | 238 | override func finalLayoutAttributesForDisappearingSupplementaryElementOfKind( 239 | elementKind: String, 240 | atIndexPath elementIndexPath: NSIndexPath) 241 | -> UICollectionViewLayoutAttributes? 242 | { 243 | return layoutAttributesForSupplementaryViewOfKind(elementKind, atIndexPath: elementIndexPath) 244 | } 245 | ``` 246 | 247 | > Try commenting these methods out to see how this affects the animations. 248 | 249 | #### Invalidation 250 | 251 | Invalidating the layout efficiently and correctly proved to be the most difficult part of this implementation. By default, I knew the layout needed to be invalidated each time a bounds change occurred to be able to update the sticky header positions. Otherwise they would never appear to be stuck to the top of the collection view. 252 | 253 | ```swift 254 | override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { 255 | return true 256 | } 257 | ``` 258 | 259 | If you comment out all the invalidation methods with the exception of `shouldInvalidateLayoutForBoundsChange(_:)`, you'll see that for the most part, this implementation works. Even though it is extremely inefficient, it does perform the correct animations in most cases. Where it breaks down is when sticky headers are animating on or off the screen. In order to fix this problem, it first required understanding why the section headers were misbehaving. Then required implementing a custom invalidation context. 260 | 261 | > Why the section headers misbehave in this situation is actually quite complicated. What is happening is thate invalidation occurs multiple times during the animation causing the `previousSectionAttributes` values to get out-of-sync causing the incorrect animation to be executed. 262 | 263 | ##### Invalidation Context 264 | 265 | Stopping the duplicate invalidation pass from occurring required a MUCH deeper understanding of the overall invalidation process along with implementing a custom [UICollectionViewLayoutInvalidationContext](https://developer.apple.com/library/tvos/documentation/UIKit/Reference/UICollectionViewLayoutInvalidationContext_class/index.html). What is really interesting is that Apple is creating an invalidation context under-the-hood during each layout invalidation without you even knowing it. It initializes a default one where `invalidateEverything` is set to `true` causing a full recalculation of all the layout attributes. Generally, this is what you want during an invalidation. However, when you are scrolling, you only need to invalidate the sticky header layout attributes, not the content cell attributes. Implementing a custom invalidation context let's you do this. 266 | 267 | The first step is to create an invalidation context subclass and override the `invalidateEverything` property. 268 | 269 | ```swift 270 | class InvalidationContext: UICollectionViewLayoutInvalidationContext { 271 | var invalidateSectionHeaders = false 272 | var shouldInvalidateEverything = true 273 | 274 | override var invalidateEverything: Bool { 275 | return shouldInvalidateEverything 276 | } 277 | } 278 | ``` 279 | 280 | Then you need to override the `invalidationContextClass()` method in your layout. 281 | 282 | ```swift 283 | override class func invalidationContextClass() -> AnyClass { 284 | return InvalidationContext.self 285 | } 286 | ``` 287 | 288 | This method tells the collection view what type of invalidation context to instantiate when it needs one. This method is called when the layout is manually invalidated by calling `layout.invalidateLayout()`. Since this manually called when expanding and/or collapsing a cell, the default value of `shouldInvalidateEverything` needs to be `true`. 289 | 290 | ##### Invalidation Context for Bounds Change 291 | 292 | When invalidation occurs due to a bounds change, Apple gives you the opportunity to provide a custom invalidation context before actually invalidating the layout. This allows you to store additional state in the invalidation context to help selectively perform invalidations. 293 | 294 | ```swift 295 | override func invalidationContextForBoundsChange(newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { 296 | let invalidationContext = super.invalidationContextForBoundsChange(newBounds) as! InvalidationContext 297 | 298 | guard let oldBounds = collectionView?.bounds else { return invalidationContext } 299 | guard oldBounds != newBounds else { return invalidationContext } 300 | 301 | let originChanged = !CGPointEqualToPoint(oldBounds.origin, newBounds.origin) 302 | let sizeChanged = !CGSizeEqualToSize(oldBounds.size, newBounds.size) 303 | 304 | if sizeChanged { 305 | invalidationContext.shouldInvalidateEverything = true 306 | } else { 307 | invalidationContext.shouldInvalidateEverything = false 308 | } 309 | 310 | if originChanged { 311 | invalidationContext.invalidateSectionHeaders = true 312 | } 313 | 314 | return invalidationContext 315 | } 316 | ``` 317 | 318 | The first step is to create an `InvalidationContext`, then perform the same types of `bounds` checks that were used in the `Cell Animations` app. 319 | 320 | * **Size Changed** - Invalidate all the layout attributes because the cell is being expanded or collapsed 321 | * **Origin Changed** - Invalidate only the section header layout attributes because the collection view is scrolling 322 | 323 | ##### Invalidate Layout with Context 324 | 325 | The final step in the invalidation process is to actually perform the invalidation. This happens in the `invalidateLayoutWithContext(_:)` method. 326 | 327 | ```swift 328 | override func invalidateLayoutWithContext(context: UICollectionViewLayoutInvalidationContext) { 329 | let invalidationContext = context as! InvalidationContext 330 | 331 | guard invalidationContext.invalidateEverything || invalidationContext.invalidateSectionHeaders else { return } 332 | 333 | guard !invalidationContext.invalidateEverything else { 334 | super.invalidateLayoutWithContext(invalidationContext) 335 | return 336 | } 337 | 338 | //============== Recompute Section Headers ================= 339 | 340 | prepareSectionHeaderAttributes() 341 | 342 | var sectionHeaderIndexPaths: [NSIndexPath] = [] 343 | 344 | for sectionIndex in 0.. This is why the `prepareLayout` method is split into two different methods. This allows the section header attributes to be recomputed without having to recompute ALL the attributes. 362 | 363 | After recomputing the section header attributes, the `invalidationContext` is notified of which supplementary elements need to be invalidated for the specified index paths. This doesn't have any affect on the collection view until `super` is called. By calling `super`, the layout attributes are invalidated for the supplementary elements and the collection view updates the layout attributes for the section headers at the specified index paths. 364 | 365 | This logic eliminates the problem of the duplicate invalidation pass and also is much more performant. While collection views and custom layouts are quite complex, they are extremely powerful when used correctly. 366 | 367 | --- 368 | 369 | ## Author 370 | 371 | - [Christian Noon](https://github.com/cnoon) ([@Christian_Noon](https://twitter.com/christian_noon)) 372 | 373 | ## License 374 | 375 | CollectionViewAnimations is released under the MIT license. See LICENSE for details. 376 | -------------------------------------------------------------------------------- /Source/Cell Animations/Layout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Layout.swift 3 | // Cell Animations 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Layout: UICollectionViewLayout { 12 | 13 | // MARK: - Properties 14 | 15 | var previousAttributes: [UICollectionViewLayoutAttributes] = [] 16 | var currentAttributes: [UICollectionViewLayoutAttributes] = [] 17 | 18 | var contentSize = CGSizeZero 19 | var selectedCellIndexPath: NSIndexPath? 20 | 21 | // MARK: - Preparation 22 | 23 | override func prepareLayout() { 24 | super.prepareLayout() 25 | 26 | previousAttributes = currentAttributes 27 | 28 | contentSize = CGSizeZero 29 | currentAttributes = [] 30 | 31 | if let collectionView = collectionView { 32 | let itemCount = collectionView.numberOfItemsInSection(0) 33 | let width = collectionView.bounds.size.width 34 | var y: CGFloat = 0 35 | 36 | for itemIndex in 0.. UICollectionViewLayoutAttributes? { 58 | return previousAttributes[itemIndexPath.item] 59 | } 60 | 61 | override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 62 | return currentAttributes[indexPath.item] 63 | } 64 | 65 | override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 66 | return layoutAttributesForItemAtIndexPath(itemIndexPath) 67 | } 68 | 69 | override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 70 | return currentAttributes.filter { CGRectIntersectsRect(rect, $0.frame) } 71 | } 72 | 73 | // MARK: - Invalidation 74 | 75 | override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { 76 | if let oldBounds = collectionView?.bounds where !CGSizeEqualToSize(oldBounds.size, newBounds.size) { 77 | return true 78 | } 79 | 80 | return false 81 | } 82 | 83 | // MARK: - Collection View Info 84 | 85 | override func collectionViewContentSize() -> CGSize { 86 | return contentSize 87 | } 88 | 89 | override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint { 90 | guard let selectedCellIndexPath = selectedCellIndexPath else { return proposedContentOffset } 91 | 92 | var finalContentOffset = proposedContentOffset 93 | 94 | if let frame = layoutAttributesForItemAtIndexPath(selectedCellIndexPath)?.frame { 95 | let collectionViewHeight = collectionView?.bounds.size.height ?? 0 96 | 97 | let collectionViewTop = proposedContentOffset.y 98 | let collectionViewBottom = collectionViewTop + collectionViewHeight 99 | 100 | let cellTop = frame.origin.y 101 | let cellBottom = cellTop + frame.size.height 102 | 103 | if cellBottom > collectionViewBottom { 104 | finalContentOffset = CGPointMake(0.0, collectionViewTop + (cellBottom - collectionViewBottom)) 105 | } else if cellTop < collectionViewTop { 106 | finalContentOffset = CGPointMake(0.0, collectionViewTop - (collectionViewTop - cellTop)) 107 | } 108 | } 109 | 110 | return finalContentOffset 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Source/Cell Animations/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Cell Animations 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import SnapKit 10 | import UIKit 11 | 12 | class ViewController: UIViewController { 13 | 14 | // MARK: Properties 15 | 16 | let colors: [UIColor] 17 | var collectionView: UICollectionView! 18 | var layout = Layout() 19 | 20 | // MARK: Initialization 21 | 22 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { 23 | var colors: [UIColor] = [] 24 | 25 | for _ in 1...20 { 26 | colors.append(UIColor.randomColor()) 27 | } 28 | 29 | self.colors = colors 30 | 31 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 32 | } 33 | 34 | required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | // MARK: View Lifecycle 39 | 40 | override func viewDidLoad() { 41 | super.viewDidLoad() 42 | 43 | collectionView = { 44 | let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout) 45 | collectionView.backgroundColor = UIColor.whiteColor() 46 | 47 | collectionView.dataSource = self 48 | collectionView.delegate = self 49 | 50 | collectionView.registerClass(ContentCell.self, forCellWithReuseIdentifier: ContentCell.kind) 51 | 52 | return collectionView 53 | }() 54 | 55 | view.addSubview(collectionView) 56 | 57 | collectionView.snp_makeConstraints { make in 58 | make.edges.equalTo(view) 59 | } 60 | } 61 | 62 | // MARK: Status Bar 63 | 64 | override func prefersStatusBarHidden() -> Bool { 65 | return true 66 | } 67 | } 68 | 69 | // MARK: - UICollectionViewDataSource 70 | 71 | extension ViewController: UICollectionViewDataSource { 72 | func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 73 | return colors.count 74 | } 75 | 76 | func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 77 | let cell = collectionView.dequeueReusableCellWithReuseIdentifier( 78 | ContentCell.kind, 79 | forIndexPath: indexPath 80 | ) as! ContentCell 81 | 82 | cell.backgroundColor = colors[indexPath.item] 83 | cell.label.text = "Cell \(indexPath.item)" 84 | 85 | return cell 86 | } 87 | } 88 | 89 | // MARK: - UICollectionViewDelegate 90 | 91 | extension ViewController: UICollectionViewDelegate { 92 | func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 93 | layout.selectedCellIndexPath = layout.selectedCellIndexPath == indexPath ? nil : indexPath 94 | 95 | let bounceEnabled = false 96 | 97 | UIView.animateWithDuration( 98 | 0.4, 99 | delay: 0.0, 100 | usingSpringWithDamping: bounceEnabled ? 0.5 : 1.0, 101 | initialSpringVelocity: bounceEnabled ? 2.0 : 0.0, 102 | options: UIViewAnimationOptions(), 103 | animations: { 104 | self.layout.invalidateLayout() 105 | self.collectionView.layoutIfNeeded() 106 | }, 107 | completion: nil 108 | ) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Source/Common/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CollectionViewAnimations 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | // MARK: - App State Methods 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | window = { 19 | let window = UIWindow(frame: UIScreen.mainScreen().bounds) 20 | window.rootViewController = ViewController() 21 | window.backgroundColor = UIColor.whiteColor() 22 | window.makeKeyAndVisible() 23 | 24 | return window 25 | }() 26 | 27 | return true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Common/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Source/Common/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Source/Common/Cell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cell.swift 3 | // CollectionViewAnimations 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContentCell: UICollectionViewCell { 12 | class var reuseIdentifier: String { return "\(self)" } 13 | class var kind: String { return "ContentCell" } 14 | 15 | var label: UILabel! 16 | 17 | // MARK: Initialization 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame: frame) 21 | 22 | label = { 23 | let label = UILabel() 24 | label.font = UIFont.systemFontOfSize(20) 25 | label.textColor = UIColor.whiteColor() 26 | 27 | return label 28 | }() 29 | 30 | contentView.addSubview(label) 31 | 32 | label.snp_makeConstraints { make in 33 | make.center.equalTo(contentView) 34 | } 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | fatalError() 39 | } 40 | 41 | override func prepareForReuse() { 42 | UIView.performWithoutAnimation { 43 | self.backgroundColor = nil 44 | } 45 | } 46 | 47 | // MARK: Layout 48 | 49 | override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes) { 50 | super.applyLayoutAttributes(layoutAttributes) 51 | layoutIfNeeded() 52 | } 53 | } 54 | 55 | // MARK: - 56 | 57 | class SectionHeaderCell: UICollectionReusableView { 58 | class var reuseIdentifier: String { return "\(self)" } 59 | class var kind: String { return "SectionHeaderCell" } 60 | 61 | var label: UILabel! 62 | 63 | // MARK: Initialization 64 | 65 | override init(frame: CGRect) { 66 | super.init(frame: frame) 67 | 68 | backgroundColor = UIColor(white: 0.2, alpha: 1.0) 69 | 70 | label = { 71 | let label = UILabel() 72 | label.font = UIFont.boldSystemFontOfSize(14) 73 | label.textColor = UIColor.whiteColor() 74 | 75 | return label 76 | }() 77 | 78 | addSubview(label) 79 | 80 | label.snp_makeConstraints { make in 81 | make.leading.equalTo(self).offset(20) 82 | make.trailing.equalTo(self).offset(-20) 83 | make.centerY.equalTo(self) 84 | } 85 | } 86 | 87 | required init?(coder aDecoder: NSCoder) { 88 | fatalError() 89 | } 90 | 91 | // MARK: Layout 92 | 93 | override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes) { 94 | super.applyLayoutAttributes(layoutAttributes) 95 | layoutIfNeeded() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Source/Common/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/Common/Number.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Number.swift 3 | // CollectionViewAnimations 4 | // 5 | // Created by Christian Noon on 11/2/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Number { 12 | static func random(from from: Int, to: Int) -> Int { 13 | guard from < to else { fatalError("`from` MUST be less than `to`") } 14 | let delta = UInt32(to + 1 - from) 15 | 16 | return from + Int(arc4random_uniform(delta)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Common/UIColor+CVA.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+CVA.swift 3 | // CollectionViewAnimations 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | class func randomColor() -> UIColor { 13 | let red = CGFloat(Number.random(from: 0, to: 255)) / 255.0 14 | let green = CGFloat(Number.random(from: 0, to: 255)) / 255.0 15 | let blue = CGFloat(Number.random(from: 0, to: 255)) / 255.0 16 | 17 | return UIColor(red: red, green: green, blue: blue, alpha: 1.0) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Sticky Headers/InvalidationContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InvalidationContext.swift 3 | // Sticky Headers 4 | // 5 | // Created by Christian Noon on 11/3/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class InvalidationContext: UICollectionViewLayoutInvalidationContext { 12 | var invalidateSectionHeaders = false 13 | var shouldInvalidateEverything = true 14 | 15 | override var invalidateEverything: Bool { 16 | return shouldInvalidateEverything 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Sticky Headers/Layout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpandCollapseLayout.swift 3 | // Sticky Headers 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Layout: UICollectionViewLayout { 12 | 13 | // MARK: - Helper Types 14 | 15 | struct SectionLimit { 16 | let top: CGFloat 17 | let bottom: CGFloat 18 | } 19 | 20 | // MARK: - Properties 21 | 22 | var previousAttributes: [[UICollectionViewLayoutAttributes]] = [] 23 | var currentAttributes: [[UICollectionViewLayoutAttributes]] = [] 24 | 25 | var previousSectionAttributes: [UICollectionViewLayoutAttributes] = [] 26 | var currentSectionAttributes: [UICollectionViewLayoutAttributes] = [] 27 | 28 | var currentSectionLimits: [SectionLimit] = [] 29 | 30 | let sectionHeaderHeight: CGFloat = 40 31 | 32 | var contentSize = CGSizeZero 33 | var selectedCellIndexPath: NSIndexPath? 34 | 35 | // MARK: - Preparation 36 | 37 | override func prepareLayout() { 38 | super.prepareLayout() 39 | 40 | prepareContentCellAttributes() 41 | prepareSectionHeaderAttributes() 42 | } 43 | 44 | private func prepareContentCellAttributes() { 45 | guard let collectionView = collectionView else { return } 46 | 47 | //================== Reset Content Cell Attributes ================ 48 | 49 | previousAttributes = currentAttributes 50 | 51 | contentSize = CGSizeZero 52 | currentAttributes = [] 53 | currentSectionLimits = [] 54 | 55 | //================== Calculate New Content Cell Attributes ================== 56 | 57 | let width = collectionView.bounds.size.width 58 | var y: CGFloat = 0 59 | 60 | for sectionIndex in 0.. UICollectionViewLayoutAttributes? { 139 | return previousAttributes[itemIndexPath.section][itemIndexPath.item] 140 | } 141 | 142 | override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 143 | return currentAttributes[indexPath.section][indexPath.item] 144 | } 145 | 146 | override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { 147 | return layoutAttributesForItemAtIndexPath(itemIndexPath) 148 | } 149 | 150 | override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 151 | var attributes: [UICollectionViewLayoutAttributes] = [] 152 | 153 | for sectionIndex in 0..<(collectionView?.numberOfSections() ?? 0) { 154 | let sectionAttributes = currentSectionAttributes[sectionIndex] 155 | 156 | if CGRectIntersectsRect(rect, sectionAttributes.frame) { 157 | attributes.append(sectionAttributes) 158 | } 159 | 160 | for item in currentAttributes[sectionIndex] where CGRectIntersectsRect(rect, item.frame) { 161 | attributes.append(item) 162 | } 163 | } 164 | 165 | return attributes 166 | } 167 | 168 | // MARK: - Layout Attributes - Section Header Cell 169 | 170 | override func initialLayoutAttributesForAppearingSupplementaryElementOfKind( 171 | elementKind: String, 172 | atIndexPath elementIndexPath: NSIndexPath) 173 | -> UICollectionViewLayoutAttributes? 174 | { 175 | return previousSectionAttributes[elementIndexPath.section] 176 | } 177 | 178 | override func layoutAttributesForSupplementaryViewOfKind( 179 | elementKind: String, 180 | atIndexPath indexPath: NSIndexPath) 181 | -> UICollectionViewLayoutAttributes? 182 | { 183 | return currentSectionAttributes[indexPath.section] 184 | } 185 | 186 | override func finalLayoutAttributesForDisappearingSupplementaryElementOfKind( 187 | elementKind: String, 188 | atIndexPath elementIndexPath: NSIndexPath) 189 | -> UICollectionViewLayoutAttributes? 190 | { 191 | return layoutAttributesForSupplementaryViewOfKind(elementKind, atIndexPath: elementIndexPath) 192 | } 193 | 194 | // MARK: - Invalidation 195 | 196 | override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { 197 | return true 198 | } 199 | 200 | override class func invalidationContextClass() -> AnyClass { 201 | return InvalidationContext.self 202 | } 203 | 204 | override func invalidationContextForBoundsChange(newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { 205 | let invalidationContext = super.invalidationContextForBoundsChange(newBounds) as! InvalidationContext 206 | 207 | guard let oldBounds = collectionView?.bounds else { return invalidationContext } 208 | guard oldBounds != newBounds else { return invalidationContext } 209 | 210 | let originChanged = !CGPointEqualToPoint(oldBounds.origin, newBounds.origin) 211 | let sizeChanged = !CGSizeEqualToSize(oldBounds.size, newBounds.size) 212 | 213 | if sizeChanged { 214 | invalidationContext.shouldInvalidateEverything = true 215 | } else { 216 | invalidationContext.shouldInvalidateEverything = false 217 | } 218 | 219 | if originChanged { 220 | invalidationContext.invalidateSectionHeaders = true 221 | } 222 | 223 | return invalidationContext 224 | } 225 | 226 | override func invalidateLayoutWithContext(context: UICollectionViewLayoutInvalidationContext) { 227 | let invalidationContext = context as! InvalidationContext 228 | 229 | if invalidationContext.invalidateSectionHeaders { 230 | prepareSectionHeaderAttributes() 231 | 232 | var sectionHeaderIndexPaths: [NSIndexPath] = [] 233 | 234 | for sectionIndex in 0.. CGSize { 250 | return contentSize 251 | } 252 | 253 | override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint { 254 | guard let selectedCellIndexPath = selectedCellIndexPath else { return proposedContentOffset } 255 | 256 | var finalContentOffset = proposedContentOffset 257 | 258 | if let frame = layoutAttributesForItemAtIndexPath(selectedCellIndexPath)?.frame { 259 | let collectionViewHeight = collectionView?.bounds.size.height ?? 0 260 | 261 | let collectionViewTop = proposedContentOffset.y 262 | let collectionViewBottom = collectionViewTop + collectionViewHeight 263 | 264 | let cellTop = frame.origin.y 265 | let cellBottom = cellTop + frame.size.height 266 | 267 | if cellBottom > collectionViewBottom { 268 | finalContentOffset = CGPointMake(0.0, collectionViewTop + (cellBottom - collectionViewBottom)) 269 | } else if cellTop < collectionViewTop + sectionHeaderHeight { 270 | finalContentOffset = CGPointMake(0.0, collectionViewTop - (collectionViewTop - cellTop) - sectionHeaderHeight) 271 | } 272 | } 273 | 274 | return finalContentOffset 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /Source/Sticky Headers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Sticky Headers 4 | // 5 | // Created by Christian Noon on 10/29/15. 6 | // Copyright © 2015 Noondev. All rights reserved. 7 | // 8 | 9 | import SnapKit 10 | import UIKit 11 | 12 | class ViewController: UIViewController { 13 | 14 | // MARK: Properties 15 | 16 | let colors: [[UIColor]] 17 | var collectionView: UICollectionView! 18 | var layout = Layout() 19 | 20 | // MARK: Initialization 21 | 22 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { 23 | self.colors = { 24 | var colorsBySection: [[UIColor]] = [] 25 | 26 | for _ in 0...Number.random(from: 2, to: 4) { 27 | var colors: [UIColor] = [] 28 | 29 | for _ in 0...Number.random(from: 2, to: 10) { 30 | colors.append(UIColor.randomColor()) 31 | } 32 | 33 | colorsBySection.append(colors) 34 | } 35 | 36 | return colorsBySection 37 | }() 38 | 39 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 40 | } 41 | 42 | required init?(coder aDecoder: NSCoder) { 43 | fatalError("init(coder:) has not been implemented") 44 | } 45 | 46 | // MARK: View Lifecycle 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | 51 | collectionView = { 52 | let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout) 53 | collectionView.backgroundColor = UIColor.whiteColor() 54 | 55 | collectionView.dataSource = self 56 | collectionView.delegate = self 57 | 58 | collectionView.registerClass(ContentCell.self, forCellWithReuseIdentifier: ContentCell.reuseIdentifier) 59 | 60 | collectionView.registerClass( 61 | SectionHeaderCell.self, 62 | forSupplementaryViewOfKind: SectionHeaderCell.kind, 63 | withReuseIdentifier: SectionHeaderCell.reuseIdentifier 64 | ) 65 | 66 | return collectionView 67 | }() 68 | 69 | view.addSubview(collectionView) 70 | 71 | collectionView.snp_makeConstraints { make in 72 | make.edges.equalTo(view) 73 | } 74 | } 75 | 76 | // MARK: Status Bar 77 | 78 | override func prefersStatusBarHidden() -> Bool { 79 | return true 80 | } 81 | } 82 | 83 | // MARK: - UICollectionViewDataSource 84 | 85 | extension ViewController: UICollectionViewDataSource { 86 | func collectionView( 87 | collectionView: UICollectionView, 88 | layout collectionViewLayout: UICollectionViewLayout, 89 | referenceSizeForHeaderInSection section: Int) 90 | -> CGSize 91 | { 92 | return CGSize(width: collectionView.bounds.width, height: 40.0) 93 | } 94 | 95 | func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { 96 | return colors.count 97 | } 98 | 99 | func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 100 | return colors[section].count 101 | } 102 | 103 | func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 104 | let cell = collectionView.dequeueReusableCellWithReuseIdentifier( 105 | ContentCell.reuseIdentifier, 106 | forIndexPath: indexPath 107 | ) as! ContentCell 108 | 109 | UIView.performWithoutAnimation { 110 | cell.backgroundColor = self.colors[indexPath.section][indexPath.item] 111 | cell.label.text = "Cell (\(indexPath.section), \(indexPath.item))" 112 | } 113 | 114 | return cell 115 | } 116 | 117 | func collectionView( 118 | collectionView: UICollectionView, 119 | viewForSupplementaryElementOfKind kind: String, 120 | atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView 121 | { 122 | let cell = collectionView.dequeueReusableSupplementaryViewOfKind( 123 | SectionHeaderCell.kind, 124 | withReuseIdentifier: SectionHeaderCell.reuseIdentifier, 125 | forIndexPath: indexPath 126 | ) as! SectionHeaderCell 127 | 128 | cell.label.text = "Section \(indexPath.section)" 129 | 130 | return cell 131 | } 132 | } 133 | 134 | // MARK: - UICollectionViewDelegate 135 | 136 | extension ViewController: UICollectionViewDelegate { 137 | func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 138 | layout.selectedCellIndexPath = layout.selectedCellIndexPath == indexPath ? nil : indexPath 139 | 140 | let bounceEnabled = false 141 | 142 | UIView.animateWithDuration( 143 | 0.4, 144 | delay: 0.0, 145 | usingSpringWithDamping: bounceEnabled ? 0.5 : 1.0, 146 | initialSpringVelocity: bounceEnabled ? 2.0 : 0.0, 147 | options: UIViewAnimationOptions(), 148 | animations: { 149 | self.layout.invalidateLayout() 150 | self.collectionView.layoutIfNeeded() 151 | }, 152 | completion: nil 153 | ) 154 | } 155 | } 156 | --------------------------------------------------------------------------------