├── .gitignore ├── PullToRefreshAnimation ├── PullToRefreshAnimation.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── PullToRefreshAnimation │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── PrimeFlowLayout.h │ ├── PrimeFlowLayout.m │ ├── PullToRefreshAnimation-Info.plist │ ├── PullToRefreshAnimation-Prefix.pch │ ├── SampleCell.h │ ├── SampleCell.m │ ├── SampleViewController.h │ ├── SampleViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── PullToRefreshAnimationTests │ ├── PullToRefreshAnimationTests-Info.plist │ ├── PullToRefreshAnimationTests.m │ └── en.lproj │ └── InfoPlist.strings └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 / OS X Source projects 3 | # 4 | # Version 2.0 5 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 6 | # 7 | # 2013 updates: 8 | # - fixed the broken "save personal Schemes" 9 | # 10 | # NB: if you are storing "built" products, this WILL NOT WORK, 11 | # and you should use a different .gitignore (or none at all) 12 | # This file is for SOURCE projects, where there are many extra 13 | # files that we want to exclude 14 | # 15 | ######################### 16 | 17 | ##### 18 | # OS X temporary files that should never be committed 19 | 20 | .DS_Store 21 | *.swp 22 | *.lock 23 | profile 24 | 25 | 26 | #### 27 | # Xcode temporary files that should never be committed 28 | # 29 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 30 | 31 | *~.nib 32 | 33 | 34 | #### 35 | # Xcode build files - 36 | # 37 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 38 | 39 | DerivedData/ 40 | 41 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 42 | 43 | build/ 44 | 45 | 46 | ##### 47 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 48 | # 49 | # This is complicated: 50 | # 51 | # SOMETIMES you need to put this file in version control. 52 | # Apple designed it poorly - if you use "custom executables", they are 53 | # saved in this file. 54 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 55 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 56 | 57 | *.pbxuser 58 | *.mode1v3 59 | *.mode2v3 60 | *.perspectivev3 61 | # NB: also, whitelist the default ones, some projects need to use these 62 | !default.pbxuser 63 | !default.mode1v3 64 | !default.mode2v3 65 | !default.perspectivev3 66 | 67 | 68 | #### 69 | # Xcode 4 - semi-personal settings 70 | # 71 | # 72 | # OPTION 1: --------------------------------- 73 | # throw away ALL personal settings (including custom schemes! 74 | # - unless they are "shared") 75 | # 76 | # NB: this is exclusive with OPTION 2 below 77 | xcuserdata 78 | 79 | # OPTION 2: --------------------------------- 80 | # get rid of ALL personal settings, but KEEP SOME OF THEM 81 | # - NB: you must manually uncomment the bits you want to keep 82 | # 83 | # NB: this is exclusive with OPTION 1 above 84 | # 85 | #xcuserdata/**/* 86 | 87 | # (requires option 2 above): Personal Schemes 88 | # 89 | #!xcuserdata/**/xcschemes/* 90 | 91 | #### 92 | # XCode 4 workspaces - more detailed 93 | # 94 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 95 | # 96 | # Workspace layout is quite spammy. For reference: 97 | # 98 | # /(root)/ 99 | # /(project-name).xcodeproj/ 100 | # project.pbxproj 101 | # /project.xcworkspace/ 102 | # contents.xcworkspacedata 103 | # /xcuserdata/ 104 | # /(your name)/xcuserdatad/ 105 | # UserInterfaceState.xcuserstate 106 | # /xcsshareddata/ 107 | # /xcschemes/ 108 | # (shared scheme name).xcscheme 109 | # /xcuserdata/ 110 | # /(your name)/xcuserdatad/ 111 | # (private scheme).xcscheme 112 | # xcschememanagement.plist 113 | # 114 | # 115 | 116 | #### 117 | # Xcode 4 - Deprecated classes 118 | # 119 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 120 | # 121 | # We're using source-control, so this is a "feature" that we do not want! 122 | 123 | *.moved-aside 124 | 125 | #### 126 | # Cocoapods: cocoapods.org 127 | # 128 | # Ignoring these files means that whoever uses the code will first have to run: 129 | # pod install 130 | # in the App.xcodeproj directory. 131 | # This ensures the latest dependencies are used. 132 | Pods/ 133 | Podfile.lock 134 | 135 | 136 | #### 137 | # UNKNOWN: recommended by others, but I can't discover what these files are 138 | # 139 | # ...none. Everything is now explained. -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6F245E3E18143C5400D7B823 /* PrimeFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F245E3D18143C5400D7B823 /* PrimeFlowLayout.m */; }; 11 | 6F93D39D180C592B00AF29AC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D39C180C592B00AF29AC /* Foundation.framework */; }; 12 | 6F93D39F180C592B00AF29AC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D39E180C592B00AF29AC /* CoreGraphics.framework */; }; 13 | 6F93D3A1180C592B00AF29AC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D3A0180C592B00AF29AC /* UIKit.framework */; }; 14 | 6F93D3A7180C592B00AF29AC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F93D3A5180C592B00AF29AC /* InfoPlist.strings */; }; 15 | 6F93D3A9180C592B00AF29AC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93D3A8180C592B00AF29AC /* main.m */; }; 16 | 6F93D3AD180C592B00AF29AC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93D3AC180C592B00AF29AC /* AppDelegate.m */; }; 17 | 6F93D3B0180C592B00AF29AC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6F93D3AE180C592B00AF29AC /* Main.storyboard */; }; 18 | 6F93D3B5180C592B00AF29AC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F93D3B4180C592B00AF29AC /* Images.xcassets */; }; 19 | 6F93D3BC180C592B00AF29AC /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D3BB180C592B00AF29AC /* XCTest.framework */; }; 20 | 6F93D3BD180C592B00AF29AC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D39C180C592B00AF29AC /* Foundation.framework */; }; 21 | 6F93D3BE180C592B00AF29AC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D3A0180C592B00AF29AC /* UIKit.framework */; }; 22 | 6F93D3C6180C592B00AF29AC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F93D3C4180C592B00AF29AC /* InfoPlist.strings */; }; 23 | 6F93D3C8180C592B00AF29AC /* PullToRefreshAnimationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93D3C7180C592B00AF29AC /* PullToRefreshAnimationTests.m */; }; 24 | 6F93D3D2180C615500AF29AC /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F93D3D1180C615500AF29AC /* QuartzCore.framework */; }; 25 | 6F93D3D5180C718A00AF29AC /* SampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93D3D4180C718A00AF29AC /* SampleViewController.m */; }; 26 | 6F93D3D8180C720A00AF29AC /* SampleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93D3D7180C720A00AF29AC /* SampleCell.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 6F93D3BF180C592B00AF29AC /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 6F93D391180C592B00AF29AC /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 6F93D398180C592B00AF29AC; 35 | remoteInfo = PullToRefreshAnimation; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 6F245E3C18143C5400D7B823 /* PrimeFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrimeFlowLayout.h; sourceTree = ""; }; 41 | 6F245E3D18143C5400D7B823 /* PrimeFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrimeFlowLayout.m; sourceTree = ""; }; 42 | 6F93D399180C592B00AF29AC /* PullToRefreshAnimation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PullToRefreshAnimation.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 6F93D39C180C592B00AF29AC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 44 | 6F93D39E180C592B00AF29AC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 45 | 6F93D3A0180C592B00AF29AC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 46 | 6F93D3A4180C592B00AF29AC /* PullToRefreshAnimation-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PullToRefreshAnimation-Info.plist"; sourceTree = ""; }; 47 | 6F93D3A6180C592B00AF29AC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 48 | 6F93D3A8180C592B00AF29AC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 6F93D3AA180C592B00AF29AC /* PullToRefreshAnimation-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PullToRefreshAnimation-Prefix.pch"; sourceTree = ""; }; 50 | 6F93D3AB180C592B00AF29AC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 51 | 6F93D3AC180C592B00AF29AC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 52 | 6F93D3AF180C592B00AF29AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 6F93D3B4180C592B00AF29AC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | 6F93D3BA180C592B00AF29AC /* PullToRefreshAnimationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PullToRefreshAnimationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 6F93D3BB180C592B00AF29AC /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 56 | 6F93D3C3180C592B00AF29AC /* PullToRefreshAnimationTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PullToRefreshAnimationTests-Info.plist"; sourceTree = ""; }; 57 | 6F93D3C5180C592B00AF29AC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 58 | 6F93D3C7180C592B00AF29AC /* PullToRefreshAnimationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PullToRefreshAnimationTests.m; sourceTree = ""; }; 59 | 6F93D3D1180C615500AF29AC /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 60 | 6F93D3D3180C718A00AF29AC /* SampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleViewController.h; sourceTree = ""; }; 61 | 6F93D3D4180C718A00AF29AC /* SampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleViewController.m; sourceTree = ""; }; 62 | 6F93D3D6180C720A00AF29AC /* SampleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleCell.h; sourceTree = ""; }; 63 | 6F93D3D7180C720A00AF29AC /* SampleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleCell.m; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 6F93D396180C592B00AF29AC /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 6F93D3D2180C615500AF29AC /* QuartzCore.framework in Frameworks */, 72 | 6F93D39F180C592B00AF29AC /* CoreGraphics.framework in Frameworks */, 73 | 6F93D3A1180C592B00AF29AC /* UIKit.framework in Frameworks */, 74 | 6F93D39D180C592B00AF29AC /* Foundation.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 6F93D3B7180C592B00AF29AC /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 6F93D3BC180C592B00AF29AC /* XCTest.framework in Frameworks */, 83 | 6F93D3BE180C592B00AF29AC /* UIKit.framework in Frameworks */, 84 | 6F93D3BD180C592B00AF29AC /* Foundation.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 6F93D390180C592B00AF29AC = { 92 | isa = PBXGroup; 93 | children = ( 94 | 6F93D3A2180C592B00AF29AC /* PullToRefreshAnimation */, 95 | 6F93D3C1180C592B00AF29AC /* PullToRefreshAnimationTests */, 96 | 6F93D39B180C592B00AF29AC /* Frameworks */, 97 | 6F93D39A180C592B00AF29AC /* Products */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 6F93D39A180C592B00AF29AC /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 6F93D399180C592B00AF29AC /* PullToRefreshAnimation.app */, 105 | 6F93D3BA180C592B00AF29AC /* PullToRefreshAnimationTests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 6F93D39B180C592B00AF29AC /* Frameworks */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 6F93D3D1180C615500AF29AC /* QuartzCore.framework */, 114 | 6F93D39C180C592B00AF29AC /* Foundation.framework */, 115 | 6F93D39E180C592B00AF29AC /* CoreGraphics.framework */, 116 | 6F93D3A0180C592B00AF29AC /* UIKit.framework */, 117 | 6F93D3BB180C592B00AF29AC /* XCTest.framework */, 118 | ); 119 | name = Frameworks; 120 | sourceTree = ""; 121 | }; 122 | 6F93D3A2180C592B00AF29AC /* PullToRefreshAnimation */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 6F93D3AB180C592B00AF29AC /* AppDelegate.h */, 126 | 6F93D3AC180C592B00AF29AC /* AppDelegate.m */, 127 | 6F93D3AE180C592B00AF29AC /* Main.storyboard */, 128 | 6F93D3D3180C718A00AF29AC /* SampleViewController.h */, 129 | 6F93D3D4180C718A00AF29AC /* SampleViewController.m */, 130 | 6F245E3C18143C5400D7B823 /* PrimeFlowLayout.h */, 131 | 6F245E3D18143C5400D7B823 /* PrimeFlowLayout.m */, 132 | 6F93D3D6180C720A00AF29AC /* SampleCell.h */, 133 | 6F93D3D7180C720A00AF29AC /* SampleCell.m */, 134 | 6F93D3B4180C592B00AF29AC /* Images.xcassets */, 135 | 6F93D3A3180C592B00AF29AC /* Supporting Files */, 136 | ); 137 | path = PullToRefreshAnimation; 138 | sourceTree = ""; 139 | }; 140 | 6F93D3A3180C592B00AF29AC /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 6F93D3A4180C592B00AF29AC /* PullToRefreshAnimation-Info.plist */, 144 | 6F93D3A5180C592B00AF29AC /* InfoPlist.strings */, 145 | 6F93D3A8180C592B00AF29AC /* main.m */, 146 | 6F93D3AA180C592B00AF29AC /* PullToRefreshAnimation-Prefix.pch */, 147 | ); 148 | name = "Supporting Files"; 149 | sourceTree = ""; 150 | }; 151 | 6F93D3C1180C592B00AF29AC /* PullToRefreshAnimationTests */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 6F93D3C7180C592B00AF29AC /* PullToRefreshAnimationTests.m */, 155 | 6F93D3C2180C592B00AF29AC /* Supporting Files */, 156 | ); 157 | path = PullToRefreshAnimationTests; 158 | sourceTree = ""; 159 | }; 160 | 6F93D3C2180C592B00AF29AC /* Supporting Files */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 6F93D3C3180C592B00AF29AC /* PullToRefreshAnimationTests-Info.plist */, 164 | 6F93D3C4180C592B00AF29AC /* InfoPlist.strings */, 165 | ); 166 | name = "Supporting Files"; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXGroup section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | 6F93D398180C592B00AF29AC /* PullToRefreshAnimation */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 6F93D3CB180C592B00AF29AC /* Build configuration list for PBXNativeTarget "PullToRefreshAnimation" */; 175 | buildPhases = ( 176 | 6F93D395180C592B00AF29AC /* Sources */, 177 | 6F93D396180C592B00AF29AC /* Frameworks */, 178 | 6F93D397180C592B00AF29AC /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = PullToRefreshAnimation; 185 | productName = PullToRefreshAnimation; 186 | productReference = 6F93D399180C592B00AF29AC /* PullToRefreshAnimation.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | 6F93D3B9180C592B00AF29AC /* PullToRefreshAnimationTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 6F93D3CE180C592B00AF29AC /* Build configuration list for PBXNativeTarget "PullToRefreshAnimationTests" */; 192 | buildPhases = ( 193 | 6F93D3B6180C592B00AF29AC /* Sources */, 194 | 6F93D3B7180C592B00AF29AC /* Frameworks */, 195 | 6F93D3B8180C592B00AF29AC /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 6F93D3C0180C592B00AF29AC /* PBXTargetDependency */, 201 | ); 202 | name = PullToRefreshAnimationTests; 203 | productName = PullToRefreshAnimationTests; 204 | productReference = 6F93D3BA180C592B00AF29AC /* PullToRefreshAnimationTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 6F93D391180C592B00AF29AC /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastUpgradeCheck = 0500; 214 | ORGANIZATIONNAME = Skype; 215 | TargetAttributes = { 216 | 6F93D3B9180C592B00AF29AC = { 217 | TestTargetID = 6F93D398180C592B00AF29AC; 218 | }; 219 | }; 220 | }; 221 | buildConfigurationList = 6F93D394180C592B00AF29AC /* Build configuration list for PBXProject "PullToRefreshAnimation" */; 222 | compatibilityVersion = "Xcode 3.2"; 223 | developmentRegion = English; 224 | hasScannedForEncodings = 0; 225 | knownRegions = ( 226 | en, 227 | Base, 228 | ); 229 | mainGroup = 6F93D390180C592B00AF29AC; 230 | productRefGroup = 6F93D39A180C592B00AF29AC /* Products */; 231 | projectDirPath = ""; 232 | projectRoot = ""; 233 | targets = ( 234 | 6F93D398180C592B00AF29AC /* PullToRefreshAnimation */, 235 | 6F93D3B9180C592B00AF29AC /* PullToRefreshAnimationTests */, 236 | ); 237 | }; 238 | /* End PBXProject section */ 239 | 240 | /* Begin PBXResourcesBuildPhase section */ 241 | 6F93D397180C592B00AF29AC /* Resources */ = { 242 | isa = PBXResourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 6F93D3B5180C592B00AF29AC /* Images.xcassets in Resources */, 246 | 6F93D3A7180C592B00AF29AC /* InfoPlist.strings in Resources */, 247 | 6F93D3B0180C592B00AF29AC /* Main.storyboard in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 6F93D3B8180C592B00AF29AC /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | 6F93D3C6180C592B00AF29AC /* InfoPlist.strings in Resources */, 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | /* End PBXResourcesBuildPhase section */ 260 | 261 | /* Begin PBXSourcesBuildPhase section */ 262 | 6F93D395180C592B00AF29AC /* Sources */ = { 263 | isa = PBXSourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 6F93D3D8180C720A00AF29AC /* SampleCell.m in Sources */, 267 | 6F245E3E18143C5400D7B823 /* PrimeFlowLayout.m in Sources */, 268 | 6F93D3AD180C592B00AF29AC /* AppDelegate.m in Sources */, 269 | 6F93D3D5180C718A00AF29AC /* SampleViewController.m in Sources */, 270 | 6F93D3A9180C592B00AF29AC /* main.m in Sources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | 6F93D3B6180C592B00AF29AC /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 6F93D3C8180C592B00AF29AC /* PullToRefreshAnimationTests.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXSourcesBuildPhase section */ 283 | 284 | /* Begin PBXTargetDependency section */ 285 | 6F93D3C0180C592B00AF29AC /* PBXTargetDependency */ = { 286 | isa = PBXTargetDependency; 287 | target = 6F93D398180C592B00AF29AC /* PullToRefreshAnimation */; 288 | targetProxy = 6F93D3BF180C592B00AF29AC /* PBXContainerItemProxy */; 289 | }; 290 | /* End PBXTargetDependency section */ 291 | 292 | /* Begin PBXVariantGroup section */ 293 | 6F93D3A5180C592B00AF29AC /* InfoPlist.strings */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 6F93D3A6180C592B00AF29AC /* en */, 297 | ); 298 | name = InfoPlist.strings; 299 | sourceTree = ""; 300 | }; 301 | 6F93D3AE180C592B00AF29AC /* Main.storyboard */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 6F93D3AF180C592B00AF29AC /* Base */, 305 | ); 306 | name = Main.storyboard; 307 | sourceTree = ""; 308 | }; 309 | 6F93D3C4180C592B00AF29AC /* InfoPlist.strings */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 6F93D3C5180C592B00AF29AC /* en */, 313 | ); 314 | name = InfoPlist.strings; 315 | sourceTree = ""; 316 | }; 317 | /* End PBXVariantGroup section */ 318 | 319 | /* Begin XCBuildConfiguration section */ 320 | 6F93D3C9180C592B00AF29AC /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 326 | CLANG_CXX_LIBRARY = "libc++"; 327 | CLANG_ENABLE_MODULES = YES; 328 | CLANG_ENABLE_OBJC_ARC = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_CONSTANT_CONVERSION = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | GCC_C_LANGUAGE_STANDARD = gnu99; 340 | GCC_DYNAMIC_NO_PIC = NO; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | }; 357 | name = Debug; 358 | }; 359 | 6F93D3CA180C592B00AF29AC /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_SEARCH_USER_PATHS = NO; 363 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | COPY_PHASE_STRIP = YES; 378 | ENABLE_NS_ASSERTIONS = NO; 379 | GCC_C_LANGUAGE_STANDARD = gnu99; 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 387 | SDKROOT = iphoneos; 388 | VALIDATE_PRODUCT = YES; 389 | }; 390 | name = Release; 391 | }; 392 | 6F93D3CC180C592B00AF29AC /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 396 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 397 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 398 | GCC_PREFIX_HEADER = "PullToRefreshAnimation/PullToRefreshAnimation-Prefix.pch"; 399 | INFOPLIST_FILE = "PullToRefreshAnimation/PullToRefreshAnimation-Info.plist"; 400 | PRODUCT_NAME = "$(TARGET_NAME)"; 401 | WRAPPER_EXTENSION = app; 402 | }; 403 | name = Debug; 404 | }; 405 | 6F93D3CD180C592B00AF29AC /* Release */ = { 406 | isa = XCBuildConfiguration; 407 | buildSettings = { 408 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 409 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 410 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 411 | GCC_PREFIX_HEADER = "PullToRefreshAnimation/PullToRefreshAnimation-Prefix.pch"; 412 | INFOPLIST_FILE = "PullToRefreshAnimation/PullToRefreshAnimation-Info.plist"; 413 | PRODUCT_NAME = "$(TARGET_NAME)"; 414 | WRAPPER_EXTENSION = app; 415 | }; 416 | name = Release; 417 | }; 418 | 6F93D3CF180C592B00AF29AC /* Debug */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 422 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PullToRefreshAnimation.app/PullToRefreshAnimation"; 423 | FRAMEWORK_SEARCH_PATHS = ( 424 | "$(SDKROOT)/Developer/Library/Frameworks", 425 | "$(inherited)", 426 | "$(DEVELOPER_FRAMEWORKS_DIR)", 427 | ); 428 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 429 | GCC_PREFIX_HEADER = "PullToRefreshAnimation/PullToRefreshAnimation-Prefix.pch"; 430 | GCC_PREPROCESSOR_DEFINITIONS = ( 431 | "DEBUG=1", 432 | "$(inherited)", 433 | ); 434 | INFOPLIST_FILE = "PullToRefreshAnimationTests/PullToRefreshAnimationTests-Info.plist"; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | TEST_HOST = "$(BUNDLE_LOADER)"; 437 | WRAPPER_EXTENSION = xctest; 438 | }; 439 | name = Debug; 440 | }; 441 | 6F93D3D0180C592B00AF29AC /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 445 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PullToRefreshAnimation.app/PullToRefreshAnimation"; 446 | FRAMEWORK_SEARCH_PATHS = ( 447 | "$(SDKROOT)/Developer/Library/Frameworks", 448 | "$(inherited)", 449 | "$(DEVELOPER_FRAMEWORKS_DIR)", 450 | ); 451 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 452 | GCC_PREFIX_HEADER = "PullToRefreshAnimation/PullToRefreshAnimation-Prefix.pch"; 453 | INFOPLIST_FILE = "PullToRefreshAnimationTests/PullToRefreshAnimationTests-Info.plist"; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | TEST_HOST = "$(BUNDLE_LOADER)"; 456 | WRAPPER_EXTENSION = xctest; 457 | }; 458 | name = Release; 459 | }; 460 | /* End XCBuildConfiguration section */ 461 | 462 | /* Begin XCConfigurationList section */ 463 | 6F93D394180C592B00AF29AC /* Build configuration list for PBXProject "PullToRefreshAnimation" */ = { 464 | isa = XCConfigurationList; 465 | buildConfigurations = ( 466 | 6F93D3C9180C592B00AF29AC /* Debug */, 467 | 6F93D3CA180C592B00AF29AC /* Release */, 468 | ); 469 | defaultConfigurationIsVisible = 0; 470 | defaultConfigurationName = Release; 471 | }; 472 | 6F93D3CB180C592B00AF29AC /* Build configuration list for PBXNativeTarget "PullToRefreshAnimation" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | 6F93D3CC180C592B00AF29AC /* Debug */, 476 | 6F93D3CD180C592B00AF29AC /* Release */, 477 | ); 478 | defaultConfigurationIsVisible = 0; 479 | defaultConfigurationName = Release; 480 | }; 481 | 6F93D3CE180C592B00AF29AC /* Build configuration list for PBXNativeTarget "PullToRefreshAnimationTests" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | 6F93D3CF180C592B00AF29AC /* Debug */, 485 | 6F93D3D0180C592B00AF29AC /* Release */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | /* End XCConfigurationList section */ 491 | }; 492 | rootObject = 6F93D391180C592B00AF29AC /* Project object */; 493 | } 494 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/PrimeFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrimeFlowLayout.h 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/20/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | 10 | // This class has nothing to do with the animation, it's just here to make the demo pretty. 11 | 12 | 13 | #import 14 | 15 | @interface PrimeFlowLayout : UICollectionViewFlowLayout 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/PrimeFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // PrimeFlowLayout.m 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/20/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | 10 | // This class has nothing to do with the animation, it's just here to make the demo pretty. 11 | 12 | 13 | #import "PrimeFlowLayout.h" 14 | 15 | @implementation PrimeFlowLayout 16 | 17 | - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath 18 | { 19 | UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; 20 | 21 | CATransform3D transform = CATransform3DIdentity; 22 | transform.m34 = -1./800.; 23 | transform = CATransform3DRotate(transform, M_PI_2, -1, 0, 0); 24 | transform = CATransform3DScale(transform, .8, .8, .8); 25 | attributes.transform3D = transform; 26 | 27 | return attributes; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/PullToRefreshAnimation-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.skype.test.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/PullToRefreshAnimation-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/SampleCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SampleCell.h 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | 10 | // This class has nothing to do with the animation, it's just here to make the demo pretty. 11 | 12 | 13 | #import 14 | 15 | @interface SampleCell : UICollectionViewCell 16 | 17 | @property (nonatomic, weak) IBOutlet UILabel *label; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/SampleCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleCell.m 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | 10 | // This class has nothing to do with the animation, it's just here to make the demo pretty. 11 | 12 | 13 | #import "SampleCell.h" 14 | 15 | @implementation SampleCell 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/SampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SampleViewController.h 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SampleViewController : UICollectionViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimation/SampleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleViewController.m 3 | // PullToRefreshAnimation 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | #import "SampleViewController.h" 10 | #import "SampleCell.h" 11 | #import 12 | 13 | @interface SampleViewController () 14 | 15 | /** The sample data (not related to the animation) */ 16 | @property (strong) NSArray *primes; 17 | 18 | /** The layer that is animated as the user pulls down */ 19 | @property (strong) CAShapeLayer *pullToRefreshShape; 20 | 21 | /** The layer that is animated as the app is loading more data */ 22 | @property (strong) CAShapeLayer *loadingShape; 23 | 24 | /** A view that contain both the pull to refresh and loading layers */ 25 | @property (strong) UIView *loadingIndicator; 26 | 27 | /** If new data is currently being loaded */ 28 | @property (assign) BOOL isLoading; 29 | 30 | @end 31 | 32 | @implementation SampleViewController 33 | 34 | - (void)viewDidLoad 35 | { 36 | [super viewDidLoad]; 37 | 38 | self.primes = @[]; 39 | self.isLoading = YES; 40 | 41 | [self setupLoadingIndicator]; 42 | 43 | [self.pullToRefreshShape addAnimation:[self pullDownAnimation] 44 | forKey:@"Write 'Load' as you drag down"]; 45 | 46 | [self fetchMoreDataWithCompletion:^{ 47 | self.isLoading = NO; 48 | }]; 49 | } 50 | 51 | #pragma mark - Pull down 52 | 53 | /** 54 | This is the magic of the entire 55 | */ 56 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 57 | { 58 | CGFloat offset = scrollView.contentOffset.y+scrollView.contentInset.top; 59 | if (offset <= 0.0 && !self.isLoading && [self isViewLoaded]) { 60 | CGFloat startLoadingThreshold = 60.0; 61 | CGFloat fractionDragged = -offset/startLoadingThreshold; 62 | 63 | self.pullToRefreshShape.timeOffset = MIN(1.0, fractionDragged); 64 | 65 | if (fractionDragged >= 1.0) { 66 | [self startLoading]; 67 | } 68 | } 69 | } 70 | 71 | #pragma mark - Animation setup 72 | 73 | /** 74 | This is the animation that is controlled using timeOffset when the user pulls down 75 | */ 76 | - (CAAnimation *)pullDownAnimation 77 | { 78 | // Text is drawn by stroking the path from 0% to 100% 79 | CABasicAnimation *writeText = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 80 | writeText.fromValue = @0; 81 | writeText.toValue = @1; 82 | 83 | // The layer is moved up so that the larger loading layer can fit above the cells 84 | CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"position.y"]; 85 | move.byValue = @(-22); 86 | move.toValue = @0; 87 | 88 | CAAnimationGroup *group = [CAAnimationGroup animation]; 89 | group.duration = 1.0; // For convenience when using timeOffset to control the animation 90 | group.animations = @[writeText, move]; 91 | 92 | return group; 93 | } 94 | 95 | /** 96 | The loading animation is quickly drawing the last the letters (ing) 97 | */ 98 | - (CAAnimation *)loadingAnimation 99 | { 100 | CABasicAnimation *write2 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 101 | write2.fromValue = @0; 102 | write2.toValue = @1; 103 | write2.fillMode = kCAFillModeBoth; 104 | write2.removedOnCompletion = NO; 105 | write2.duration = 0.4; 106 | return write2; 107 | } 108 | 109 | #pragma mark - 110 | 111 | /** 112 | Two stroked shape layers that form the text 'Load' and 'ing' 113 | */ 114 | - (void)setupLoadingIndicator 115 | { 116 | CAShapeLayer *loadShape = [CAShapeLayer layer]; 117 | loadShape.path = [self loadPath]; 118 | CAShapeLayer *ingShape = [CAShapeLayer layer]; 119 | ingShape.path = [self ingPath]; 120 | 121 | UIView *loadingIndicator = [[UIView alloc] initWithFrame:CGRectMake(0, -45, 230, 70)]; 122 | [self.collectionView addSubview:loadingIndicator]; 123 | self.loadingIndicator = loadingIndicator; 124 | 125 | for (CAShapeLayer *shape in @[loadShape, ingShape]) { 126 | shape.strokeColor = [UIColor blackColor].CGColor; 127 | shape.fillColor = [UIColor clearColor].CGColor; 128 | shape.lineCap = kCALineCapRound; 129 | shape.lineJoin = kCALineJoinRound; 130 | shape.lineWidth = 5.0; 131 | shape.position = CGPointMake(75, 0); 132 | 133 | shape.strokeEnd = .0; 134 | 135 | [loadingIndicator.layer addSublayer:shape]; 136 | } 137 | 138 | loadShape.speed = 0; // pull to refresh layer is paused here 139 | 140 | self.pullToRefreshShape = loadShape; 141 | self.loadingShape = ingShape; 142 | } 143 | 144 | #pragma mark - Loading 145 | 146 | /** 147 | Start the loading animation and load more data 148 | */ 149 | - (void)startLoading 150 | { 151 | self.isLoading = YES; 152 | 153 | // start the loading animation 154 | [self.loadingShape addAnimation:[self loadingAnimation] 155 | forKey:@"Write that word"]; 156 | 157 | CGFloat contentInset = self.collectionView.contentInset.top; 158 | // inset the top to keep the loading indicator on screen 159 | self.collectionView.contentInset = UIEdgeInsetsMake(contentInset+CGRectGetHeight(self.loadingIndicator.frame), 0, 0, 0); 160 | self.collectionView.scrollEnabled = NO; // no further scrolling 161 | 162 | [self loadMoreDataWithAnimation:^{ 163 | // during the reload animation (where new cells are inserted) 164 | self.collectionView.contentInset = UIEdgeInsetsMake(contentInset, 0, 0, 0); 165 | self.loadingIndicator.alpha = 0.0; 166 | } completion:^{ 167 | // reset everything 168 | [self.loadingShape removeAllAnimations]; 169 | self.loadingIndicator.alpha = 1.0; 170 | self.collectionView.scrollEnabled = YES; 171 | self.pullToRefreshShape.timeOffset = 0.0; // back to the start 172 | self.isLoading = NO; 173 | }]; 174 | } 175 | 176 | 177 | /** 178 | @note You shouldn't stall data fetching like this in a real app ;) 179 | */ 180 | - (void)loadMoreDataWithAnimation:(void (^)())animation completion:(void (^)())completion 181 | { 182 | // I had to cheat a little because calculating 6 new primes is quite fast ;) 183 | double delayInSeconds = 0.8; 184 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 185 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 186 | 187 | [UIView animateWithDuration:0.3 animations:animation]; 188 | 189 | [self fetchMoreDataWithCompletion:completion]; 190 | 191 | }); 192 | 193 | } 194 | 195 | #pragma mark - Collection View methods 196 | 197 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 198 | { 199 | return 1; 200 | } 201 | 202 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 203 | { 204 | return self.primes.count; 205 | } 206 | 207 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 208 | cellForItemAtIndexPath:(NSIndexPath *)indexPath 209 | { 210 | SampleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; 211 | cell.label.text = [NSString stringWithFormat:@"%@", self.primes[indexPath.row]]; 212 | return cell; 213 | } 214 | 215 | #pragma mark - 216 | 217 | /** 218 | For conveience so that new data is both calculated and inserted into the collection 219 | before the completion block is run (after the insertions) 220 | */ 221 | - (void)fetchMoreDataWithCompletion:(void (^)())completion 222 | { 223 | [self findMorePrimesWithCompletion:^(NSArray *newPrimes, NSArray *indexPaths) { 224 | 225 | NSArray *reverseNewPrimes = [[newPrimes reverseObjectEnumerator] allObjects]; 226 | 227 | [self.collectionView performBatchUpdates:^{ 228 | 229 | self.primes = [reverseNewPrimes arrayByAddingObjectsFromArray:self.primes]; 230 | [self.collectionView insertItemsAtIndexPaths:indexPaths]; 231 | } completion:^(BOOL finished) { 232 | if (completion != NULL) { 233 | completion(); 234 | } 235 | }]; 236 | }]; 237 | } 238 | 239 | /** 240 | This method calclates N new primes after the ones that is already known and returns the 241 | new primes in the array passed into the completion block. 242 | 243 | This is not related to the animation at all. I just wanted a more interesting data source. 244 | @param completion The block that will be called when the new primes have been calculated 245 | */ 246 | - (void)findMorePrimesWithCompletion:(void (^)(NSArray *newPrimes, NSArray *indexPaths))completion 247 | { 248 | NSArray *knownPrimes = [[[self.primes reverseObjectEnumerator] allObjects] copy]; 249 | 250 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 251 | NSMutableArray *primesFound = [NSMutableArray new]; 252 | NSMutableArray *indexPaths = [NSMutableArray new]; 253 | 254 | NSInteger number = [[knownPrimes lastObject] integerValue]; 255 | NSInteger amountFound = 0; 256 | NSInteger numberOfPrimesToFetch = 9; 257 | NSInteger *prims = malloc(sizeof(NSInteger) * (knownPrimes.count+numberOfPrimesToFetch)); 258 | if (number == 0) { 259 | [primesFound addObject:@(2)]; 260 | [indexPaths addObject:[NSIndexPath indexPathForItem:0 inSection:0]]; 261 | prims[amountFound++] = 2; 262 | number = 3; // after first prime 263 | } else { 264 | number++; 265 | for (NSInteger i=0; i 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimationTests/PullToRefreshAnimationTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.skype.test.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimationTests/PullToRefreshAnimationTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PullToRefreshAnimationTests.m 3 | // PullToRefreshAnimationTests 4 | // 5 | // Created by David Rönnqvist on 10/14/13. 6 | // Copyright (c) 2013 Skype. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PullToRefreshAnimationTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PullToRefreshAnimationTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /PullToRefreshAnimation/PullToRefreshAnimationTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | blogpost-codesample-PullToRefresh 2 | ================================= 3 | 4 | A custom pull to refresh animation where timeOffset is used to control the timing as you drag down 5 | 6 | This is the sample project used in [this blog post](http://ronnqvi.st/controlling-animation-timing/) 7 | 8 | --------- 9 | 10 | The point of this project is to show how you can control animation timing. The animation being controlled looks like this 11 | 12 | ![Direct control over the animation timing using scroll events](http://ronnqvi.st/images/peak.gif) 13 | --------------------------------------------------------------------------------