├── .gitignore ├── README.md ├── TWRefresh.xcodeproj └── project.pbxproj ├── TWRefresh ├── Base │ ├── TWRefreshIndicator.h │ ├── TWRefreshView.h │ ├── TWRefreshView.m │ ├── UIScrollView+TWRefresh.h │ └── UIScrollView+TWRefresh.m ├── Implements │ ├── TWRefresh.h │ ├── TWRefreshCollectionView.h │ ├── TWRefreshCollectionView.m │ ├── TWRefreshTableView.h │ ├── TWRefreshTableView.m │ └── TWRefreshType.h ├── Indicators │ ├── TWRefreshIndicatorView.h │ └── TWRefreshIndicatorView.m ├── Info.plist ├── TWRefresh.h └── bugs.txt ├── TWRefreshDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── TWRefreshDemo.xccheckout │ └── xcuserdata │ │ └── Chris.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── Chris.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── TWRefreshDemo.xcscheme │ └── xcschememanagement.plist ├── TWRefreshDemo ├── .DS_Store ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── Default-568@2x.png │ │ └── Default@2x.png │ └── indicator-center-icon.imageset │ │ ├── Contents.json │ │ ├── indicator-center-icon@2.png │ │ └── indicator-center-icon@3.png ├── Info.plist ├── TWRefreshCollectionViewController.h ├── TWRefreshCollectionViewController.m ├── TWRefreshScrollViewController.h ├── TWRefreshScrollViewController.m ├── TWRefreshTableViewController.h ├── TWRefreshTableViewController.m ├── ViewController.h ├── ViewController.m └── main.m ├── TWRefreshDemoTests ├── Info.plist └── TWRefreshDemoTests.m └── TWRefreshTests ├── Info.plist └── TWRefreshTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | // 2 | .DS_Store 3 | TWRefreshDemo/.DS_Store 4 | *.xccheckout 5 | */xcschememanagement.plist 6 | TWRefresh.xcodeproj/xcuserdata/* 7 | TWRefresh.xcodeproj/project.xcworkspace/ 8 | TWRefreshDemo.xcodeproj/project.xcworkspace/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TWRefresh 2 | UIScroll View Pulling Refresh 3 | 4 | #Classes 5 | 6 | Base: 7 | UIScrollView+TWRefresh.h 8 | TWRefreshView.h 9 | TWRefreshIndicator.h 10 | 11 | Indicators: 12 | TWRefreshIndicatorView.h 13 | 14 | Implements: 15 | TWRefreshTableView.h 16 | TWRefreshCollectionView.h 17 | 18 | 19 | # You can easily create your own indicators, just need to implement protocal TWRefreshIndicator 20 | 21 | Example: 22 | 23 | @interface CustomRefreshIndicatorView : UIView <TWRefreshIndicator> 24 | 25 | @end 26 | 27 | @implementation CustomRefreshIndicatorView 28 | 29 | - (void) start { 30 | } 31 | 32 | - (void) stop { 33 | } 34 | 35 | - (void) pullingWithRatio:(CGFloat)ratio { 36 | //The value of ratio range 0.0 to 1.0. 37 | } 38 | 39 | @end 40 | 41 | # API Usage (In UIScrollView+TWRefresh.h) 42 | 43 | // Set refresh Header indicator 44 | - (void) setRefreshHeaderIndicator:(id) indicator; 45 | 46 | // Set refresh Header with indicator class 47 | - (void) setRefreshHeaderWithIndicatorClass:(Class) clazz; 48 | 49 | // Set refresh Footer indicator 50 | - (void) setRefreshFooterIndicator:(id) indicator; 51 | 52 | // Set refresh Footer with indicator class 53 | - (void) setRefreshFooterWithIndicatorClass:(Class) clazz; 54 | 55 | # Set refresh enabled 56 | // Set refresh enabled, sometimes you need to switch refreshable state 57 | - (void) setRefreshEnabled:(BOOL) refreshEnabled; 58 | 59 | // Set refresh enabled, sometimes you need to switch refreshable state 60 | - (void) setRefreshHeaderEnabled:(BOOL) refreshEnabled; 61 | 62 | // Set refresh enabled, sometimes you need to switch refreshable state 63 | - (void) setRefreshFooterEnabled:(BOOL) refreshEnabled; 64 | 65 | # Inheritance 66 | // Header Refresing 67 | - (void) refreshHeader; 68 | 69 | // Footer Refresing 70 | - (void) refreshFooter; 71 | 72 | // Stop Header Refreshing 73 | - (void) stopHeaderRefreshing; 74 | 75 | // Stop Footer Refreshing 76 | - (void) stopFooterRefreshing; 77 | 78 | # Use callback 79 | // Refresh header callback 80 | - (void) setRefreshHeaderCallback:(void (^)())refreshHeaderCallback; 81 | 82 | // Refresh footer callback 83 | - (void) setRefreshFooterCallback:(void (^)())refreshFooterCallback; 84 | 85 | -------------------------------------------------------------------------------- /TWRefresh.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 834877FC1C9D3A5100FE3021 /* TWRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = 834877FB1C9D3A5100FE3021 /* TWRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 834878031C9D3A5100FE3021 /* TWRefresh.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 834877F81C9D3A5100FE3021 /* TWRefresh.framework */; }; 12 | 834878081C9D3A5100FE3021 /* TWRefreshTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 834878071C9D3A5100FE3021 /* TWRefreshTests.m */; }; 13 | 834878211C9D418E00FE3021 /* TWRefreshIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 834878131C9D418E00FE3021 /* TWRefreshIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 834878221C9D418E00FE3021 /* TWRefreshView.h in Headers */ = {isa = PBXBuildFile; fileRef = 834878141C9D418E00FE3021 /* TWRefreshView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 834878231C9D418E00FE3021 /* TWRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 834878151C9D418E00FE3021 /* TWRefreshView.m */; }; 16 | 834878241C9D418E00FE3021 /* UIScrollView+TWRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = 834878161C9D418E00FE3021 /* UIScrollView+TWRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 834878251C9D418E00FE3021 /* UIScrollView+TWRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 834878171C9D418E00FE3021 /* UIScrollView+TWRefresh.m */; }; 18 | 834878261C9D418E00FE3021 /* TWRefreshType.h in Headers */ = {isa = PBXBuildFile; fileRef = 834878191C9D418E00FE3021 /* TWRefreshType.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 834878271C9D418E00FE3021 /* TWRefreshCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8348781A1C9D418E00FE3021 /* TWRefreshCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 834878281C9D418E00FE3021 /* TWRefreshCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8348781B1C9D418E00FE3021 /* TWRefreshCollectionView.m */; }; 21 | 834878291C9D418E00FE3021 /* TWRefreshTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8348781C1C9D418E00FE3021 /* TWRefreshTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | 8348782A1C9D418E00FE3021 /* TWRefreshTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8348781D1C9D418E00FE3021 /* TWRefreshTableView.m */; }; 23 | 8348782B1C9D418E00FE3021 /* TWRefreshIndicatorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8348781F1C9D418E00FE3021 /* TWRefreshIndicatorView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | 8348782C1C9D418E00FE3021 /* TWRefreshIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 834878201C9D418E00FE3021 /* TWRefreshIndicatorView.m */; }; 25 | 834878401C9D8A7C00FE3021 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8348783F1C9D8A7C00FE3021 /* UIKit.framework */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 834878041C9D3A5100FE3021 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 834877EF1C9D3A5100FE3021 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 834877F71C9D3A5100FE3021; 34 | remoteInfo = TWRefresh; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 834877F81C9D3A5100FE3021 /* TWRefresh.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TWRefresh.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 834877FB1C9D3A5100FE3021 /* TWRefresh.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWRefresh.h; sourceTree = ""; }; 41 | 834877FD1C9D3A5100FE3021 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 834878021C9D3A5100FE3021 /* TWRefreshTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TWRefreshTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 834878071C9D3A5100FE3021 /* TWRefreshTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWRefreshTests.m; sourceTree = ""; }; 44 | 834878091C9D3A5100FE3021 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 834878131C9D418E00FE3021 /* TWRefreshIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshIndicator.h; sourceTree = ""; }; 46 | 834878141C9D418E00FE3021 /* TWRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshView.h; sourceTree = ""; }; 47 | 834878151C9D418E00FE3021 /* TWRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshView.m; sourceTree = ""; }; 48 | 834878161C9D418E00FE3021 /* UIScrollView+TWRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+TWRefresh.h"; sourceTree = ""; }; 49 | 834878171C9D418E00FE3021 /* UIScrollView+TWRefresh.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+TWRefresh.m"; sourceTree = ""; }; 50 | 834878191C9D418E00FE3021 /* TWRefreshType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshType.h; sourceTree = ""; }; 51 | 8348781A1C9D418E00FE3021 /* TWRefreshCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshCollectionView.h; sourceTree = ""; }; 52 | 8348781B1C9D418E00FE3021 /* TWRefreshCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshCollectionView.m; sourceTree = ""; }; 53 | 8348781C1C9D418E00FE3021 /* TWRefreshTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshTableView.h; sourceTree = ""; }; 54 | 8348781D1C9D418E00FE3021 /* TWRefreshTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshTableView.m; sourceTree = ""; }; 55 | 8348781F1C9D418E00FE3021 /* TWRefreshIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshIndicatorView.h; sourceTree = ""; }; 56 | 834878201C9D418E00FE3021 /* TWRefreshIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshIndicatorView.m; sourceTree = ""; }; 57 | 8348783F1C9D8A7C00FE3021 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 834877F41C9D3A5100FE3021 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 834878401C9D8A7C00FE3021 /* UIKit.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 834877FF1C9D3A5100FE3021 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | 834878031C9D3A5100FE3021 /* TWRefresh.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | 834877EE1C9D3A5100FE3021 = { 81 | isa = PBXGroup; 82 | children = ( 83 | 834878411C9D8A9900FE3021 /* Frameworks */, 84 | 834877FA1C9D3A5100FE3021 /* TWRefresh */, 85 | 834878061C9D3A5100FE3021 /* TWRefreshTests */, 86 | 834877F91C9D3A5100FE3021 /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 834877F91C9D3A5100FE3021 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 834877F81C9D3A5100FE3021 /* TWRefresh.framework */, 94 | 834878021C9D3A5100FE3021 /* TWRefreshTests.xctest */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | 834877FA1C9D3A5100FE3021 /* TWRefresh */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 834878121C9D418E00FE3021 /* Base */, 103 | 834878181C9D418E00FE3021 /* Implements */, 104 | 8348781E1C9D418E00FE3021 /* Indicators */, 105 | 834877FB1C9D3A5100FE3021 /* TWRefresh.h */, 106 | 834877FD1C9D3A5100FE3021 /* Info.plist */, 107 | ); 108 | path = TWRefresh; 109 | sourceTree = ""; 110 | }; 111 | 834878061C9D3A5100FE3021 /* TWRefreshTests */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 834878071C9D3A5100FE3021 /* TWRefreshTests.m */, 115 | 834878091C9D3A5100FE3021 /* Info.plist */, 116 | ); 117 | path = TWRefreshTests; 118 | sourceTree = ""; 119 | }; 120 | 834878121C9D418E00FE3021 /* Base */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 834878131C9D418E00FE3021 /* TWRefreshIndicator.h */, 124 | 834878141C9D418E00FE3021 /* TWRefreshView.h */, 125 | 834878151C9D418E00FE3021 /* TWRefreshView.m */, 126 | 834878161C9D418E00FE3021 /* UIScrollView+TWRefresh.h */, 127 | 834878171C9D418E00FE3021 /* UIScrollView+TWRefresh.m */, 128 | ); 129 | path = Base; 130 | sourceTree = ""; 131 | }; 132 | 834878181C9D418E00FE3021 /* Implements */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 834878191C9D418E00FE3021 /* TWRefreshType.h */, 136 | 8348781A1C9D418E00FE3021 /* TWRefreshCollectionView.h */, 137 | 8348781B1C9D418E00FE3021 /* TWRefreshCollectionView.m */, 138 | 8348781C1C9D418E00FE3021 /* TWRefreshTableView.h */, 139 | 8348781D1C9D418E00FE3021 /* TWRefreshTableView.m */, 140 | ); 141 | path = Implements; 142 | sourceTree = ""; 143 | }; 144 | 8348781E1C9D418E00FE3021 /* Indicators */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 8348781F1C9D418E00FE3021 /* TWRefreshIndicatorView.h */, 148 | 834878201C9D418E00FE3021 /* TWRefreshIndicatorView.m */, 149 | ); 150 | path = Indicators; 151 | sourceTree = ""; 152 | }; 153 | 834878411C9D8A9900FE3021 /* Frameworks */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 8348783F1C9D8A7C00FE3021 /* UIKit.framework */, 157 | ); 158 | name = Frameworks; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXHeadersBuildPhase section */ 164 | 834877F51C9D3A5100FE3021 /* Headers */ = { 165 | isa = PBXHeadersBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | 834877FC1C9D3A5100FE3021 /* TWRefresh.h in Headers */, 169 | 834878261C9D418E00FE3021 /* TWRefreshType.h in Headers */, 170 | 8348782B1C9D418E00FE3021 /* TWRefreshIndicatorView.h in Headers */, 171 | 834878221C9D418E00FE3021 /* TWRefreshView.h in Headers */, 172 | 834878241C9D418E00FE3021 /* UIScrollView+TWRefresh.h in Headers */, 173 | 834878211C9D418E00FE3021 /* TWRefreshIndicator.h in Headers */, 174 | 834878291C9D418E00FE3021 /* TWRefreshTableView.h in Headers */, 175 | 834878271C9D418E00FE3021 /* TWRefreshCollectionView.h in Headers */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXHeadersBuildPhase section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 834877F71C9D3A5100FE3021 /* TWRefresh */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 8348780C1C9D3A5100FE3021 /* Build configuration list for PBXNativeTarget "TWRefresh" */; 185 | buildPhases = ( 186 | 834877F31C9D3A5100FE3021 /* Sources */, 187 | 834877F41C9D3A5100FE3021 /* Frameworks */, 188 | 834877F51C9D3A5100FE3021 /* Headers */, 189 | 834877F61C9D3A5100FE3021 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = TWRefresh; 196 | productName = TWRefresh; 197 | productReference = 834877F81C9D3A5100FE3021 /* TWRefresh.framework */; 198 | productType = "com.apple.product-type.framework"; 199 | }; 200 | 834878011C9D3A5100FE3021 /* TWRefreshTests */ = { 201 | isa = PBXNativeTarget; 202 | buildConfigurationList = 8348780F1C9D3A5100FE3021 /* Build configuration list for PBXNativeTarget "TWRefreshTests" */; 203 | buildPhases = ( 204 | 834877FE1C9D3A5100FE3021 /* Sources */, 205 | 834877FF1C9D3A5100FE3021 /* Frameworks */, 206 | 834878001C9D3A5100FE3021 /* Resources */, 207 | ); 208 | buildRules = ( 209 | ); 210 | dependencies = ( 211 | 834878051C9D3A5100FE3021 /* PBXTargetDependency */, 212 | ); 213 | name = TWRefreshTests; 214 | productName = TWRefreshTests; 215 | productReference = 834878021C9D3A5100FE3021 /* TWRefreshTests.xctest */; 216 | productType = "com.apple.product-type.bundle.unit-test"; 217 | }; 218 | /* End PBXNativeTarget section */ 219 | 220 | /* Begin PBXProject section */ 221 | 834877EF1C9D3A5100FE3021 /* Project object */ = { 222 | isa = PBXProject; 223 | attributes = { 224 | LastSwiftUpdateCheck = 0730; 225 | LastUpgradeCheck = 0720; 226 | ORGANIZATIONNAME = iEasyNote; 227 | TargetAttributes = { 228 | 834877F71C9D3A5100FE3021 = { 229 | CreatedOnToolsVersion = 7.2.1; 230 | }; 231 | 834878011C9D3A5100FE3021 = { 232 | CreatedOnToolsVersion = 7.2.1; 233 | }; 234 | }; 235 | }; 236 | buildConfigurationList = 834877F21C9D3A5100FE3021 /* Build configuration list for PBXProject "TWRefresh" */; 237 | compatibilityVersion = "Xcode 3.2"; 238 | developmentRegion = English; 239 | hasScannedForEncodings = 0; 240 | knownRegions = ( 241 | en, 242 | ); 243 | mainGroup = 834877EE1C9D3A5100FE3021; 244 | productRefGroup = 834877F91C9D3A5100FE3021 /* Products */; 245 | projectDirPath = ""; 246 | projectRoot = ""; 247 | targets = ( 248 | 834877F71C9D3A5100FE3021 /* TWRefresh */, 249 | 834878011C9D3A5100FE3021 /* TWRefreshTests */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXResourcesBuildPhase section */ 255 | 834877F61C9D3A5100FE3021 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | 834878001C9D3A5100FE3021 /* Resources */ = { 263 | isa = PBXResourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXResourcesBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 834877F31C9D3A5100FE3021 /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 8348782A1C9D418E00FE3021 /* TWRefreshTableView.m in Sources */, 277 | 834878281C9D418E00FE3021 /* TWRefreshCollectionView.m in Sources */, 278 | 834878251C9D418E00FE3021 /* UIScrollView+TWRefresh.m in Sources */, 279 | 834878231C9D418E00FE3021 /* TWRefreshView.m in Sources */, 280 | 8348782C1C9D418E00FE3021 /* TWRefreshIndicatorView.m in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 834877FE1C9D3A5100FE3021 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 834878081C9D3A5100FE3021 /* TWRefreshTests.m in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXSourcesBuildPhase section */ 293 | 294 | /* Begin PBXTargetDependency section */ 295 | 834878051C9D3A5100FE3021 /* PBXTargetDependency */ = { 296 | isa = PBXTargetDependency; 297 | target = 834877F71C9D3A5100FE3021 /* TWRefresh */; 298 | targetProxy = 834878041C9D3A5100FE3021 /* PBXContainerItemProxy */; 299 | }; 300 | /* End PBXTargetDependency section */ 301 | 302 | /* Begin XCBuildConfiguration section */ 303 | 8348780A1C9D3A5100FE3021 /* Debug */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BOOL_CONVERSION = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INT_CONVERSION = YES; 317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 321 | COPY_PHASE_STRIP = NO; 322 | CURRENT_PROJECT_VERSION = 1; 323 | DEBUG_INFORMATION_FORMAT = dwarf; 324 | ENABLE_STRICT_OBJC_MSGSEND = YES; 325 | ENABLE_TESTABILITY = YES; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_DYNAMIC_NO_PIC = NO; 328 | GCC_NO_COMMON_BLOCKS = YES; 329 | GCC_OPTIMIZATION_LEVEL = 0; 330 | GCC_PREPROCESSOR_DEFINITIONS = ( 331 | "DEBUG=1", 332 | "$(inherited)", 333 | ); 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 341 | MTL_ENABLE_DEBUG_INFO = YES; 342 | ONLY_ACTIVE_ARCH = YES; 343 | SDKROOT = iphoneos; 344 | TARGETED_DEVICE_FAMILY = "1,2"; 345 | VERSIONING_SYSTEM = "apple-generic"; 346 | VERSION_INFO_PREFIX = ""; 347 | }; 348 | name = Debug; 349 | }; 350 | 8348780B1C9D3A5100FE3021 /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ALWAYS_SEARCH_USER_PATHS = NO; 354 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 355 | CLANG_CXX_LIBRARY = "libc++"; 356 | CLANG_ENABLE_MODULES = YES; 357 | CLANG_ENABLE_OBJC_ARC = YES; 358 | CLANG_WARN_BOOL_CONVERSION = YES; 359 | CLANG_WARN_CONSTANT_CONVERSION = YES; 360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 361 | CLANG_WARN_EMPTY_BODY = YES; 362 | CLANG_WARN_ENUM_CONVERSION = YES; 363 | CLANG_WARN_INT_CONVERSION = YES; 364 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 365 | CLANG_WARN_UNREACHABLE_CODE = YES; 366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | COPY_PHASE_STRIP = NO; 369 | CURRENT_PROJECT_VERSION = 1; 370 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 371 | ENABLE_NS_ASSERTIONS = NO; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu99; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 382 | MTL_ENABLE_DEBUG_INFO = NO; 383 | SDKROOT = iphoneos; 384 | TARGETED_DEVICE_FAMILY = "1,2"; 385 | VALIDATE_PRODUCT = YES; 386 | VERSIONING_SYSTEM = "apple-generic"; 387 | VERSION_INFO_PREFIX = ""; 388 | }; 389 | name = Release; 390 | }; 391 | 8348780D1C9D3A5100FE3021 /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; 395 | CLANG_ENABLE_MODULES = YES; 396 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 397 | DEFINES_MODULE = YES; 398 | DEVELOPMENT_TEAM = ""; 399 | DYLIB_COMPATIBILITY_VERSION = 1; 400 | DYLIB_CURRENT_VERSION = 1; 401 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 402 | INFOPLIST_FILE = TWRefresh/Info.plist; 403 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 404 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 406 | PRODUCT_BUNDLE_IDENTIFIER = com.ieasynote.TWRefresh; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | SKIP_INSTALL = YES; 409 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 410 | }; 411 | name = Debug; 412 | }; 413 | 8348780E1C9D3A5100FE3021 /* Release */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; 417 | CLANG_ENABLE_MODULES = YES; 418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 419 | DEFINES_MODULE = YES; 420 | DYLIB_COMPATIBILITY_VERSION = 1; 421 | DYLIB_CURRENT_VERSION = 1; 422 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 423 | INFOPLIST_FILE = TWRefresh/Info.plist; 424 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 425 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 426 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 427 | PRODUCT_BUNDLE_IDENTIFIER = com.ieasynote.TWRefresh; 428 | PRODUCT_NAME = "$(TARGET_NAME)"; 429 | SKIP_INSTALL = YES; 430 | }; 431 | name = Release; 432 | }; 433 | 834878101C9D3A5100FE3021 /* Debug */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | INFOPLIST_FILE = TWRefreshTests/Info.plist; 437 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 438 | PRODUCT_BUNDLE_IDENTIFIER = com.ieasynote.TWRefreshTests; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | }; 441 | name = Debug; 442 | }; 443 | 834878111C9D3A5100FE3021 /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | INFOPLIST_FILE = TWRefreshTests/Info.plist; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 448 | PRODUCT_BUNDLE_IDENTIFIER = com.ieasynote.TWRefreshTests; 449 | PRODUCT_NAME = "$(TARGET_NAME)"; 450 | }; 451 | name = Release; 452 | }; 453 | /* End XCBuildConfiguration section */ 454 | 455 | /* Begin XCConfigurationList section */ 456 | 834877F21C9D3A5100FE3021 /* Build configuration list for PBXProject "TWRefresh" */ = { 457 | isa = XCConfigurationList; 458 | buildConfigurations = ( 459 | 8348780A1C9D3A5100FE3021 /* Debug */, 460 | 8348780B1C9D3A5100FE3021 /* Release */, 461 | ); 462 | defaultConfigurationIsVisible = 0; 463 | defaultConfigurationName = Release; 464 | }; 465 | 8348780C1C9D3A5100FE3021 /* Build configuration list for PBXNativeTarget "TWRefresh" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 8348780D1C9D3A5100FE3021 /* Debug */, 469 | 8348780E1C9D3A5100FE3021 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 8348780F1C9D3A5100FE3021 /* Build configuration list for PBXNativeTarget "TWRefreshTests" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 834878101C9D3A5100FE3021 /* Debug */, 478 | 834878111C9D3A5100FE3021 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | /* End XCConfigurationList section */ 484 | }; 485 | rootObject = 834877EF1C9D3A5100FE3021 /* Project object */; 486 | } 487 | -------------------------------------------------------------------------------- /TWRefresh/Base/TWRefreshIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshIndicator.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | 25 | @protocol TWRefreshIndicator 26 | 27 | @optional 28 | - (CGFloat)indicatorHeight; 29 | - (void)start; 30 | - (void)stop; 31 | - (void)reset; 32 | - (void)pullingWithRatio:(CGFloat)ratio; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /TWRefresh/Base/TWRefreshView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshView.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | #import "TWRefreshIndicator.h" 25 | 26 | typedef NS_ENUM(NSInteger, TWRefreshState) { 27 | TWRefreshStateUnknown = -1, 28 | TWRefreshStateNormal, 29 | TWRefreshStateReadyToRefresh, 30 | TWRefreshStateRefreshing, 31 | }; 32 | 33 | @interface TWRefreshView : UIView 34 | { 35 | // Scroll view to be pulling refresh 36 | __weak UIScrollView *_scrollView; 37 | 38 | // Indicator 39 | __unsafe_unretained id _indicator; 40 | 41 | // Refresh state 42 | TWRefreshState _state; 43 | 44 | // Scroll view original content inset 45 | UIEdgeInsets _originalContentInset; 46 | 47 | // Refresh inset height added when refreshing 48 | CGFloat _refreshInsetHeight; 49 | } 50 | 51 | @property (nonatomic, assign) TWRefreshState state; 52 | 53 | // Pulling refresh view 54 | @property (nonatomic, assign, readonly) UIScrollView *scrollView; 55 | 56 | // Refresh indicator view 57 | @property (nonatomic, assign) id indicator; 58 | 59 | @end 60 | 61 | @interface TWRefreshHeaderView : TWRefreshView 62 | 63 | @end 64 | 65 | @interface TWRefreshFooterView : TWRefreshView 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /TWRefresh/Base/TWRefreshView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshView.m 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import "TWRefreshView.h" 24 | #import "UIScrollView+TWRefresh.h" 25 | 26 | static NSString *TWRefreshContentOffsetKeyPath = @"contentOffset"; 27 | static NSString *TWRefreshContentInsetKeyPath = @"contentInset"; 28 | static NSString *TWRefreshContentSizeKeyPath = @"contentSize"; 29 | static NSString *TWRefreshFrameKeyPath = @"frame"; 30 | 31 | static CGFloat TWRefreshHeaderViewHeight = 54; 32 | static CGFloat TWRefreshFooterViewHeight = 49; 33 | 34 | @interface TWRefreshView () 35 | // Refresh state, indicator 36 | - (void)setState:(TWRefreshState)state; 37 | - (void)setIndicator:(id)indicator; 38 | 39 | // Content inset 40 | - (void)addRefreshContentInset:(BOOL)animated; 41 | - (void)removeRefreshContentInset:(BOOL)animated; 42 | 43 | - (void)layout; 44 | 45 | // Observer handles 46 | - (void)contentOffsetChanged:(NSDictionary *)change; 47 | - (void)contentInsetChanged:(NSDictionary *)change; 48 | - (void)contentSizeChanged:(NSDictionary *)change; 49 | - (void)contentFrameChanged:(NSDictionary *)change; 50 | @end 51 | 52 | // Observers 53 | @interface TWRefreshView (ObserverMethods) 54 | - (void)addObservers; 55 | - (void)removeObservers; 56 | @end 57 | 58 | @implementation TWRefreshView 59 | 60 | - (id)initWithFrame:(CGRect)frame { 61 | self = [super initWithFrame:frame]; 62 | if (self) { 63 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 64 | } 65 | return self; 66 | } 67 | 68 | - (void)layoutSubviews { 69 | [super layoutSubviews]; 70 | [self layout]; 71 | } 72 | 73 | - (UIScrollView *)scrollView { 74 | return _scrollView; 75 | } 76 | 77 | - (void)willMoveToSuperview:(UIView *)newSuperview { 78 | // Call super method 79 | [super willMoveToSuperview:newSuperview]; 80 | 81 | // Check super view tye 82 | if (!newSuperview || ![newSuperview isKindOfClass:[UIScrollView class]]) { 83 | return; 84 | } 85 | 86 | _scrollView = (UIScrollView *)newSuperview; 87 | 88 | // Work around, just get the correct original content inset 89 | [self performSelector:@selector(initOriginalContentInset) withObject:nil afterDelay:0.01]; 90 | 91 | CGRect r = self.frame; 92 | r.origin.x = 0; 93 | r.size.width = newSuperview.frame.size.width; 94 | self.frame = r; 95 | 96 | //Layout position 97 | [self layout]; 98 | 99 | // If has super view 100 | if (self.superview) { 101 | [self removeObservers]; 102 | } 103 | 104 | // Add required observers 105 | [self addObservers]; 106 | } 107 | 108 | - (void)initOriginalContentInset { 109 | _originalContentInset = _scrollView.contentInset; 110 | } 111 | 112 | - (void)removeFromSuperview { 113 | [self removeObservers]; 114 | [super removeFromSuperview]; 115 | } 116 | 117 | - (void)setIndicator:(id)indicator { 118 | if(_indicator != indicator) { 119 | if (_indicator && [_indicator isKindOfClass:[UIView class]]) { 120 | [((UIView *)_indicator) removeFromSuperview]; 121 | _indicator = nil; 122 | } 123 | _indicator = indicator; 124 | if ([indicator isKindOfClass:[UIView class]]) { 125 | [self addSubview:(UIView *)indicator]; 126 | } 127 | } 128 | } 129 | 130 | - (void)setState:(TWRefreshState)state { 131 | if (state == _state) { 132 | return; 133 | } 134 | _state = state; 135 | if (state == TWRefreshStateRefreshing) { 136 | _refreshInsetHeight = self.frame.size.height; 137 | 138 | // Set content inset 139 | [self addRefreshContentInset:YES]; 140 | 141 | if ([_indicator respondsToSelector:@selector(pullingWithRatio:)]) { 142 | [_indicator pullingWithRatio:1.0]; 143 | } 144 | 145 | // Start indicator 146 | if ([_indicator respondsToSelector:@selector(start)]) { 147 | [_indicator start]; 148 | } 149 | } 150 | else if (state == TWRefreshStateNormal) { 151 | // Set content inset 152 | [self removeRefreshContentInset:YES]; 153 | 154 | // Start indicator 155 | if ([_indicator respondsToSelector:@selector(stop)]) { 156 | [_indicator stop]; 157 | } 158 | } 159 | } 160 | 161 | 162 | // Override by sub classes 163 | - (void)addRefreshContentInset:(BOOL)animated { 164 | // Realization in sub class 165 | } 166 | 167 | - (void)removeRefreshContentInset:(BOOL)animated { 168 | // Realization in sub class 169 | } 170 | 171 | - (void)layout { 172 | // Realization in sub class 173 | } 174 | 175 | - (void)contentOffsetChanged:(NSDictionary *)change { 176 | // Realization in sub class 177 | } 178 | 179 | - (void)contentInsetChanged:(NSDictionary *)change { 180 | [self layout]; 181 | } 182 | 183 | - (void)contentSizeChanged:(NSDictionary *)change { 184 | [self layout]; 185 | } 186 | 187 | - (void)contentFrameChanged:(NSDictionary *)change { 188 | // Realization in sub class 189 | } 190 | 191 | @end 192 | 193 | @implementation TWRefreshView (ObserverMethods) 194 | 195 | - (void)addObservers { 196 | [_scrollView addObserver:self forKeyPath:TWRefreshContentOffsetKeyPath options:NSKeyValueObservingOptionNew context:nil]; 197 | [_scrollView addObserver:self forKeyPath:TWRefreshContentInsetKeyPath options:NSKeyValueObservingOptionNew context:nil]; 198 | [_scrollView addObserver:self forKeyPath:TWRefreshContentSizeKeyPath options:NSKeyValueObservingOptionNew context:nil]; 199 | [_scrollView addObserver:self forKeyPath:TWRefreshFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; 200 | } 201 | 202 | - (void)removeObservers { 203 | [self.superview removeObserver:self forKeyPath:TWRefreshContentOffsetKeyPath]; 204 | [self.superview removeObserver:self forKeyPath:TWRefreshContentInsetKeyPath]; 205 | [self.superview removeObserver:self forKeyPath:TWRefreshContentSizeKeyPath]; 206 | [self.superview removeObserver:self forKeyPath:TWRefreshFrameKeyPath]; 207 | } 208 | 209 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 210 | if ([TWRefreshContentOffsetKeyPath isEqualToString:keyPath]) { 211 | [self contentOffsetChanged:change]; 212 | } 213 | else if ([TWRefreshContentInsetKeyPath isEqualToString:keyPath]) { 214 | [self contentInsetChanged:change]; 215 | } 216 | else if ([TWRefreshContentSizeKeyPath isEqualToString:keyPath]) { 217 | [self contentSizeChanged:change]; 218 | } 219 | else if ([TWRefreshFrameKeyPath isEqualToString:keyPath]) { 220 | [self contentFrameChanged:change]; 221 | } 222 | } 223 | 224 | @end 225 | 226 | #pragma refresh header view 227 | @implementation TWRefreshHeaderView 228 | { 229 | int _flag; // This flag just do .... work around flag 230 | } 231 | 232 | - (void)contentOffsetChanged:(NSDictionary *)change { 233 | // Call super method 234 | [super contentOffsetChanged:change]; 235 | 236 | // Check refresh state 237 | if (_state==TWRefreshStateRefreshing) { 238 | [self adjustContentInset]; 239 | return; 240 | } 241 | 242 | // Correct content inset firstly set here, assignment in willMoveToSuperView not work infact 243 | //_originalContentInsets = _scrollView.contentInset; 244 | 245 | CGPoint contentOffset = _scrollView.contentOffset; 246 | if (_scrollView.isDragging) { 247 | CGFloat ratio = 0; 248 | BOOL invokeRatio = NO; 249 | CGFloat offsetY = contentOffset.y + _originalContentInset.top; 250 | if (offsetY<=-self.frame.size.height) { 251 | _state = TWRefreshStateReadyToRefresh; 252 | } 253 | else { 254 | _state = TWRefreshStateNormal; 255 | } 256 | if (offsetY<0) { 257 | ratio = MAX(0, MIN(-offsetY / self.frame.size.height, 1.0)); 258 | invokeRatio = YES; 259 | } 260 | if (invokeRatio && [_indicator respondsToSelector:@selector(pullingWithRatio:)]) { 261 | [_indicator pullingWithRatio:ratio]; 262 | } 263 | } 264 | else { 265 | if (_state==TWRefreshStateReadyToRefresh) { 266 | [_scrollView refreshHeader]; 267 | } 268 | } 269 | } 270 | 271 | - (void)adjustContentInset { 272 | if (_flag>0) { 273 | return; 274 | } 275 | // Fix: A known issue here, when table view with section header, and scroll up, a gap between top and section header 276 | CGPoint contentOffset = _scrollView.contentOffset; 277 | if (contentOffset.y > -_originalContentInset.top - _refreshInsetHeight) { 278 | UIEdgeInsets inset = _scrollView.contentInset; 279 | _refreshInsetHeight = - MIN((contentOffset.y + _originalContentInset.top), 0); 280 | inset.top = _originalContentInset.top +_refreshInsetHeight; 281 | _scrollView.contentInset = inset; 282 | } 283 | else { 284 | UIEdgeInsets inset = _scrollView.contentInset; 285 | _refreshInsetHeight = MIN(-(contentOffset.y + _originalContentInset.top), self.frame.size.height); 286 | inset.top = _originalContentInset.top +_refreshInsetHeight; 287 | _scrollView.contentInset = inset; 288 | } 289 | } 290 | 291 | - (void)contentFrameChanged:(NSDictionary *)change { 292 | _flag = 2; 293 | } 294 | 295 | - (void)contentInsetChanged:(NSDictionary *)change { 296 | if (_state != TWRefreshStateRefreshing) { 297 | _flag = 1; 298 | _originalContentInset = _scrollView.contentInset; 299 | } 300 | else { 301 | if (_flag > 0) { 302 | _flag --; 303 | _originalContentInset.top = _scrollView.contentInset.top - _refreshInsetHeight; 304 | } 305 | } 306 | [super contentInsetChanged:change]; 307 | } 308 | 309 | - (void)contentSizeChanged:(NSDictionary *)change { 310 | [super contentSizeChanged:change]; 311 | } 312 | 313 | - (void)addRefreshContentInset:(BOOL)animated { 314 | UIEdgeInsets edgeInset = _scrollView.contentInset; 315 | edgeInset.top = _refreshInsetHeight + _originalContentInset.top; 316 | CGFloat duration = animated? 0.25 : 0; 317 | [UIView animateWithDuration:duration animations:^{ 318 | _scrollView.contentInset = edgeInset; 319 | _scrollView.contentOffset = CGPointMake(0, -edgeInset.top); 320 | } completion:^(BOOL finished) { 321 | 322 | }]; 323 | } 324 | 325 | - (void)removeRefreshContentInset:(BOOL)animated { 326 | UIEdgeInsets edgeInset = _scrollView.contentInset; 327 | // Fix inset issue, when scroll view is refresh, and push another view controller 328 | edgeInset.top -= _refreshInsetHeight;//=_originalContentInsets.top; 329 | _refreshInsetHeight = 0; 330 | CGFloat duration = animated? 0.25 : 0; 331 | [UIView animateWithDuration:duration animations:^{ 332 | _scrollView.contentInset = edgeInset; 333 | } completion:^(BOOL finished) { 334 | _flag = NO; 335 | if ([_indicator respondsToSelector:@selector(reset)]) { 336 | [_indicator reset]; 337 | } 338 | }]; 339 | } 340 | 341 | - (void)layout { 342 | CGRect r = self.frame; 343 | CGFloat indicatorHeight = 0; 344 | if ([_indicator respondsToSelector:@selector(indicatorHeight)]) { 345 | if ([_indicator indicatorHeight] > 0) { 346 | indicatorHeight = [_indicator indicatorHeight]; 347 | } 348 | } 349 | if (indicatorHeight <= 0) { 350 | indicatorHeight = TWRefreshHeaderViewHeight; 351 | } 352 | r.size.height = indicatorHeight; 353 | r.origin.y = -r.size.height; 354 | self.frame = r; 355 | if (_indicator) { 356 | ((UIView*)_indicator).frame = self.bounds; 357 | } 358 | } 359 | 360 | @end 361 | 362 | #pragma refresh footer view 363 | @implementation TWRefreshFooterView 364 | 365 | - (void)contentOffsetChanged:(NSDictionary *)change { 366 | 367 | // Call super method 368 | [super contentOffsetChanged:change]; 369 | 370 | // Check refresh state 371 | if (_state == TWRefreshStateRefreshing) { 372 | return; 373 | } 374 | 375 | // Correct content inset firstly set here, assignment in willMoveToSuperView not work infact 376 | //_originalContentInsets = _scrollView.contentInset; 377 | 378 | CGPoint contentOffset = _scrollView.contentOffset; 379 | if (_scrollView.isDragging) { 380 | CGFloat ratio = 0; 381 | BOOL invokeRatio = NO; 382 | CGFloat offsetY = contentOffset.y + _originalContentInset.top; 383 | CGFloat beginY = MAX(_scrollView.contentSize.height - _scrollView.frame.size.height + _originalContentInset.top + _originalContentInset.bottom, 0); 384 | if (offsetY >= beginY + self.frame.size.height) { 385 | _state = TWRefreshStateReadyToRefresh; 386 | } 387 | else { 388 | _state = TWRefreshStateNormal; 389 | } 390 | if (offsetY > beginY) { 391 | ratio = MAX(0, MIN((offsetY - beginY) / self.frame.size.height, 1.0)); 392 | invokeRatio = YES; 393 | } 394 | if (invokeRatio && [_indicator respondsToSelector:@selector(pullingWithRatio:)]) { 395 | [_indicator pullingWithRatio:ratio]; 396 | } 397 | } 398 | else { 399 | if (_state == TWRefreshStateReadyToRefresh) { 400 | [_scrollView refreshFooter]; 401 | } 402 | } 403 | } 404 | 405 | - (void)contentInsetChanged:(NSDictionary *)change { 406 | /* 407 | if (_state!=TWRefreshStateRefreshing) { 408 | _originalContentInset = _scrollView.contentInset; 409 | } 410 | else { 411 | CGFloat top = _originalContentInset.top; 412 | CGFloat bottom = _originalContentInset.bottom; 413 | 414 | _originalContentInset.bottom = _scrollView.contentInset.bottom; 415 | _originalContentInset.bottom -= self.frame.size.height; 416 | 417 | // If scroll view content size height less than it's frame 418 | _originalContentInset.bottom -= MAX(_scrollView.frame.size.height-top-bottom-_scrollView.contentSize.height, 0); 419 | }*/ 420 | [super contentInsetChanged:change]; 421 | } 422 | 423 | - (void)contentSizeChanged:(NSDictionary *)change { 424 | [super contentSizeChanged:change]; 425 | } 426 | 427 | - (void)addRefreshContentInset:(BOOL)animated { 428 | UIEdgeInsets edgeInset = _scrollView.contentInset; 429 | edgeInset.bottom = _refreshInsetHeight + _originalContentInset.bottom + MAX(_scrollView.frame.size.height - _originalContentInset.top - _originalContentInset.bottom - _scrollView.contentSize.height, 0); 430 | CGFloat duration = animated? 0.25 : 0; 431 | [UIView animateWithDuration:duration animations:^{ 432 | _scrollView.contentInset = edgeInset; 433 | } completion:^(BOOL finished) { 434 | }]; 435 | } 436 | 437 | - (void)removeRefreshContentInset:(BOOL)animated { 438 | UIEdgeInsets edgeInset = _scrollView.contentInset; 439 | // Fix inset issue, when scroll view is refresh, and push another view controller 440 | edgeInset.bottom -= _refreshInsetHeight;//=_originalContentInsets.bottom; 441 | _refreshInsetHeight = 0; 442 | 443 | // If scroll view content size height less than it's frame 444 | edgeInset.bottom -= MAX(_scrollView.frame.size.height - _originalContentInset.top - _originalContentInset.bottom - _scrollView.contentSize.height, 0); 445 | 446 | CGFloat duration = animated? 0.25 : 0; 447 | [UIView animateWithDuration:duration animations:^{ 448 | _scrollView.contentInset = edgeInset; 449 | } completion:^(BOOL finished) { 450 | if ([_indicator respondsToSelector:@selector(reset)]) { 451 | [_indicator reset]; 452 | } 453 | }]; 454 | } 455 | 456 | - (void)layout { 457 | CGRect r = self.frame; 458 | CGFloat indicatorHeight = 0; 459 | if ([_indicator respondsToSelector:@selector(indicatorHeight)]) { 460 | if ([_indicator indicatorHeight] > 0) { 461 | indicatorHeight = [_indicator indicatorHeight]; 462 | } 463 | } 464 | if (indicatorHeight <= 0) { 465 | indicatorHeight = TWRefreshFooterViewHeight; 466 | } 467 | r.size.height = indicatorHeight; 468 | r.origin.y = MAX(_scrollView.contentSize.height, _scrollView.frame.size.height - _originalContentInset.top - _originalContentInset.bottom); 469 | self.frame = r; 470 | if (_indicator) { 471 | ((UIView *)_indicator).frame = self.bounds; 472 | } 473 | } 474 | 475 | @end 476 | -------------------------------------------------------------------------------- /TWRefresh/Base/UIScrollView+TWRefresh.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // UIScrollView+TWRefres.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | #import "TWRefreshView.h" 25 | #import "TWRefreshIndicator.h" 26 | 27 | @interface UIScrollView (TWRefresh) 28 | 29 | // Set refresh Header indicator 30 | - (void)setRefreshHeaderIndicator:(id)indicator; 31 | 32 | // Set refresh Header with indicator class 33 | - (void)setRefreshHeaderWithIndicatorClass:(Class)clazz; 34 | 35 | // Set refresh Footer indicator 36 | - (void)setRefreshFooterIndicator:(id)indicator; 37 | 38 | // Set refresh Footer with indicator class 39 | - (void)setRefreshFooterWithIndicatorClass:(Class)clazz; 40 | 41 | // Stop Header Refreshing 42 | - (void)stopHeaderRefreshing; 43 | 44 | // Stop Footer Refreshing 45 | - (void)stopFooterRefreshing; 46 | 47 | // Header Refresing 48 | - (void)refreshHeader; 49 | 50 | // Footer Refresing 51 | - (void)refreshFooter; 52 | 53 | // Set refresh enabled, sometimes you need to switch refreshable state 54 | - (void)setRefreshEnabled:(BOOL)refreshEnabled; 55 | 56 | // Set refresh enabled, sometimes you need to switch refreshable state 57 | - (void)setRefreshHeaderEnabled:(BOOL)refreshEnabled; 58 | 59 | // Get state of refresh header enabled 60 | - (BOOL)refreshHeaderEnabled; 61 | 62 | // Set refresh enabled, sometimes you need to switch refreshable state 63 | - (void)setRefreshFooterEnabled:(BOOL)refreshEnabled; 64 | 65 | // Get state of refresh footer enabled 66 | - (BOOL)refreshFooterEnabled; 67 | 68 | // Refresh header callback 69 | - (void)setRefreshHeaderCallback:(void (^)())refreshHeaderCallback; 70 | 71 | // Refresh footer callback 72 | - (void)setRefreshFooterCallback:(void (^)())refreshFooterCallback; 73 | 74 | // Refresh header state 75 | - (TWRefreshState)refreshHeaderState; 76 | 77 | // Refresh footer state 78 | - (TWRefreshState)refreshFooterState; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /TWRefresh/Base/UIScrollView+TWRefresh.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // UIScrollView+TWRefres.m 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import "UIScrollView+TWRefresh.h" 24 | #import 25 | 26 | @interface UIScrollView () 27 | @property (nonatomic, strong) TWRefreshView *header; 28 | @property (nonatomic, strong) TWRefreshView *footer; 29 | @property (nonatomic, copy) void (^refreshHeaderCallback)(); 30 | @property (nonatomic, copy) void (^refreshFooterCallback)(); 31 | @end 32 | 33 | static const NSString *TWRefreshHeaderKey = @"TWRefreshHeaderView"; 34 | static const NSString *TWRefreshFooterKey = @"TWRefreshFooterView"; 35 | static const NSString *TWRefreshHeaderCallbackKey = @"TWRefreshHeaderViewCallback"; 36 | static const NSString *TWRefreshFooterCallbackKey = @"TWRefreshFooterViewCallback"; 37 | 38 | @implementation UIScrollView (TWRefresh) 39 | 40 | - (void)setHeader:(TWRefreshView *)header { 41 | objc_setAssociatedObject(self, &TWRefreshHeaderKey, header, OBJC_ASSOCIATION_RETAIN); 42 | } 43 | 44 | - (void)setFooter:(TWRefreshView *)footer { 45 | objc_setAssociatedObject(self, &TWRefreshFooterKey, footer, OBJC_ASSOCIATION_RETAIN); 46 | } 47 | 48 | - (TWRefreshView *)header { 49 | return objc_getAssociatedObject(self, &TWRefreshHeaderKey); 50 | } 51 | 52 | - (TWRefreshView *)footer { 53 | return objc_getAssociatedObject(self, &TWRefreshFooterKey); 54 | } 55 | 56 | - (void)setRefreshHeaderCallback:(void (^)())refreshHeaderCallback { 57 | objc_setAssociatedObject(self, &TWRefreshHeaderCallbackKey, refreshHeaderCallback, OBJC_ASSOCIATION_COPY); 58 | } 59 | 60 | - (void)setRefreshFooterCallback:(void (^)())refreshFooterCallback { 61 | objc_setAssociatedObject(self, &TWRefreshFooterCallbackKey, refreshFooterCallback, OBJC_ASSOCIATION_COPY); 62 | } 63 | 64 | - (void(^)())refreshHeaderCallback { 65 | return objc_getAssociatedObject(self, &TWRefreshHeaderCallbackKey); 66 | } 67 | 68 | - (void(^)())refreshFooterCallback { 69 | return objc_getAssociatedObject(self, &TWRefreshFooterCallbackKey); 70 | } 71 | 72 | - (void)setRefreshHeaderWithIndicatorClass:(Class)clazz { 73 | if ([clazz conformsToProtocol:@protocol(TWRefreshIndicator)]) { 74 | id indicator = [[clazz alloc] init]; 75 | [self setRefreshHeaderIndicator:indicator]; 76 | } 77 | } 78 | 79 | - (void)setRefreshHeaderIndicator:(id)indicator { 80 | if (!self.header) { 81 | TWRefreshView *refreshView = [[TWRefreshHeaderView alloc] init]; 82 | [self addSubview:refreshView]; 83 | self.header = refreshView; 84 | } 85 | if (indicator) { 86 | [self.header setIndicator:indicator]; 87 | } 88 | } 89 | 90 | - (void)setRefreshFooterWithIndicatorClass:(Class)clazz { 91 | if ([clazz conformsToProtocol:@protocol(TWRefreshIndicator)]) { 92 | id indicator = [[clazz alloc] init]; 93 | [self setRefreshFooterIndicator:indicator]; 94 | } 95 | } 96 | 97 | - (void)setRefreshFooterIndicator:(id)indicator { 98 | if (!self.footer) { 99 | TWRefreshView *refreshView = [[TWRefreshFooterView alloc] init]; 100 | [self addSubview:refreshView]; 101 | self.footer = refreshView; 102 | } 103 | if (indicator) { 104 | [self.footer setIndicator:indicator]; 105 | } 106 | } 107 | 108 | - (void)refreshHeader { 109 | if (self.header) { 110 | BOOL refreshRequired = [self refreshHeaderState] != TWRefreshStateRefreshing; 111 | [self.header setState:TWRefreshStateRefreshing]; 112 | if (refreshRequired) { 113 | if (self.refreshHeaderCallback) { 114 | self.refreshHeaderCallback(); 115 | } 116 | } 117 | } 118 | } 119 | 120 | - (void)refreshFooter { 121 | if (self.footer) { 122 | BOOL refreshRequired = [self refreshFooterState] != TWRefreshStateRefreshing; 123 | [self.footer setState:TWRefreshStateRefreshing]; 124 | if (refreshRequired) { 125 | if (self.refreshFooterCallback) { 126 | self.refreshFooterCallback(); 127 | } 128 | } 129 | } 130 | } 131 | 132 | - (void)stopHeaderRefreshing { 133 | if (self.header) { 134 | [self.header setState:TWRefreshStateNormal]; 135 | } 136 | } 137 | 138 | - (void)stopFooterRefreshing { 139 | if (self.footer) { 140 | [self.footer setState:TWRefreshStateNormal]; 141 | } 142 | } 143 | 144 | - (void)setRefreshEnabled:(BOOL)refreshEnabled { 145 | [self setRefreshHeaderEnabled:refreshEnabled]; 146 | [self setRefreshFooterEnabled:refreshEnabled]; 147 | } 148 | 149 | - (void)setRefreshHeaderEnabled:(BOOL)refreshEnabled { 150 | if (refreshEnabled) { 151 | if (self.header && !self.header.superview) { 152 | [self addSubview:self.header]; 153 | } 154 | } 155 | else { 156 | if (self.header && self.header.superview == self) { 157 | // If is refreshing, stop it to adjust content inset 158 | [self.header setState:TWRefreshStateNormal]; 159 | 160 | // Remove from self 161 | [self.header removeFromSuperview]; 162 | } 163 | } 164 | } 165 | 166 | - (BOOL)refreshHeaderEnabled { 167 | if (self.header && self.header.superview == self) { 168 | return YES; 169 | } 170 | return NO; 171 | } 172 | 173 | - (void)setRefreshFooterEnabled:(BOOL)refreshEnabled { 174 | if (refreshEnabled) { 175 | if (self.footer && !self.footer.superview) { 176 | [self addSubview:self.footer]; 177 | } 178 | } 179 | else { 180 | if (self.footer && self.footer.superview == self) { 181 | // If is refreshing, stop it to adjust content inset 182 | [self.footer setState:TWRefreshStateNormal]; 183 | 184 | // Remove from self 185 | [self.footer removeFromSuperview]; 186 | } 187 | } 188 | } 189 | 190 | - (BOOL)refreshFooterEnabled { 191 | if (self.footer && self.footer.superview == self) { 192 | return YES; 193 | } 194 | return NO; 195 | } 196 | 197 | - (TWRefreshState)refreshHeaderState { 198 | if (self.header) { 199 | return self.header.state; 200 | } 201 | return TWRefreshStateUnknown; 202 | } 203 | 204 | - (TWRefreshState)refreshFooterState { 205 | if (self.footer) { 206 | return self.footer.state; 207 | } 208 | return TWRefreshStateUnknown; 209 | } 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefresh.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefresh.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #ifndef TWRefreshDemo_TWRefresh_h 10 | #define TWRefreshDemo_TWRefresh_h 11 | 12 | typedef enum{ 13 | TWRefreshTypeTop = 1<<0, 14 | TWRefreshTypeBottom = 1<<1, 15 | }TWRefreshType; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefreshCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshCollectionView.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | #import "UIScrollView+TWRefresh.h" 25 | #import "TWRefreshType.h" 26 | 27 | @protocol TWCollectionViewRefreshingDelegate; 28 | 29 | @interface TWRefreshCollectionView : UICollectionView 30 | 31 | //Refresh Delegate 32 | @property (nonatomic, weak) id refreshDelegate; 33 | 34 | @property (nonatomic, assign) TWRefreshType refreshType; 35 | 36 | // Constructors 37 | - (id)initWithFrame:(CGRect)frame refreshType:(TWRefreshType)refreshType; 38 | 39 | - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout refreshType:(TWRefreshType)refreshType; 40 | 41 | @end 42 | 43 | @protocol TWCollectionViewRefreshingDelegate 44 | 45 | @optional 46 | - (void)beginRefreshHeader:(TWRefreshCollectionView *)collectionView; 47 | - (void)beginRefreshFooter:(TWRefreshCollectionView *)collectionView; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefreshCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshCollectionView.m 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import "TWRefreshCollectionView.h" 24 | #import "TWRefreshIndicatorView.h" 25 | 26 | @implementation TWRefreshCollectionView 27 | { 28 | TWRefreshType _refreshType; 29 | } 30 | 31 | - (id)initWithFrame:(CGRect)frame refreshType:(TWRefreshType)refreshType { 32 | self = [super initWithFrame:frame]; 33 | if (self) { 34 | _refreshType = refreshType; 35 | [self prepareRefresh]; 36 | } 37 | return self; 38 | } 39 | 40 | - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout refreshType:(TWRefreshType)refreshType { 41 | self = [super initWithFrame:frame collectionViewLayout:layout]; 42 | if (self) { 43 | _refreshType = refreshType; 44 | [self prepareRefresh]; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)setRefreshType:(TWRefreshType)refreshType { 50 | if (_refreshType != refreshType) { 51 | _refreshType = refreshType; 52 | [self prepareRefresh]; 53 | } 54 | } 55 | 56 | - (void)prepareRefresh { 57 | if((_refreshType & TWRefreshTypeTop) == TWRefreshTypeTop){ 58 | [self setRefreshHeaderWithIndicatorClass:[TWRefreshIndicatorView class]]; 59 | } 60 | if((_refreshType & TWRefreshTypeBottom) == TWRefreshTypeBottom){ 61 | [self setRefreshFooterWithIndicatorClass:[TWRefreshIndicatorView class]]; 62 | } 63 | } 64 | 65 | - (void)refreshHeader { 66 | BOOL refreshRequired = [self refreshHeaderState] != TWRefreshStateRefreshing; 67 | [super refreshHeader]; 68 | if (refreshRequired) { 69 | if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(beginRefreshHeader:)]) { 70 | [self.refreshDelegate beginRefreshHeader:self]; 71 | } 72 | } 73 | } 74 | 75 | - (void)refreshFooter { 76 | BOOL refreshRequired = [self refreshHeaderState] != TWRefreshStateRefreshing; 77 | [super refreshFooter]; 78 | if (refreshRequired) { 79 | if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(beginRefreshFooter:)]) { 80 | [self.refreshDelegate beginRefreshFooter:self]; 81 | } 82 | } 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefreshTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshTableView.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | #import "UIScrollView+TWRefresh.h" 25 | #import "TWRefreshType.h" 26 | 27 | @protocol TWTableViewRefreshingDelegate; 28 | 29 | @interface TWRefreshTableView : UITableView 30 | 31 | //Refresh Delegate 32 | @property (nonatomic, weak) id refreshDelegate; 33 | 34 | @property (nonatomic, assign) TWRefreshType refreshType; 35 | 36 | // Constructors 37 | - (id)initWithFrame:(CGRect)frame refreshType:(TWRefreshType)refreshTpye; 38 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle) style refreshType:(TWRefreshType)refreshType; 39 | 40 | @end 41 | 42 | @protocol TWTableViewRefreshingDelegate 43 | 44 | @optional 45 | - (void)beginRefreshHeader:(TWRefreshTableView *)tableView; 46 | - (void)beginRefreshFooter:(TWRefreshTableView *)tableView; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefreshTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshTableView.m 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import "TWRefreshTableView.h" 24 | #import "TWRefreshIndicatorView.h" 25 | 26 | @implementation TWRefreshTableView 27 | { 28 | TWRefreshType _refreshType; 29 | } 30 | 31 | - (id)initWithFrame:(CGRect)frame { 32 | return [self initWithFrame:frame refreshType:TWRefreshTypeTop|TWRefreshTypeBottom]; 33 | } 34 | 35 | - (id)initWithFrame:(CGRect)frame refreshType:(TWRefreshType)refreshTpye { 36 | return [self initWithFrame:frame style:UITableViewStylePlain refreshType:refreshTpye]; 37 | } 38 | 39 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle) style refreshType:(TWRefreshType)refreshType { 40 | self = [super initWithFrame:frame style:style]; 41 | if (self) { 42 | _refreshType = refreshType; 43 | [self prepareRefresh]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)setRefreshType:(TWRefreshType)refreshType { 49 | if (_refreshType != refreshType) { 50 | _refreshType = refreshType; 51 | [self prepareRefresh]; 52 | } 53 | } 54 | 55 | - (void)prepareRefresh { 56 | if((_refreshType & TWRefreshTypeTop) == TWRefreshTypeTop){ 57 | [self setRefreshHeaderWithIndicatorClass:[TWRefreshIndicatorView class]]; 58 | } 59 | if((_refreshType & TWRefreshTypeBottom) == TWRefreshTypeBottom){ 60 | [self setRefreshFooterWithIndicatorClass:[TWRefreshIndicatorView class]]; 61 | } 62 | } 63 | 64 | - (void)refreshHeader { 65 | BOOL refreshRequired = [self refreshHeaderState] != TWRefreshStateRefreshing; 66 | [super refreshHeader]; 67 | if (refreshRequired) { 68 | if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(beginRefreshHeader:)]) { 69 | [self.refreshDelegate beginRefreshHeader:self]; 70 | } 71 | } 72 | } 73 | 74 | - (void)refreshFooter { 75 | BOOL refreshRequired = [self refreshFooterState] != TWRefreshStateRefreshing; 76 | [super refreshFooter]; 77 | if (refreshRequired) { 78 | if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(beginRefreshFooter:)]) { 79 | [self.refreshDelegate beginRefreshFooter:self]; 80 | } 81 | } 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /TWRefresh/Implements/TWRefreshType.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshType.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #ifndef TWRefresh_TWRefreshType_h 24 | #define TWRefresh_TWRefreshType_h 25 | 26 | typedef enum{ 27 | TWRefreshTypeTop = 1 << 0, 28 | TWRefreshTypeBottom = 1 << 1, 29 | }TWRefreshType; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /TWRefresh/Indicators/TWRefreshIndicatorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshIndicatorView.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | #import "TWRefreshIndicator.h" 25 | 26 | @interface TWRefreshIndicatorView : UIView 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /TWRefresh/Indicators/TWRefreshIndicatorView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefreshIndicatorView.m 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import "TWRefreshIndicatorView.h" 24 | 25 | @implementation TWRefreshIndicatorView 26 | { 27 | CAShapeLayer *_arcLayer; 28 | UIImageView *_imageView, *_wordsView; 29 | } 30 | 31 | - (id)initWithFrame:(CGRect)frame { 32 | self = [super initWithFrame:frame]; 33 | if (self) { 34 | [self addShapeLayer]; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)setFrame:(CGRect)frame { 40 | if (!CGRectEqualToRect(frame, self.frame)) { 41 | [super setFrame:frame]; 42 | [self adjustShapeLayerPath]; 43 | } 44 | } 45 | 46 | - (void)start { 47 | [self startAnimation]; 48 | } 49 | 50 | - (void)stop { 51 | [self removeAnimations]; 52 | } 53 | 54 | - (void)reset { 55 | [self drawLineAnimationWithRatio:0]; 56 | } 57 | 58 | - (void)pullingWithRatio:(CGFloat)ratio { 59 | [self drawLineAnimationWithRatio:ratio]; 60 | } 61 | 62 | - (void)removeAnimations { 63 | [_arcLayer removeAnimationForKey:@"pulling.refresh.rotation"]; 64 | [_imageView.layer removeAnimationForKey:@"pulling.refresh.rotation"]; 65 | } 66 | 67 | -(void)addShapeLayer { 68 | _arcLayer = [CAShapeLayer layer]; 69 | _arcLayer.lineCap = kCALineCapRound; 70 | _arcLayer.lineJoin = kCALineJoinRound; 71 | _arcLayer.fillColor = [UIColor clearColor].CGColor; 72 | _arcLayer.strokeColor = [UIColor redColor].CGColor; 73 | _arcLayer.lineWidth = 2; 74 | _arcLayer.strokeStart = 0; 75 | [self.layer addSublayer:_arcLayer]; 76 | } 77 | 78 | - (void)adjustShapeLayerPath { 79 | if (_arcLayer) { 80 | _arcLayer.frame = self.bounds; 81 | UIBezierPath *path = [UIBezierPath bezierPath]; 82 | [path addArcWithCenter:CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2) radius:12 startAngle:-M_PI_2 endAngle:2 * M_PI - M_PI_2 clockwise:YES]; 83 | _arcLayer.path = path.CGPath; 84 | } 85 | if (_imageView) { 86 | _imageView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2); 87 | } 88 | if (_wordsView) { 89 | _wordsView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2); 90 | } 91 | } 92 | 93 | -(void)drawLineAnimationWithRatio:(CGFloat)ratio { 94 | _arcLayer.strokeEnd = ratio; 95 | _wordsView.transform = CGAffineTransformMakeScale(ratio, ratio); 96 | } 97 | 98 | - (void)startAnimation { 99 | // Remove animation first 100 | [self removeAnimations]; 101 | 102 | // Add animation 103 | _arcLayer.strokeEnd = 0.94; 104 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 105 | animation.fromValue = [NSNumber numberWithFloat:0.0]; 106 | animation.toValue = [NSNumber numberWithFloat:2 * M_PI]; 107 | animation.duration = 1.0; 108 | animation.repeatCount = INT_MAX; 109 | [_arcLayer addAnimation:animation forKey:@"pulling.refresh.rotation"]; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /TWRefresh/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TWRefresh/TWRefresh.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2016 Chris. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // 17 | // TWRefresh.h 18 | // TWRefresh 19 | // 20 | // Created by Chris on 24/5/2016. 21 | // 22 | 23 | #import 24 | 25 | //! Project version number for TWRefresh. 26 | FOUNDATION_EXPORT double TWRefreshVersionNumber; 27 | 28 | //! Project version string for TWRefresh. 29 | FOUNDATION_EXPORT const unsigned char TWRefreshVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | 33 | #if __has_include() 34 | #import 35 | #import 36 | #import 37 | #import 38 | #else 39 | #import "TWRefreshIndicator.h" 40 | #import "TWRefreshIndicatorView.h" 41 | #import "TWRefreshTableView.h" 42 | #import "TWRefreshCollectionView.h" 43 | #endif 44 | -------------------------------------------------------------------------------- /TWRefresh/bugs.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefresh/bugs.txt -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 833238CE1B2E6D5F00B806CE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 833238CD1B2E6D5F00B806CE /* main.m */; }; 11 | 833238D11B2E6D5F00B806CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 833238D01B2E6D5F00B806CE /* AppDelegate.m */; }; 12 | 833238D41B2E6D5F00B806CE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 833238D31B2E6D5F00B806CE /* ViewController.m */; }; 13 | 833238D71B2E6D5F00B806CE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 833238D51B2E6D5F00B806CE /* Main.storyboard */; }; 14 | 833238D91B2E6D5F00B806CE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 833238D81B2E6D5F00B806CE /* Images.xcassets */; }; 15 | 833238DC1B2E6D5F00B806CE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 833238DA1B2E6D5F00B806CE /* LaunchScreen.xib */; }; 16 | 833238E81B2E6D6000B806CE /* TWRefreshDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 833238E71B2E6D6000B806CE /* TWRefreshDemoTests.m */; }; 17 | 833239191B2FD4B600B806CE /* TWRefreshTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 833239181B2FD4B600B806CE /* TWRefreshTableViewController.m */; }; 18 | 8332391C1B2FD4D300B806CE /* TWRefreshCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8332391B1B2FD4D300B806CE /* TWRefreshCollectionViewController.m */; }; 19 | 833239361B3009A500B806CE /* TWRefreshScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 833239351B3009A500B806CE /* TWRefreshScrollViewController.m */; }; 20 | 83C943D61CF3F8DF005A22D2 /* TWRefresh.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83C943CD1CF3F7A4005A22D2 /* TWRefresh.framework */; }; 21 | 83C943D71CF3F8DF005A22D2 /* TWRefresh.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 83C943CD1CF3F7A4005A22D2 /* TWRefresh.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 833238E21B2E6D6000B806CE /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 833238C01B2E6D5E00B806CE /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 833238C71B2E6D5F00B806CE; 30 | remoteInfo = TWRefreshDemo; 31 | }; 32 | 83C943CC1CF3F7A4005A22D2 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */; 35 | proxyType = 2; 36 | remoteGlobalIDString = 834877F81C9D3A5100FE3021; 37 | remoteInfo = TWRefresh; 38 | }; 39 | 83C943CE1CF3F7A4005A22D2 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */; 42 | proxyType = 2; 43 | remoteGlobalIDString = 834878021C9D3A5100FE3021; 44 | remoteInfo = TWRefreshTests; 45 | }; 46 | 83C943D81CF3F8DF005A22D2 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 834877F71C9D3A5100FE3021; 51 | remoteInfo = TWRefresh; 52 | }; 53 | /* End PBXContainerItemProxy section */ 54 | 55 | /* Begin PBXCopyFilesBuildPhase section */ 56 | 83C943DA1CF3F8DF005A22D2 /* Embed Frameworks */ = { 57 | isa = PBXCopyFilesBuildPhase; 58 | buildActionMask = 2147483647; 59 | dstPath = ""; 60 | dstSubfolderSpec = 10; 61 | files = ( 62 | 83C943D71CF3F8DF005A22D2 /* TWRefresh.framework in Embed Frameworks */, 63 | ); 64 | name = "Embed Frameworks"; 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXCopyFilesBuildPhase section */ 68 | 69 | /* Begin PBXFileReference section */ 70 | 833238C81B2E6D5F00B806CE /* TWRefreshDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TWRefreshDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 833238CC1B2E6D5F00B806CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 72 | 833238CD1B2E6D5F00B806CE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 73 | 833238CF1B2E6D5F00B806CE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 74 | 833238D01B2E6D5F00B806CE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 75 | 833238D21B2E6D5F00B806CE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 76 | 833238D31B2E6D5F00B806CE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 77 | 833238D61B2E6D5F00B806CE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 78 | 833238D81B2E6D5F00B806CE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 79 | 833238DB1B2E6D5F00B806CE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 80 | 833238E11B2E6D6000B806CE /* TWRefreshDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TWRefreshDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 833238E61B2E6D6000B806CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | 833238E71B2E6D6000B806CE /* TWRefreshDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWRefreshDemoTests.m; sourceTree = ""; }; 83 | 833239171B2FD4B600B806CE /* TWRefreshTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshTableViewController.h; sourceTree = ""; }; 84 | 833239181B2FD4B600B806CE /* TWRefreshTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshTableViewController.m; sourceTree = ""; }; 85 | 8332391A1B2FD4D300B806CE /* TWRefreshCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshCollectionViewController.h; sourceTree = ""; }; 86 | 8332391B1B2FD4D300B806CE /* TWRefreshCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshCollectionViewController.m; sourceTree = ""; }; 87 | 833239341B3009A500B806CE /* TWRefreshScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWRefreshScrollViewController.h; sourceTree = ""; }; 88 | 833239351B3009A500B806CE /* TWRefreshScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWRefreshScrollViewController.m; sourceTree = ""; }; 89 | 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = TWRefresh.xcodeproj; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 833238C51B2E6D5F00B806CE /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | 83C943D61CF3F8DF005A22D2 /* TWRefresh.framework in Frameworks */, 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | 833238DE1B2E6D6000B806CE /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | 833238BF1B2E6D5E00B806CE = { 112 | isa = PBXGroup; 113 | children = ( 114 | 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */, 115 | 833238CA1B2E6D5F00B806CE /* TWRefreshDemo */, 116 | 833238E41B2E6D6000B806CE /* TWRefreshDemoTests */, 117 | 833238C91B2E6D5F00B806CE /* Products */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 833238C91B2E6D5F00B806CE /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 833238C81B2E6D5F00B806CE /* TWRefreshDemo.app */, 125 | 833238E11B2E6D6000B806CE /* TWRefreshDemoTests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | 833238CA1B2E6D5F00B806CE /* TWRefreshDemo */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 833238CF1B2E6D5F00B806CE /* AppDelegate.h */, 134 | 833238D01B2E6D5F00B806CE /* AppDelegate.m */, 135 | 833238D21B2E6D5F00B806CE /* ViewController.h */, 136 | 833238D31B2E6D5F00B806CE /* ViewController.m */, 137 | 833239171B2FD4B600B806CE /* TWRefreshTableViewController.h */, 138 | 833239181B2FD4B600B806CE /* TWRefreshTableViewController.m */, 139 | 8332391A1B2FD4D300B806CE /* TWRefreshCollectionViewController.h */, 140 | 8332391B1B2FD4D300B806CE /* TWRefreshCollectionViewController.m */, 141 | 833239341B3009A500B806CE /* TWRefreshScrollViewController.h */, 142 | 833239351B3009A500B806CE /* TWRefreshScrollViewController.m */, 143 | 833238D51B2E6D5F00B806CE /* Main.storyboard */, 144 | 833238D81B2E6D5F00B806CE /* Images.xcassets */, 145 | 833238DA1B2E6D5F00B806CE /* LaunchScreen.xib */, 146 | 833238CB1B2E6D5F00B806CE /* Supporting Files */, 147 | ); 148 | path = TWRefreshDemo; 149 | sourceTree = ""; 150 | }; 151 | 833238CB1B2E6D5F00B806CE /* Supporting Files */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 833238CC1B2E6D5F00B806CE /* Info.plist */, 155 | 833238CD1B2E6D5F00B806CE /* main.m */, 156 | ); 157 | name = "Supporting Files"; 158 | sourceTree = ""; 159 | }; 160 | 833238E41B2E6D6000B806CE /* TWRefreshDemoTests */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 833238E71B2E6D6000B806CE /* TWRefreshDemoTests.m */, 164 | 833238E51B2E6D6000B806CE /* Supporting Files */, 165 | ); 166 | path = TWRefreshDemoTests; 167 | sourceTree = ""; 168 | }; 169 | 833238E51B2E6D6000B806CE /* Supporting Files */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 833238E61B2E6D6000B806CE /* Info.plist */, 173 | ); 174 | name = "Supporting Files"; 175 | sourceTree = ""; 176 | }; 177 | 83C943C81CF3F7A3005A22D2 /* Products */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 83C943CD1CF3F7A4005A22D2 /* TWRefresh.framework */, 181 | 83C943CF1CF3F7A4005A22D2 /* TWRefreshTests.xctest */, 182 | ); 183 | name = Products; 184 | sourceTree = ""; 185 | }; 186 | /* End PBXGroup section */ 187 | 188 | /* Begin PBXNativeTarget section */ 189 | 833238C71B2E6D5F00B806CE /* TWRefreshDemo */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 833238EB1B2E6D6000B806CE /* Build configuration list for PBXNativeTarget "TWRefreshDemo" */; 192 | buildPhases = ( 193 | 833238C41B2E6D5F00B806CE /* Sources */, 194 | 833238C51B2E6D5F00B806CE /* Frameworks */, 195 | 833238C61B2E6D5F00B806CE /* Resources */, 196 | 83C943DA1CF3F8DF005A22D2 /* Embed Frameworks */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 83C943D91CF3F8DF005A22D2 /* PBXTargetDependency */, 202 | ); 203 | name = TWRefreshDemo; 204 | productName = TWRefreshDemo; 205 | productReference = 833238C81B2E6D5F00B806CE /* TWRefreshDemo.app */; 206 | productType = "com.apple.product-type.application"; 207 | }; 208 | 833238E01B2E6D6000B806CE /* TWRefreshDemoTests */ = { 209 | isa = PBXNativeTarget; 210 | buildConfigurationList = 833238EE1B2E6D6000B806CE /* Build configuration list for PBXNativeTarget "TWRefreshDemoTests" */; 211 | buildPhases = ( 212 | 833238DD1B2E6D6000B806CE /* Sources */, 213 | 833238DE1B2E6D6000B806CE /* Frameworks */, 214 | 833238DF1B2E6D6000B806CE /* Resources */, 215 | ); 216 | buildRules = ( 217 | ); 218 | dependencies = ( 219 | 833238E31B2E6D6000B806CE /* PBXTargetDependency */, 220 | ); 221 | name = TWRefreshDemoTests; 222 | productName = TWRefreshDemoTests; 223 | productReference = 833238E11B2E6D6000B806CE /* TWRefreshDemoTests.xctest */; 224 | productType = "com.apple.product-type.bundle.unit-test"; 225 | }; 226 | /* End PBXNativeTarget section */ 227 | 228 | /* Begin PBXProject section */ 229 | 833238C01B2E6D5E00B806CE /* Project object */ = { 230 | isa = PBXProject; 231 | attributes = { 232 | LastUpgradeCheck = 0630; 233 | ORGANIZATIONNAME = EasyBaking; 234 | TargetAttributes = { 235 | 833238C71B2E6D5F00B806CE = { 236 | CreatedOnToolsVersion = 6.3; 237 | DevelopmentTeam = 77C328S8HC; 238 | }; 239 | 833238E01B2E6D6000B806CE = { 240 | CreatedOnToolsVersion = 6.3; 241 | TestTargetID = 833238C71B2E6D5F00B806CE; 242 | }; 243 | }; 244 | }; 245 | buildConfigurationList = 833238C31B2E6D5E00B806CE /* Build configuration list for PBXProject "TWRefreshDemo" */; 246 | compatibilityVersion = "Xcode 3.2"; 247 | developmentRegion = English; 248 | hasScannedForEncodings = 0; 249 | knownRegions = ( 250 | en, 251 | Base, 252 | ); 253 | mainGroup = 833238BF1B2E6D5E00B806CE; 254 | productRefGroup = 833238C91B2E6D5F00B806CE /* Products */; 255 | projectDirPath = ""; 256 | projectReferences = ( 257 | { 258 | ProductGroup = 83C943C81CF3F7A3005A22D2 /* Products */; 259 | ProjectRef = 83C943C71CF3F7A3005A22D2 /* TWRefresh.xcodeproj */; 260 | }, 261 | ); 262 | projectRoot = ""; 263 | targets = ( 264 | 833238C71B2E6D5F00B806CE /* TWRefreshDemo */, 265 | 833238E01B2E6D6000B806CE /* TWRefreshDemoTests */, 266 | ); 267 | }; 268 | /* End PBXProject section */ 269 | 270 | /* Begin PBXReferenceProxy section */ 271 | 83C943CD1CF3F7A4005A22D2 /* TWRefresh.framework */ = { 272 | isa = PBXReferenceProxy; 273 | fileType = wrapper.framework; 274 | path = TWRefresh.framework; 275 | remoteRef = 83C943CC1CF3F7A4005A22D2 /* PBXContainerItemProxy */; 276 | sourceTree = BUILT_PRODUCTS_DIR; 277 | }; 278 | 83C943CF1CF3F7A4005A22D2 /* TWRefreshTests.xctest */ = { 279 | isa = PBXReferenceProxy; 280 | fileType = wrapper.cfbundle; 281 | path = TWRefreshTests.xctest; 282 | remoteRef = 83C943CE1CF3F7A4005A22D2 /* PBXContainerItemProxy */; 283 | sourceTree = BUILT_PRODUCTS_DIR; 284 | }; 285 | /* End PBXReferenceProxy section */ 286 | 287 | /* Begin PBXResourcesBuildPhase section */ 288 | 833238C61B2E6D5F00B806CE /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 833238D71B2E6D5F00B806CE /* Main.storyboard in Resources */, 293 | 833238DC1B2E6D5F00B806CE /* LaunchScreen.xib in Resources */, 294 | 833238D91B2E6D5F00B806CE /* Images.xcassets in Resources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | 833238DF1B2E6D6000B806CE /* Resources */ = { 299 | isa = PBXResourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | /* End PBXResourcesBuildPhase section */ 306 | 307 | /* Begin PBXSourcesBuildPhase section */ 308 | 833238C41B2E6D5F00B806CE /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 833239191B2FD4B600B806CE /* TWRefreshTableViewController.m in Sources */, 313 | 833238D41B2E6D5F00B806CE /* ViewController.m in Sources */, 314 | 8332391C1B2FD4D300B806CE /* TWRefreshCollectionViewController.m in Sources */, 315 | 833239361B3009A500B806CE /* TWRefreshScrollViewController.m in Sources */, 316 | 833238D11B2E6D5F00B806CE /* AppDelegate.m in Sources */, 317 | 833238CE1B2E6D5F00B806CE /* main.m in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 833238DD1B2E6D6000B806CE /* Sources */ = { 322 | isa = PBXSourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | 833238E81B2E6D6000B806CE /* TWRefreshDemoTests.m in Sources */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | /* End PBXSourcesBuildPhase section */ 330 | 331 | /* Begin PBXTargetDependency section */ 332 | 833238E31B2E6D6000B806CE /* PBXTargetDependency */ = { 333 | isa = PBXTargetDependency; 334 | target = 833238C71B2E6D5F00B806CE /* TWRefreshDemo */; 335 | targetProxy = 833238E21B2E6D6000B806CE /* PBXContainerItemProxy */; 336 | }; 337 | 83C943D91CF3F8DF005A22D2 /* PBXTargetDependency */ = { 338 | isa = PBXTargetDependency; 339 | name = TWRefresh; 340 | targetProxy = 83C943D81CF3F8DF005A22D2 /* PBXContainerItemProxy */; 341 | }; 342 | /* End PBXTargetDependency section */ 343 | 344 | /* Begin PBXVariantGroup section */ 345 | 833238D51B2E6D5F00B806CE /* Main.storyboard */ = { 346 | isa = PBXVariantGroup; 347 | children = ( 348 | 833238D61B2E6D5F00B806CE /* Base */, 349 | ); 350 | name = Main.storyboard; 351 | sourceTree = ""; 352 | }; 353 | 833238DA1B2E6D5F00B806CE /* LaunchScreen.xib */ = { 354 | isa = PBXVariantGroup; 355 | children = ( 356 | 833238DB1B2E6D5F00B806CE /* Base */, 357 | ); 358 | name = LaunchScreen.xib; 359 | sourceTree = ""; 360 | }; 361 | /* End PBXVariantGroup section */ 362 | 363 | /* Begin XCBuildConfiguration section */ 364 | 833238E91B2E6D6000B806CE /* Debug */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_SEARCH_USER_PATHS = NO; 368 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 369 | CLANG_CXX_LIBRARY = "libc++"; 370 | CLANG_ENABLE_MODULES = YES; 371 | CLANG_ENABLE_OBJC_ARC = YES; 372 | CLANG_WARN_BOOL_CONVERSION = YES; 373 | CLANG_WARN_CONSTANT_CONVERSION = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INT_CONVERSION = YES; 378 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 379 | CLANG_WARN_UNREACHABLE_CODE = YES; 380 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 381 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 382 | COPY_PHASE_STRIP = NO; 383 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 384 | ENABLE_STRICT_OBJC_MSGSEND = YES; 385 | GCC_C_LANGUAGE_STANDARD = gnu99; 386 | GCC_DYNAMIC_NO_PIC = NO; 387 | GCC_NO_COMMON_BLOCKS = YES; 388 | GCC_OPTIMIZATION_LEVEL = 0; 389 | GCC_PREPROCESSOR_DEFINITIONS = ( 390 | "DEBUG=1", 391 | "$(inherited)", 392 | ); 393 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 394 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 395 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 396 | GCC_WARN_UNDECLARED_SELECTOR = YES; 397 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 398 | GCC_WARN_UNUSED_FUNCTION = YES; 399 | GCC_WARN_UNUSED_VARIABLE = YES; 400 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 401 | MTL_ENABLE_DEBUG_INFO = YES; 402 | ONLY_ACTIVE_ARCH = YES; 403 | SDKROOT = iphoneos; 404 | }; 405 | name = Debug; 406 | }; 407 | 833238EA1B2E6D6000B806CE /* Release */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ALWAYS_SEARCH_USER_PATHS = NO; 411 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 412 | CLANG_CXX_LIBRARY = "libc++"; 413 | CLANG_ENABLE_MODULES = YES; 414 | CLANG_ENABLE_OBJC_ARC = YES; 415 | CLANG_WARN_BOOL_CONVERSION = YES; 416 | CLANG_WARN_CONSTANT_CONVERSION = YES; 417 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 418 | CLANG_WARN_EMPTY_BODY = YES; 419 | CLANG_WARN_ENUM_CONVERSION = YES; 420 | CLANG_WARN_INT_CONVERSION = YES; 421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 422 | CLANG_WARN_UNREACHABLE_CODE = YES; 423 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 424 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 425 | COPY_PHASE_STRIP = NO; 426 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 427 | ENABLE_NS_ASSERTIONS = NO; 428 | ENABLE_STRICT_OBJC_MSGSEND = YES; 429 | GCC_C_LANGUAGE_STANDARD = gnu99; 430 | GCC_NO_COMMON_BLOCKS = YES; 431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 433 | GCC_WARN_UNDECLARED_SELECTOR = YES; 434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 435 | GCC_WARN_UNUSED_FUNCTION = YES; 436 | GCC_WARN_UNUSED_VARIABLE = YES; 437 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 438 | MTL_ENABLE_DEBUG_INFO = NO; 439 | SDKROOT = iphoneos; 440 | VALIDATE_PRODUCT = YES; 441 | }; 442 | name = Release; 443 | }; 444 | 833238EC1B2E6D6000B806CE /* Debug */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 448 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 449 | DEVELOPMENT_TEAM = 77C328S8HC; 450 | INFOPLIST_FILE = TWRefreshDemo/Info.plist; 451 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | }; 455 | name = Debug; 456 | }; 457 | 833238ED1B2E6D6000B806CE /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 462 | DEVELOPMENT_TEAM = 77C328S8HC; 463 | INFOPLIST_FILE = TWRefreshDemo/Info.plist; 464 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | }; 468 | name = Release; 469 | }; 470 | 833238EF1B2E6D6000B806CE /* Debug */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | BUNDLE_LOADER = "$(TEST_HOST)"; 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(SDKROOT)/Developer/Library/Frameworks", 476 | "$(inherited)", 477 | ); 478 | GCC_PREPROCESSOR_DEFINITIONS = ( 479 | "DEBUG=1", 480 | "$(inherited)", 481 | ); 482 | INFOPLIST_FILE = TWRefreshDemoTests/Info.plist; 483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TWRefreshDemo.app/TWRefreshDemo"; 486 | }; 487 | name = Debug; 488 | }; 489 | 833238F01B2E6D6000B806CE /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | BUNDLE_LOADER = "$(TEST_HOST)"; 493 | FRAMEWORK_SEARCH_PATHS = ( 494 | "$(SDKROOT)/Developer/Library/Frameworks", 495 | "$(inherited)", 496 | ); 497 | INFOPLIST_FILE = TWRefreshDemoTests/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TWRefreshDemo.app/TWRefreshDemo"; 501 | }; 502 | name = Release; 503 | }; 504 | /* End XCBuildConfiguration section */ 505 | 506 | /* Begin XCConfigurationList section */ 507 | 833238C31B2E6D5E00B806CE /* Build configuration list for PBXProject "TWRefreshDemo" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | 833238E91B2E6D6000B806CE /* Debug */, 511 | 833238EA1B2E6D6000B806CE /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | 833238EB1B2E6D6000B806CE /* Build configuration list for PBXNativeTarget "TWRefreshDemo" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 833238EC1B2E6D6000B806CE /* Debug */, 520 | 833238ED1B2E6D6000B806CE /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 833238EE1B2E6D6000B806CE /* Build configuration list for PBXNativeTarget "TWRefreshDemoTests" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 833238EF1B2E6D6000B806CE /* Debug */, 529 | 833238F01B2E6D6000B806CE /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 833238C01B2E6D5E00B806CE /* Project object */; 537 | } 538 | -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/project.xcworkspace/xcshareddata/TWRefreshDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | C12B1A8C-2787-40C6-A557-D2512A9CD986 9 | IDESourceControlProjectName 10 | TWRefreshDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 575b07e2-ca90-4265-8d2b-796357eef20c 14 | http://code.taobao.org/svn/easyrefresh 15 | 16 | IDESourceControlProjectPath 17 | trunk/TWRefreshDemo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 575b07e2-ca90-4265-8d2b-796357eef20c 21 | ../../.. 22 | 23 | IDESourceControlProjectVersion 24 | 111 25 | IDESourceControlProjectWCCIdentifier 26 | 575b07e2-ca90-4265-8d2b-796357eef20c 27 | IDESourceControlProjectWCConfigurations 28 | 29 | 30 | IDESourceControlRepositoryExtensionIdentifierKey 31 | public.vcs.subversion 32 | IDESourceControlWCCIdentifierKey 33 | 575b07e2-ca90-4265-8d2b-796357eef20c 34 | IDESourceControlWCCName 35 | TWRefreshX 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/project.xcworkspace/xcuserdata/Chris.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo.xcodeproj/project.xcworkspace/xcuserdata/Chris.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/xcuserdata/Chris.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/TWRefreshDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /TWRefreshDemo.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TWRefreshDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 833238C71B2E6D5F00B806CE 16 | 17 | primary 18 | 19 | 20 | 833238E01B2E6D6000B806CE 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TWRefreshDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo/.DS_Store -------------------------------------------------------------------------------- /TWRefreshDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /TWRefreshDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | ViewController *mainVC = [[ViewController alloc] init]; 22 | UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:mainVC]; 23 | self.window.rootViewController = navVC; 24 | [self.window makeKeyAndVisible]; 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | // 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. 30 | // 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. 31 | } 32 | 33 | - (void)applicationDidEnterBackground:(UIApplication *)application { 34 | // 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. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | - (void)applicationWillEnterForeground:(UIApplication *)application { 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // 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. 44 | } 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /TWRefreshDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TWRefreshDemo/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 | -------------------------------------------------------------------------------- /TWRefreshDemo/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" : "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 | } -------------------------------------------------------------------------------- /TWRefreshDemo/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 | "filename" : "Default@2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "Default-568@2x.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | } 20 | ], 21 | "info" : { 22 | "version" : 1, 23 | "author" : "xcode" 24 | } 25 | } -------------------------------------------------------------------------------- /TWRefreshDemo/Images.xcassets/LaunchImage.launchimage/Default-568@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo/Images.xcassets/LaunchImage.launchimage/Default-568@2x.png -------------------------------------------------------------------------------- /TWRefreshDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /TWRefreshDemo/Images.xcassets/indicator-center-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "indicator-center-icon@2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x", 15 | "filename" : "indicator-center-icon@3.png" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TWRefreshDemo/Images.xcassets/indicator-center-icon.imageset/indicator-center-icon@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo/Images.xcassets/indicator-center-icon.imageset/indicator-center-icon@2.png -------------------------------------------------------------------------------- /TWRefreshDemo/Images.xcassets/indicator-center-icon.imageset/indicator-center-icon@3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinst/TWRefresh/2e65961bc60f57f239a9c265270e74a111804a23/TWRefreshDemo/Images.xcassets/indicator-center-icon.imageset/indicator-center-icon@3.png -------------------------------------------------------------------------------- /TWRefreshDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.ieasynote.refresh.$(PRODUCT_NAME:rfc1034identifier) 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 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshCollectionViewController.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TWRefreshCollectionViewController : UIViewController 12 | 13 | @end 14 | 15 | @interface TWCollectionViewCell : UICollectionViewCell 16 | 17 | - (void) showWithIndex:(NSInteger) index; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshCollectionViewController.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import "TWRefreshCollectionViewController.h" 10 | #import 11 | 12 | @interface TWRefreshCollectionViewController (CollectionViewDelegate) 13 | 14 | @end 15 | 16 | @implementation TWRefreshCollectionViewController 17 | { 18 | TWRefreshCollectionView *_collectionView; 19 | int _count; 20 | } 21 | 22 | - (void) dealloc { 23 | NSLog(@"############## dealloc: %@", self); 24 | } 25 | 26 | - (void) viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | self.title = @"TWRrefrsh Collection View"; 30 | 31 | self.view.backgroundColor = [UIColor whiteColor]; 32 | _count = 20; 33 | 34 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; 35 | [flowLayout setMinimumLineSpacing:0]; 36 | [flowLayout setMinimumInteritemSpacing:0]; 37 | _collectionView = [[TWRefreshCollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout refreshType:TWRefreshTypeTop|TWRefreshTypeBottom]; 38 | _collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 39 | _collectionView.delegate = self; 40 | _collectionView.dataSource = self; 41 | _collectionView.refreshDelegate = self; 42 | _collectionView.backgroundColor = [UIColor clearColor]; 43 | [_collectionView registerClass:[TWCollectionViewCell class] forCellWithReuseIdentifier:@"collection_view_cell"]; 44 | [self.view addSubview:_collectionView]; 45 | } 46 | 47 | @end 48 | 49 | @implementation TWRefreshCollectionViewController (CollectionViewDelegate) 50 | 51 | - (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 52 | return _count; 53 | } 54 | 55 | - (UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 56 | TWCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"collection_view_cell" forIndexPath:indexPath]; 57 | [cell showWithIndex:indexPath.row]; 58 | return cell; 59 | } 60 | 61 | - (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 62 | } 63 | 64 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { 65 | return CGSizeMake(self.view.frame.size.width/2, self.view.frame.size.height/3); 66 | } 67 | 68 | - (void) stopHeader { 69 | _count = 10; 70 | [_collectionView stopHeaderRefreshing]; 71 | [_collectionView reloadData]; 72 | } 73 | 74 | - (void) stopFooter { 75 | _count+=10; 76 | [_collectionView stopFooterRefreshing]; 77 | [_collectionView reloadData]; 78 | } 79 | 80 | - (void) beginRefreshHeader:(TWRefreshCollectionView *)collectionView { 81 | [self performSelector:@selector(stopHeader) withObject:nil afterDelay:2.0]; 82 | } 83 | 84 | - (void) beginRefreshFooter:(TWRefreshCollectionView *)collectionView { 85 | [self performSelector:@selector(stopFooter) withObject:nil afterDelay:2.0]; 86 | } 87 | 88 | @end 89 | 90 | @implementation TWCollectionViewCell 91 | { 92 | UIView *_view; 93 | UILabel *_label; 94 | } 95 | 96 | - (id) initWithFrame:(CGRect)frame { 97 | self = [super initWithFrame:frame]; 98 | if (self) { 99 | _view = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 2, 2)]; 100 | _view.layer.borderColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0].CGColor; 101 | _view.backgroundColor = [UIColor colorWithRed:0.93 green:0.93 blue:0.93 alpha:1.0]; 102 | _view.layer.borderWidth = 1.0f; 103 | [self.contentView addSubview:_view]; 104 | _label = [[UILabel alloc] initWithFrame:_view.bounds]; 105 | _label.font = [UIFont boldSystemFontOfSize:64]; 106 | _label.textAlignment = NSTextAlignmentCenter; 107 | _label.shadowColor = [UIColor blackColor]; 108 | _label.shadowOffset = CGSizeMake(1, 1); 109 | [_view addSubview:_label]; 110 | } 111 | return self; 112 | } 113 | 114 | - (void) showWithIndex:(NSInteger)index { 115 | _label.text = [NSString stringWithFormat:@"%ld", (long)index]; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshScrollViewController.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TWRefreshScrollViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshScrollViewController.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import "TWRefreshScrollViewController.h" 10 | #import 11 | #import 12 | 13 | @interface TWRefreshScrollViewController (ScrollViewDelegate) 14 | 15 | @end 16 | 17 | @implementation TWRefreshScrollViewController 18 | { 19 | UIScrollView *_scrollView; 20 | int _count; 21 | } 22 | 23 | - (void) viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | _count = 20; 28 | 29 | _scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 30 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; 31 | _scrollView.delegate = self; 32 | _scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, MAX(_scrollView.contentSize.height, _scrollView.frame.size.height+1)); 33 | [_scrollView setRefreshHeaderWithIndicatorClass:[TWRefreshIndicatorView class]]; 34 | [_scrollView setRefreshFooterWithIndicatorClass:[TWRefreshIndicatorView class]]; 35 | __weak id weakSelf = self; 36 | [_scrollView setRefreshHeaderCallback:^{ 37 | [weakSelf handleRefreshHeader]; 38 | }]; 39 | [_scrollView setRefreshFooterCallback:^{ 40 | [weakSelf handleRefreshFooter]; 41 | }]; 42 | [self.view addSubview:_scrollView]; 43 | } 44 | 45 | - (void) stopHeader { 46 | [_scrollView stopHeaderRefreshing]; 47 | } 48 | 49 | - (void) stopFooter { 50 | [_scrollView stopFooterRefreshing]; 51 | } 52 | 53 | - (void) handleRefreshHeader { 54 | [self performSelector:@selector(stopHeader) withObject:nil afterDelay:2.0]; 55 | } 56 | 57 | - (void) handleRefreshFooter { 58 | [self performSelector:@selector(stopFooter) withObject:nil afterDelay:2.0]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshTableViewController.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TWRefreshTableViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TWRefreshDemo/TWRefreshTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshTableViewController.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 16/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import "TWRefreshTableViewController.h" 10 | #import 11 | 12 | @interface TWRefreshTableViewController (TableDelegate) 13 | 14 | @end 15 | 16 | @implementation TWRefreshTableViewController 17 | { 18 | TWRefreshTableView *_tableView; 19 | int _count; 20 | } 21 | 22 | - (void) dealloc { 23 | NSLog(@"############## dealloc: %@", self); 24 | } 25 | 26 | - (void) viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | self.title = @"TWRrefrsh Table View"; 30 | 31 | self.view.backgroundColor = [UIColor whiteColor]; 32 | 33 | _count = 10; 34 | 35 | _tableView = [[TWRefreshTableView alloc] initWithFrame:self.view.bounds refreshType:TWRefreshTypeTop|TWRefreshTypeBottom]; 36 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 37 | _tableView.delegate = self; 38 | _tableView.dataSource = self; 39 | _tableView.refreshDelegate = self; 40 | [self.view addSubview:_tableView]; 41 | } 42 | 43 | @end 44 | 45 | @implementation TWRefreshTableViewController (TableDelegate) 46 | 47 | - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 48 | return _count; 49 | } 50 | 51 | - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 52 | return 60; 53 | } 54 | 55 | - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 56 | static NSString *cellIdentifier = @"cell_identifier"; 57 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 58 | if (!cell) { 59 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 60 | } 61 | cell.textLabel.text = [NSString stringWithFormat:@"What king of data is here. %ld", (long)indexPath.row]; 62 | return cell; 63 | } 64 | 65 | - (void) stopHeader { 66 | _count = 10; 67 | [_tableView stopHeaderRefreshing]; 68 | [_tableView reloadData]; 69 | } 70 | 71 | - (void) stopFooter { 72 | _count+=10; 73 | [_tableView stopFooterRefreshing]; 74 | [_tableView reloadData]; 75 | } 76 | 77 | - (void) beginRefreshHeader:(TWRefreshTableView *)tableView { 78 | [self performSelector:@selector(stopHeader) withObject:nil afterDelay:2.0]; 79 | } 80 | 81 | - (void) beginRefreshFooter:(TWRefreshTableView *)tableView { 82 | [self performSelector:@selector(stopFooter) withObject:nil afterDelay:2.0]; 83 | } 84 | 85 | @end -------------------------------------------------------------------------------- /TWRefreshDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /TWRefreshDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | 12 | #import "TWRefreshTableViewController.h" 13 | #import "TWRefreshCollectionViewController.h" 14 | #import "TWRefreshScrollViewController.h" 15 | 16 | @interface ViewController () 17 | 18 | @end 19 | 20 | @interface ViewController (TableDelegate) 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | { 26 | TWRefreshTableView *_tableView; 27 | NSInteger _count; 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | // Do any additional setup after loading the view, typically from a nib. 33 | 34 | self.title = @"TWRrefrsh"; 35 | 36 | _count = 23; 37 | 38 | UIBarButtonItem *disableHeader = [[UIBarButtonItem alloc] initWithTitle:@"DH" style:UIBarButtonItemStylePlain target:self action:@selector(toggleRefreshHeaderEnabledNo:)]; 39 | UIBarButtonItem *disableFooter = [[UIBarButtonItem alloc] initWithTitle:@"DF" style:UIBarButtonItemStylePlain target:self action:@selector(toggleRefreshFooterEnabledNo:)]; 40 | self.navigationItem.rightBarButtonItems = @[disableHeader, disableFooter]; 41 | 42 | UIBarButtonItem *enableHeader = [[UIBarButtonItem alloc] initWithTitle:@"EH" style:UIBarButtonItemStylePlain target:self action:@selector(toggleRefreshHeaderEnabledYes:)]; 43 | UIBarButtonItem *enableFooter = [[UIBarButtonItem alloc] initWithTitle:@"EF" style:UIBarButtonItemStylePlain target:self action:@selector(toggleRefreshFooterEnabledYes:)]; 44 | self.navigationItem.leftBarButtonItems = @[enableHeader, enableFooter]; 45 | 46 | _tableView = [[TWRefreshTableView alloc] initWithFrame:self.view.bounds refreshType:TWRefreshTypeTop|TWRefreshTypeBottom]; 47 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 48 | _tableView.delegate = self; 49 | _tableView.dataSource = self; 50 | _tableView.refreshDelegate = self; 51 | 52 | [self.view addSubview:_tableView]; 53 | } 54 | 55 | - (void) toggleRefreshHeaderEnabledNo:(UIBarButtonItem*) sender { 56 | [_tableView setRefreshHeaderEnabled:NO]; 57 | } 58 | 59 | - (void) toggleRefreshHeaderEnabledYes:(UIBarButtonItem*) sender { 60 | [_tableView setRefreshHeaderEnabled:YES]; 61 | } 62 | 63 | - (void) toggleRefreshFooterEnabledNo:(UIBarButtonItem*) sender { 64 | [_tableView setRefreshFooterEnabled:NO]; 65 | } 66 | 67 | - (void) toggleRefreshFooterEnabledYes:(UIBarButtonItem*) sender { 68 | [_tableView setRefreshFooterEnabled:YES]; 69 | } 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; 73 | // Dispose of any resources that can be recreated. 74 | } 75 | 76 | @end 77 | 78 | @implementation ViewController (TableDelegate) 79 | 80 | - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 81 | return _count; 82 | } 83 | 84 | - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 85 | return 60; 86 | } 87 | 88 | - (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 89 | return 44; 90 | } 91 | 92 | - (UIView*) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { 93 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 44)]; 94 | view.autoresizingMask = UIViewAutoresizingFlexibleWidth; 95 | view.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]; 96 | UILabel *label = [[UILabel alloc] initWithFrame:view.bounds]; 97 | label.textAlignment = NSTextAlignmentCenter; 98 | label.font = [UIFont systemFontOfSize:14]; 99 | label.text = @"This is a section header"; 100 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth; 101 | [view addSubview:label]; 102 | return view; 103 | } 104 | 105 | - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 106 | static NSString *cellIdentifier = @"cell_identifier"; 107 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 108 | if (!cell) { 109 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 110 | } 111 | cell.accessoryType = indexPath.row<3? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; 112 | cell.textLabel.textColor = indexPath.row<3? [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0f] : [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0f]; 113 | if (indexPath.row==0) { 114 | cell.textLabel.text = @"Refresh TableView"; 115 | } 116 | else if (indexPath.row==1) { 117 | cell.textLabel.text = @"Refresh CollectionView"; 118 | } 119 | else if (indexPath.row==2) { 120 | cell.textLabel.text = @"Refresh ScrollView"; 121 | } 122 | else { 123 | cell.textLabel.text = [NSString stringWithFormat:@"What kind of data here %ld", (long)indexPath.row]; 124 | } 125 | return cell; 126 | } 127 | 128 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 129 | if (indexPath.row==0) { 130 | TWRefreshTableViewController *vc = [[TWRefreshTableViewController alloc] init]; 131 | [self.navigationController pushViewController:vc animated:YES]; 132 | } 133 | else if (indexPath.row==1) { 134 | TWRefreshCollectionViewController *vc = [[TWRefreshCollectionViewController alloc] init]; 135 | [self.navigationController pushViewController:vc animated:YES]; 136 | } 137 | else if (indexPath.row==2) { 138 | TWRefreshScrollViewController *vc = [[TWRefreshScrollViewController alloc] init]; 139 | [self.navigationController pushViewController:vc animated:YES]; 140 | } 141 | } 142 | 143 | - (void) stopHeader { 144 | _count = 3; 145 | [_tableView stopHeaderRefreshing]; 146 | [_tableView reloadData]; 147 | } 148 | 149 | - (void) stopFooter { 150 | _count += 8; 151 | [_tableView stopFooterRefreshing]; 152 | [_tableView reloadData]; 153 | } 154 | 155 | - (void) beginRefreshHeader:(TWRefreshTableView *)tableView { 156 | [self performSelector:@selector(stopHeader) withObject:nil afterDelay:4.0]; 157 | } 158 | 159 | - (void) beginRefreshFooter:(TWRefreshTableView *)tableView { 160 | [self performSelector:@selector(stopFooter) withObject:nil afterDelay:2.0]; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /TWRefreshDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TWRefreshDemo 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TWRefreshDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.ieasynote.refresh.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TWRefreshDemoTests/TWRefreshDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshDemoTests.m 3 | // TWRefreshDemoTests 4 | // 5 | // Created by Chris on 15/6/15. 6 | // Copyright (c) 2015 EasyBaking. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TWRefreshDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation TWRefreshDemoTests 17 | 18 | - (void)setUp { 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 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /TWRefreshTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TWRefreshTests/TWRefreshTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWRefreshTests.m 3 | // TWRefreshTests 4 | // 5 | // Created by Chris on 3/19/16. 6 | // Copyright © 2016 iEasyNote. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TWRefreshTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TWRefreshTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | --------------------------------------------------------------------------------