├── .gitignore ├── Demo ├── LGViewControllersDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── LGViewControllersDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── LGViewControllersDemo.xcscmblueprint ├── LGViewControllersDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── DemoCollectionViewController.h │ ├── DemoCollectionViewController.m │ ├── DemoScrollViewController.h │ ├── DemoScrollViewController.m │ ├── DemoTableViewController.h │ ├── DemoTableViewController.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── Launch-Pad-L@1x.png │ │ │ ├── Launch-Pad-L@2x.png │ │ │ ├── Launch-Pad-P@1x.png │ │ │ ├── Launch-Pad-P@2x.png │ │ │ ├── Launch-Phone-35-P@1x.png │ │ │ ├── Launch-Phone-35-P@2x.png │ │ │ ├── Launch-Phone-40-P@2x.png │ │ │ ├── Launch-Phone-47-P@2x.png │ │ │ ├── Launch-Phone-55-L@3x.png │ │ │ └── Launch-Phone-55-P@3x.png │ ├── Info.plist │ ├── LaunchScreen.storyboard │ ├── NavigationController.h │ ├── NavigationController.m │ ├── TableViewController.h │ ├── TableViewController.m │ └── main.m ├── Podfile ├── Podfile.lock ├── podsInstall.command └── podsUpdate.command ├── LGViewControllers.podspec ├── LGViewControllers ├── LGCollectionViewController │ ├── LGCollectionView.h │ ├── LGCollectionView.m │ ├── LGCollectionViewController.h │ └── LGCollectionViewController.m ├── LGScrollViewController │ ├── LGScrollView.h │ ├── LGScrollView.m │ ├── LGScrollViewController.h │ └── LGScrollViewController.m ├── LGTableViewController │ ├── LGTableView.h │ ├── LGTableView.m │ ├── LGTableViewController.h │ └── LGTableViewController.m ├── LGViewControllerAnimator │ ├── LGViewControllerAnimator.h │ └── LGViewControllerAnimator.m ├── LGViewControllers.h └── LGWebViewController │ ├── LGWebView.h │ ├── LGWebView.m │ ├── LGWebViewController.h │ └── LGWebViewController.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | 28 | ### 29 | ._* 30 | 31 | .hgignore 32 | 33 | .DS_Store 34 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A3ABCAC1AC42EE4003889C0 /* NavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A3ABCA91AC42EE4003889C0 /* NavigationController.m */; }; 11 | 4A3ABCAD1AC42EE4003889C0 /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A3ABCAB1AC42EE4003889C0 /* TableViewController.m */; }; 12 | 4A3ABCB11AC4302C003889C0 /* DemoScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A3ABCB01AC4302C003889C0 /* DemoScrollViewController.m */; }; 13 | 4A3ABCB41AC4303F003889C0 /* DemoTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A3ABCB31AC4303F003889C0 /* DemoTableViewController.m */; }; 14 | 4A3ABCB71AC43053003889C0 /* DemoCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A3ABCB61AC43053003889C0 /* DemoCollectionViewController.m */; }; 15 | 4A836B6F1BF4F59F008374ED /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A836B6E1BF4F59F008374ED /* LaunchScreen.storyboard */; }; 16 | 4AD365021AAF697400EE6F1A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD365011AAF697400EE6F1A /* main.m */; }; 17 | 4AD365051AAF697400EE6F1A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD365041AAF697400EE6F1A /* AppDelegate.m */; }; 18 | 4AD3650D1AAF697400EE6F1A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AD3650C1AAF697400EE6F1A /* Images.xcassets */; }; 19 | 7F51DFF3BB66312F12BC8BE8 /* libPods-LGViewControllersDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E0E8F7B42D50ED52044F660E /* libPods-LGViewControllersDemo.a */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 2B77219F3CFA36F3921E2476 /* Pods-LGViewControllersDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LGViewControllersDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-LGViewControllersDemo/Pods-LGViewControllersDemo.release.xcconfig"; sourceTree = ""; }; 24 | 4A3ABCA81AC42EE4003889C0 /* NavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationController.h; sourceTree = ""; }; 25 | 4A3ABCA91AC42EE4003889C0 /* NavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationController.m; sourceTree = ""; }; 26 | 4A3ABCAA1AC42EE4003889C0 /* TableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = ""; }; 27 | 4A3ABCAB1AC42EE4003889C0 /* TableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = ""; }; 28 | 4A3ABCAF1AC4302C003889C0 /* DemoScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoScrollViewController.h; sourceTree = ""; }; 29 | 4A3ABCB01AC4302C003889C0 /* DemoScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoScrollViewController.m; sourceTree = ""; }; 30 | 4A3ABCB21AC4303F003889C0 /* DemoTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoTableViewController.h; sourceTree = ""; }; 31 | 4A3ABCB31AC4303F003889C0 /* DemoTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoTableViewController.m; sourceTree = ""; }; 32 | 4A3ABCB51AC43053003889C0 /* DemoCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoCollectionViewController.h; sourceTree = ""; }; 33 | 4A3ABCB61AC43053003889C0 /* DemoCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoCollectionViewController.m; sourceTree = ""; }; 34 | 4A836B6E1BF4F59F008374ED /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 35 | 4AD364FC1AAF697400EE6F1A /* LGViewControllersDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LGViewControllersDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 4AD365001AAF697400EE6F1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 4AD365011AAF697400EE6F1A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | 4AD365031AAF697400EE6F1A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | 4AD365041AAF697400EE6F1A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 4AD3650C1AAF697400EE6F1A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 41 | 7B887A530616A117C959BA8C /* Pods-LGViewControllersDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LGViewControllersDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LGViewControllersDemo/Pods-LGViewControllersDemo.debug.xcconfig"; sourceTree = ""; }; 42 | E0E8F7B42D50ED52044F660E /* libPods-LGViewControllersDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LGViewControllersDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | 4AD364F91AAF697400EE6F1A /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | 7F51DFF3BB66312F12BC8BE8 /* libPods-LGViewControllersDemo.a in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 4A3ABCAE1AC42EE7003889C0 /* Demo View Controllers */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 4A3ABCAF1AC4302C003889C0 /* DemoScrollViewController.h */, 61 | 4A3ABCB01AC4302C003889C0 /* DemoScrollViewController.m */, 62 | 4A3ABCB21AC4303F003889C0 /* DemoTableViewController.h */, 63 | 4A3ABCB31AC4303F003889C0 /* DemoTableViewController.m */, 64 | 4A3ABCB51AC43053003889C0 /* DemoCollectionViewController.h */, 65 | 4A3ABCB61AC43053003889C0 /* DemoCollectionViewController.m */, 66 | ); 67 | name = "Demo View Controllers"; 68 | sourceTree = ""; 69 | }; 70 | 4AD364F31AAF697400EE6F1A = { 71 | isa = PBXGroup; 72 | children = ( 73 | 4AD364FE1AAF697400EE6F1A /* LGViewControllersDemo */, 74 | 4AD364FD1AAF697400EE6F1A /* Products */, 75 | E18FA7A6B41C918231772117 /* Pods */, 76 | 9312A34566155AFE9840D1FD /* Frameworks */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 4AD364FD1AAF697400EE6F1A /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 4AD364FC1AAF697400EE6F1A /* LGViewControllersDemo.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 4AD364FE1AAF697400EE6F1A /* LGViewControllersDemo */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 4AD365031AAF697400EE6F1A /* AppDelegate.h */, 92 | 4AD365041AAF697400EE6F1A /* AppDelegate.m */, 93 | 4A3ABCA81AC42EE4003889C0 /* NavigationController.h */, 94 | 4A3ABCA91AC42EE4003889C0 /* NavigationController.m */, 95 | 4A3ABCAA1AC42EE4003889C0 /* TableViewController.h */, 96 | 4A3ABCAB1AC42EE4003889C0 /* TableViewController.m */, 97 | 4A3ABCAE1AC42EE7003889C0 /* Demo View Controllers */, 98 | 4AD3650C1AAF697400EE6F1A /* Images.xcassets */, 99 | 4AD364FF1AAF697400EE6F1A /* Supporting Files */, 100 | 4A836B6E1BF4F59F008374ED /* LaunchScreen.storyboard */, 101 | ); 102 | path = LGViewControllersDemo; 103 | sourceTree = ""; 104 | }; 105 | 4AD364FF1AAF697400EE6F1A /* Supporting Files */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 4AD365001AAF697400EE6F1A /* Info.plist */, 109 | 4AD365011AAF697400EE6F1A /* main.m */, 110 | ); 111 | name = "Supporting Files"; 112 | sourceTree = ""; 113 | }; 114 | 9312A34566155AFE9840D1FD /* Frameworks */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | E0E8F7B42D50ED52044F660E /* libPods-LGViewControllersDemo.a */, 118 | ); 119 | name = Frameworks; 120 | sourceTree = ""; 121 | }; 122 | E18FA7A6B41C918231772117 /* Pods */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 7B887A530616A117C959BA8C /* Pods-LGViewControllersDemo.debug.xcconfig */, 126 | 2B77219F3CFA36F3921E2476 /* Pods-LGViewControllersDemo.release.xcconfig */, 127 | ); 128 | name = Pods; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXNativeTarget section */ 134 | 4AD364FB1AAF697400EE6F1A /* LGViewControllersDemo */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = 4AD3651F1AAF697400EE6F1A /* Build configuration list for PBXNativeTarget "LGViewControllersDemo" */; 137 | buildPhases = ( 138 | AB00FCB2AB99F54B8415BB14 /* Check Pods Manifest.lock */, 139 | 4AD364F81AAF697400EE6F1A /* Sources */, 140 | 4AD364F91AAF697400EE6F1A /* Frameworks */, 141 | 4AD364FA1AAF697400EE6F1A /* Resources */, 142 | 3251F0FCE8BEE2089F55D4E4 /* Copy Pods Resources */, 143 | DB088874A69AA1CF41DD02A9 /* Embed Pods Frameworks */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = LGViewControllersDemo; 150 | productName = LGViewControllersDemo; 151 | productReference = 4AD364FC1AAF697400EE6F1A /* LGViewControllersDemo.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 4AD364F41AAF697400EE6F1A /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | LastUpgradeCheck = 0710; 161 | ORGANIZATIONNAME = "Grigory Lutkov"; 162 | TargetAttributes = { 163 | 4AD364FB1AAF697400EE6F1A = { 164 | CreatedOnToolsVersion = 6.2; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = 4AD364F71AAF697400EE6F1A /* Build configuration list for PBXProject "LGViewControllersDemo" */; 169 | compatibilityVersion = "Xcode 3.2"; 170 | developmentRegion = English; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 4AD364F31AAF697400EE6F1A; 177 | productRefGroup = 4AD364FD1AAF697400EE6F1A /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 4AD364FB1AAF697400EE6F1A /* LGViewControllersDemo */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 4AD364FA1AAF697400EE6F1A /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 4A836B6F1BF4F59F008374ED /* LaunchScreen.storyboard in Resources */, 192 | 4AD3650D1AAF697400EE6F1A /* Images.xcassets in Resources */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXResourcesBuildPhase section */ 197 | 198 | /* Begin PBXShellScriptBuildPhase section */ 199 | 3251F0FCE8BEE2089F55D4E4 /* Copy Pods Resources */ = { 200 | isa = PBXShellScriptBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | ); 204 | inputPaths = ( 205 | ); 206 | name = "Copy Pods Resources"; 207 | outputPaths = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | shellPath = /bin/sh; 211 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LGViewControllersDemo/Pods-LGViewControllersDemo-resources.sh\"\n"; 212 | showEnvVarsInLog = 0; 213 | }; 214 | AB00FCB2AB99F54B8415BB14 /* Check Pods Manifest.lock */ = { 215 | isa = PBXShellScriptBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputPaths = ( 220 | ); 221 | name = "Check Pods Manifest.lock"; 222 | outputPaths = ( 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | shellPath = /bin/sh; 226 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 227 | showEnvVarsInLog = 0; 228 | }; 229 | DB088874A69AA1CF41DD02A9 /* Embed Pods Frameworks */ = { 230 | isa = PBXShellScriptBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | ); 234 | inputPaths = ( 235 | ); 236 | name = "Embed Pods Frameworks"; 237 | outputPaths = ( 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | shellPath = /bin/sh; 241 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LGViewControllersDemo/Pods-LGViewControllersDemo-frameworks.sh\"\n"; 242 | showEnvVarsInLog = 0; 243 | }; 244 | /* End PBXShellScriptBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | 4AD364F81AAF697400EE6F1A /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 4A3ABCB41AC4303F003889C0 /* DemoTableViewController.m in Sources */, 252 | 4A3ABCAC1AC42EE4003889C0 /* NavigationController.m in Sources */, 253 | 4AD365051AAF697400EE6F1A /* AppDelegate.m in Sources */, 254 | 4AD365021AAF697400EE6F1A /* main.m in Sources */, 255 | 4A3ABCB71AC43053003889C0 /* DemoCollectionViewController.m in Sources */, 256 | 4A3ABCAD1AC42EE4003889C0 /* TableViewController.m in Sources */, 257 | 4A3ABCB11AC4302C003889C0 /* DemoScrollViewController.m in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 4AD3651D1AAF697400EE6F1A /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_UNREACHABLE_CODE = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 282 | COPY_PHASE_STRIP = NO; 283 | ENABLE_STRICT_OBJC_MSGSEND = YES; 284 | ENABLE_TESTABILITY = YES; 285 | GCC_C_LANGUAGE_STANDARD = gnu99; 286 | GCC_DYNAMIC_NO_PIC = NO; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 294 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 295 | GCC_WARN_UNDECLARED_SELECTOR = YES; 296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 297 | GCC_WARN_UNUSED_FUNCTION = YES; 298 | GCC_WARN_UNUSED_VARIABLE = YES; 299 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 300 | MTL_ENABLE_DEBUG_INFO = YES; 301 | ONLY_ACTIVE_ARCH = YES; 302 | SDKROOT = iphoneos; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | }; 305 | name = Debug; 306 | }; 307 | 4AD3651E1AAF697400EE6F1A /* Release */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_MODULES = YES; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_CONSTANT_CONVERSION = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_UNREACHABLE_CODE = YES; 323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 324 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 325 | COPY_PHASE_STRIP = NO; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 331 | GCC_WARN_UNDECLARED_SELECTOR = YES; 332 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 333 | GCC_WARN_UNUSED_FUNCTION = YES; 334 | GCC_WARN_UNUSED_VARIABLE = YES; 335 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 336 | MTL_ENABLE_DEBUG_INFO = NO; 337 | SDKROOT = iphoneos; 338 | TARGETED_DEVICE_FAMILY = "1,2"; 339 | VALIDATE_PRODUCT = YES; 340 | }; 341 | name = Release; 342 | }; 343 | 4AD365201AAF697400EE6F1A /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | baseConfigurationReference = 7B887A530616A117C959BA8C /* Pods-LGViewControllersDemo.debug.xcconfig */; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 349 | CODE_SIGN_IDENTITY = "iPhone Developer"; 350 | INFOPLIST_FILE = LGViewControllersDemo/Info.plist; 351 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 352 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 353 | PRODUCT_BUNDLE_IDENTIFIER = "com.test.$(PRODUCT_NAME)"; 354 | PRODUCT_NAME = "$(TARGET_NAME)"; 355 | }; 356 | name = Debug; 357 | }; 358 | 4AD365211AAF697400EE6F1A /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | baseConfigurationReference = 2B77219F3CFA36F3921E2476 /* Pods-LGViewControllersDemo.release.xcconfig */; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 364 | CODE_SIGN_IDENTITY = "iPhone Developer"; 365 | INFOPLIST_FILE = LGViewControllersDemo/Info.plist; 366 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | PRODUCT_BUNDLE_IDENTIFIER = "com.test.$(PRODUCT_NAME)"; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | }; 371 | name = Release; 372 | }; 373 | /* End XCBuildConfiguration section */ 374 | 375 | /* Begin XCConfigurationList section */ 376 | 4AD364F71AAF697400EE6F1A /* Build configuration list for PBXProject "LGViewControllersDemo" */ = { 377 | isa = XCConfigurationList; 378 | buildConfigurations = ( 379 | 4AD3651D1AAF697400EE6F1A /* Debug */, 380 | 4AD3651E1AAF697400EE6F1A /* Release */, 381 | ); 382 | defaultConfigurationIsVisible = 0; 383 | defaultConfigurationName = Release; 384 | }; 385 | 4AD3651F1AAF697400EE6F1A /* Build configuration list for PBXNativeTarget "LGViewControllersDemo" */ = { 386 | isa = XCConfigurationList; 387 | buildConfigurations = ( 388 | 4AD365201AAF697400EE6F1A /* Debug */, 389 | 4AD365211AAF697400EE6F1A /* Release */, 390 | ); 391 | defaultConfigurationIsVisible = 0; 392 | defaultConfigurationName = Release; 393 | }; 394 | /* End XCConfigurationList section */ 395 | }; 396 | rootObject = 4AD364F41AAF697400EE6F1A /* Project object */; 397 | } 398 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo.xcworkspace/xcshareddata/LGViewControllersDemo.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "D1921A8DD95A49CEC02E603F7AFFE0B0D9E417F2", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "E2313CDD6C163A5D9D19AAF2AA5600F33F4ACE5E" : 0, 8 | "4029278A4951E3FF130BF388365315DBA49FB6A4" : 0, 9 | "D1921A8DD95A49CEC02E603F7AFFE0B0D9E417F2" : 0 10 | }, 11 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "136A9ED3-45AA-42D8-8C59-D11727B9AB51", 12 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 13 | "E2313CDD6C163A5D9D19AAF2AA5600F33F4ACE5E" : "LGPlaceholderView", 14 | "4029278A4951E3FF130BF388365315DBA49FB6A4" : "LGRefreshView", 15 | "D1921A8DD95A49CEC02E603F7AFFE0B0D9E417F2" : "LGViewControllers" 16 | }, 17 | "DVTSourceControlWorkspaceBlueprintNameKey" : "LGViewControllersDemo", 18 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 19 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Demo\/LGViewControllersDemo.xcworkspace", 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 21 | { 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Friend-LGA\/LGRefreshView.git", 23 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 24 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "4029278A4951E3FF130BF388365315DBA49FB6A4" 25 | }, 26 | { 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Friend-LGA\/LGViewControllers.git", 28 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 29 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D1921A8DD95A49CEC02E603F7AFFE0B0D9E417F2" 30 | }, 31 | { 32 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Friend-LGA\/LGPlaceholderView.git", 33 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E2313CDD6C163A5D9D19AAF2AA5600F33F4ACE5E" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "NavigationController.h" 11 | #import "TableViewController.h" 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | TableViewController *tableViewController = [TableViewController new]; 18 | NavigationController *navigationController = [[NavigationController alloc] initWithRootViewController:tableViewController]; 19 | 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | self.window.rootViewController = navigationController; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application 29 | { 30 | // 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. 31 | // 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. 32 | } 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application 35 | { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application 41 | { 42 | // 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. 43 | } 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application 46 | { 47 | // 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. 48 | } 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application 51 | { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/DemoCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoCollectionViewController.h 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 26.03.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "LGCollectionViewController.h" 10 | 11 | @interface DemoCollectionViewController : LGCollectionViewController 12 | 13 | - (id)initWithTitle:(NSString *)title; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/DemoCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoCollectionViewController.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 26.03.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "DemoCollectionViewController.h" 10 | 11 | @interface DemoCollectionViewCell : UICollectionViewCell 12 | 13 | @property (strong, nonatomic) UILabel *textLabel; 14 | 15 | @end 16 | 17 | @implementation DemoCollectionViewCell 18 | 19 | - (id)initWithFrame:(CGRect)frame 20 | { 21 | self = [super initWithFrame:frame]; 22 | if (self) 23 | { 24 | self.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.f]; 25 | 26 | _textLabel = [UILabel new]; 27 | _textLabel.backgroundColor = [UIColor clearColor]; 28 | _textLabel.font = [UIFont systemFontOfSize:16.f]; 29 | [self addSubview:_textLabel]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)layoutSubviews 35 | { 36 | [super layoutSubviews]; 37 | 38 | [_textLabel sizeToFit]; 39 | _textLabel.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); 40 | _textLabel.frame = CGRectIntegral(_textLabel.frame); 41 | } 42 | 43 | - (void)setHighlighted:(BOOL)highlighted 44 | { 45 | if (highlighted) self.backgroundColor = [UIColor colorWithRed:0.f green:0.5 blue:1.f alpha:1.f]; 46 | else self.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.f]; 47 | } 48 | 49 | @end 50 | 51 | #pragma mark - 52 | 53 | @interface DemoCollectionViewController () 54 | 55 | @property (assign, nonatomic, getter=isInitialized) BOOL initialized; 56 | 57 | @property (strong, nonatomic) NSMutableArray *dataArray; 58 | 59 | @property (assign, nonatomic, getter=isLoadingData) BOOL loadingData; 60 | 61 | @end 62 | 63 | @implementation DemoCollectionViewController 64 | 65 | - (id)initWithTitle:(NSString *)title 66 | { 67 | self = [super initWithPlaceholderViewEnabled:YES refreshViewEnabled:YES]; 68 | if (self) 69 | { 70 | self.title = title; 71 | 72 | self.view.backgroundColor = [UIColor whiteColor]; 73 | 74 | [self.collectionView registerClass:[DemoCollectionViewCell class] forCellWithReuseIdentifier:@"cell"]; 75 | 76 | [self.collectionView.placeholderView showActivityIndicatorAnimated:NO completionHandler:nil]; 77 | 78 | self.collectionView.alwaysBounceVertical = YES; 79 | 80 | [self addItemsToDataArray]; 81 | } 82 | return self; 83 | } 84 | 85 | #pragma mark - Dealloc 86 | 87 | - (void)dealloc 88 | { 89 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 90 | } 91 | 92 | #pragma mark - Appearing 93 | 94 | - (void)viewWillLayoutSubviews 95 | { 96 | [super viewWillLayoutSubviews]; 97 | 98 | NSUInteger numberOfCellsInARow = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? 3 : 6); 99 | if (self.view.frame.size.width > self.view.frame.size.height) numberOfCellsInARow += 2; 100 | 101 | _loadingData = YES; 102 | 103 | [self.collectionView setViewWidth:self.view.frame.size.width 104 | cellAspect:1.f 105 | numberOfCellsInARow:numberOfCellsInARow 106 | cellInsets:2.f 107 | sectionInsets:UIEdgeInsetsMake(2.f, 2.f, 2.f, 2.f) 108 | headerReferenceSize:CGSizeZero 109 | footerReferenceSize:CGSizeZero 110 | scrollDirection:UICollectionViewScrollDirectionVertical]; 111 | 112 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void) 113 | { 114 | _loadingData = NO; 115 | }); 116 | } 117 | 118 | - (void)viewDidAppear:(BOOL)animated 119 | { 120 | [super viewDidAppear:animated]; 121 | 122 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void) 123 | { 124 | [self.collectionView.placeholderView dismissAnimated:YES completionHandler:nil]; 125 | 126 | _initialized = YES; 127 | }); 128 | } 129 | 130 | #pragma mark - UICollectionView Data Source 131 | 132 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 133 | { 134 | return 1; 135 | } 136 | 137 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 138 | { 139 | return _dataArray.count; 140 | } 141 | 142 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 143 | { 144 | DemoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath]; 145 | 146 | cell.textLabel.text = _dataArray[indexPath.row]; 147 | 148 | [cell setNeedsLayout]; 149 | 150 | return cell; 151 | } 152 | 153 | #pragma mark - UICollectionView Delegate 154 | 155 | - (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath 156 | { 157 | NSLog(@"Collection view did highlight cell section: %i item: %i", (int)indexPath.section, (int)indexPath.row); 158 | } 159 | 160 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 161 | { 162 | NSLog(@"Collection view did select cell section: %i item: %i", (int)indexPath.section, (int)indexPath.row); 163 | } 164 | 165 | #pragma mark - UIScrollView Delegate 166 | 167 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 168 | { 169 | if (!self.isLoadingData && self.isInitialized && 170 | scrollView.contentOffset.y + scrollView.contentInset.top + scrollView.frame.size.height >= scrollView.contentSize.height - 100.f) 171 | { 172 | _loadingData = YES; 173 | 174 | [self.collectionView performBatchUpdates:^(void) 175 | { 176 | [self addItemsToDataArray]; 177 | 178 | NSMutableArray *array = [NSMutableArray new]; 179 | 180 | NSUInteger count = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? 30 : 70); 181 | 182 | for (NSUInteger i=_dataArray.count-count; i<_dataArray.count; i++) 183 | [array addObject:[NSIndexPath indexPathForItem:i inSection:0]]; 184 | 185 | [self.collectionView insertItemsAtIndexPaths:array]; 186 | } 187 | completion:^(BOOL finished) 188 | { 189 | _loadingData = NO; 190 | }]; 191 | } 192 | } 193 | 194 | #pragma mark - 195 | 196 | - (void)refreshActions 197 | { 198 | [_dataArray removeAllObjects]; 199 | 200 | [self addItemsToDataArray]; 201 | 202 | [self.collectionView reloadData]; 203 | 204 | [UIView transitionWithView:self.collectionView duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:nil]; 205 | 206 | [self.collectionView.refreshView endRefreshing]; 207 | } 208 | 209 | - (void)addItemsToDataArray 210 | { 211 | if (!_dataArray) 212 | _dataArray = [NSMutableArray new]; 213 | 214 | NSUInteger count = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? 30 : 70); 215 | 216 | for (NSUInteger i=0; i 12 | 13 | @property (strong, nonatomic) UILabel *label; 14 | @property (strong, nonatomic) UITextField *textField; 15 | 16 | @end 17 | 18 | @implementation DemoScrollViewController 19 | 20 | - (id)initWithTitle:(NSString *)title 21 | { 22 | self = [super initWithPlaceholderViewEnabled:YES refreshViewEnabled:YES]; 23 | if (self) 24 | { 25 | self.title = title; 26 | 27 | self.view.backgroundColor = [UIColor whiteColor]; 28 | 29 | _label = [UILabel new]; 30 | _label.text = @"Updated never"; 31 | _label.textColor = [UIColor blackColor]; 32 | [self.scrollView addSubview:_label]; 33 | 34 | _textField = [UITextField new]; 35 | _textField.placeholder = @"UITextField"; 36 | _textField.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.f]; 37 | [self.scrollView addSubview:_textField]; 38 | 39 | self.scrollView.alwaysBounceVertical = YES; 40 | 41 | UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureAction)]; 42 | gesture.delegate = self; 43 | [self.scrollView addGestureRecognizer:gesture]; 44 | 45 | self.keyboardShowHideObserverEnabled = YES; 46 | 47 | [self.scrollView.placeholderView showActivityIndicatorAnimated:NO completionHandler:nil]; 48 | } 49 | return self; 50 | } 51 | 52 | #pragma mark - Dealloc 53 | 54 | - (void)dealloc 55 | { 56 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 57 | } 58 | 59 | #pragma mark - Appearing 60 | 61 | - (void)viewWillLayoutSubviews 62 | { 63 | [super viewWillLayoutSubviews]; 64 | 65 | CGFloat shift = 20.f; 66 | 67 | [_label sizeToFit]; 68 | _label.center = CGPointMake(self.view.frame.size.width/2, shift+_label.frame.size.height/2); 69 | _label.frame = CGRectIntegral(_label.frame); 70 | 71 | _textField.frame = CGRectMake(10.f, self.scrollView.frame.size.height-self.scrollView.contentInset.top-shift-44.f, self.view.frame.size.width-20.f, 44.f); 72 | _textField.frame = CGRectIntegral(_textField.frame); 73 | 74 | self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, _textField.frame.origin.y+_textField.frame.size.height+shift); 75 | } 76 | 77 | - (void)viewDidAppear:(BOOL)animated 78 | { 79 | [super viewDidAppear:animated]; 80 | 81 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void) 82 | { 83 | [self.scrollView.placeholderView dismissAnimated:YES completionHandler:nil]; 84 | }); 85 | } 86 | 87 | #pragma mark - Refresh 88 | 89 | - (void)refreshActions 90 | { 91 | NSDate *date = [NSDate date]; 92 | 93 | NSDateFormatter *dateFormatter = [NSDateFormatter new]; 94 | dateFormatter.dateFormat = @"dd.MM.yyyy HH:mm:ss"; 95 | 96 | _label.text = [NSString stringWithFormat:@"Updated %@", [dateFormatter stringFromDate:date]]; 97 | 98 | CGFloat shift = 20.f; 99 | 100 | [_label sizeToFit]; 101 | _label.center = CGPointMake(self.scrollView.frame.size.width/2, shift+_label.frame.size.height/2); 102 | 103 | [UIView transitionWithView:_label duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:nil]; 104 | 105 | [self.scrollView.refreshView endRefreshing]; 106 | } 107 | 108 | #pragma mark - Keyboard 109 | 110 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight 111 | { 112 | [super keyboardWillShowHideActionsAppear:appear keyboardHeight:keyboardHeight]; 113 | 114 | if (appear) 115 | [self.scrollView scrollRectToVisible:_textField.frame animated:NO]; 116 | } 117 | 118 | #pragma mark - UIGestureRecognizer Delegate 119 | 120 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 121 | { 122 | return ![touch.view isKindOfClass:[UIControl class]]; 123 | } 124 | 125 | #pragma mark - 126 | 127 | - (void)gestureAction 128 | { 129 | [self.view endEditing:YES]; 130 | } 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/DemoTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoTableViewController.h 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 26.03.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "LGTableViewController.h" 10 | 11 | @interface DemoTableViewController : LGTableViewController 12 | 13 | - (id)initWithTitle:(NSString *)title; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/DemoTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoTableViewController.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 26.03.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "DemoTableViewController.h" 10 | 11 | @interface DemoTableViewCell : UITableViewCell 12 | 13 | @end 14 | 15 | @implementation DemoTableViewCell 16 | 17 | - (id)initWithFrame:(CGRect)frame 18 | { 19 | self = [super initWithFrame:frame]; 20 | if (self) 21 | { 22 | self.backgroundColor = [UIColor whiteColor]; 23 | 24 | self.selectionStyle = UITableViewCellSelectionStyleNone; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)layoutSubviews 30 | { 31 | self.textLabel.font = [UIFont systemFontOfSize:16.0]; 32 | self.textLabel.numberOfLines = 0; 33 | 34 | [super layoutSubviews]; 35 | } 36 | 37 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated 38 | { 39 | if (highlighted) self.backgroundColor = [UIColor colorWithRed:0.f green:0.5 blue:1.f alpha:1.f]; 40 | else self.backgroundColor = [UIColor whiteColor]; 41 | } 42 | 43 | @end 44 | 45 | #pragma mark - 46 | 47 | @interface DemoTableViewController () 48 | 49 | @property (assign, nonatomic, getter=isInitialized) BOOL initialized; 50 | 51 | @property (strong, nonatomic) NSMutableArray *dataArray; 52 | 53 | @property (assign, nonatomic, getter=isLoadingData) BOOL loadingData; 54 | 55 | @end 56 | 57 | @implementation DemoTableViewController 58 | 59 | - (id)initWithTitle:(NSString *)title 60 | { 61 | self = [super initWithStyle:UITableViewStylePlain asyncCalculatingHeightForRows:YES placeholderViewEnabled:YES refreshViewEnabled:YES]; 62 | if (self) 63 | { 64 | self.title = title; 65 | 66 | self.view.backgroundColor = [UIColor whiteColor]; 67 | 68 | [self.tableView registerClass:[DemoTableViewCell class] forCellReuseIdentifier:@"cell"]; 69 | 70 | [self.tableView.placeholderView showActivityIndicatorAnimated:NO completionHandler:nil]; 71 | 72 | self.tableView.alwaysBounceVertical = YES; 73 | 74 | [self addItemsToDataArray]; 75 | } 76 | return self; 77 | } 78 | 79 | #pragma mark - Dealloc 80 | 81 | - (void)dealloc 82 | { 83 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 84 | } 85 | 86 | #pragma mark - Appearing 87 | 88 | - (void)viewDidAppear:(BOOL)animated 89 | { 90 | [super viewDidAppear:animated]; 91 | 92 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void) 93 | { 94 | [self.tableView.placeholderView dismissAnimated:YES completionHandler:nil]; 95 | 96 | _initialized = YES; 97 | }); 98 | } 99 | 100 | #pragma mark - UITableView Data Source 101 | 102 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 103 | { 104 | return 1; 105 | } 106 | 107 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 108 | { 109 | return _dataArray.count; 110 | } 111 | 112 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 113 | { 114 | DemoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 115 | 116 | cell.textLabel.text = _dataArray[indexPath.row]; 117 | 118 | return cell; 119 | } 120 | 121 | #pragma mark - UITableView Delegate 122 | 123 | - (CGFloat)tableView:(LGTableView *)tableView heightForRowAtIndexPathAsync:(NSIndexPath *)indexPath 124 | { 125 | NSString *string = _dataArray[indexPath.row]; 126 | 127 | CGSize size = [string sizeWithFont:[UIFont systemFontOfSize:16.0] constrainedToSize:CGSizeMake(tableView.frame.size.width, CGFLOAT_MAX)]; 128 | 129 | return size.height+20.f; 130 | } 131 | 132 | - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath 133 | { 134 | NSLog(@"Table view did highlight cell section: %i row: %i", (int)indexPath.section, (int)indexPath.row); 135 | } 136 | 137 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 138 | { 139 | NSLog(@"Table view did select cell section: %i row: %i", (int)indexPath.section, (int)indexPath.row); 140 | } 141 | 142 | #pragma mark - UIScrollView Delegate 143 | 144 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 145 | { 146 | if (!self.isLoadingData && self.isInitialized && 147 | scrollView.contentOffset.y + scrollView.contentInset.top + scrollView.frame.size.height >= scrollView.contentSize.height - 100.f) 148 | { 149 | _loadingData = YES; 150 | 151 | [self.tableView beginUpdates]; 152 | 153 | [self addItemsToDataArray]; 154 | 155 | NSMutableArray *array = [NSMutableArray new]; 156 | 157 | NSUInteger count = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? 30 : 40); 158 | 159 | for (NSUInteger i=_dataArray.count-count; i<_dataArray.count; i++) 160 | [array addObject:[NSIndexPath indexPathForRow:i inSection:0]]; 161 | 162 | [self.tableView insertRowsAtIndexPaths:array 163 | withRowAnimation:UITableViewRowAnimationAutomatic 164 | completionHandler:^(void) 165 | { 166 | [self.tableView endUpdates]; 167 | 168 | _loadingData = NO; 169 | }]; 170 | } 171 | } 172 | 173 | #pragma mark - 174 | 175 | - (void)refreshActions 176 | { 177 | [_dataArray removeAllObjects]; 178 | 179 | [self addItemsToDataArray]; 180 | 181 | [self.tableView reloadDataWithCompletionHandler:^(void) 182 | { 183 | [UIView transitionWithView:self.tableView duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:nil]; 184 | 185 | [self.tableView.refreshView endRefreshing]; 186 | }]; 187 | } 188 | 189 | - (void)addItemsToDataArray 190 | { 191 | if (!_dataArray) 192 | _dataArray = [NSMutableArray new]; 193 | 194 | NSUInteger count = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? 30 : 40); 195 | 196 | for (NSUInteger i=0; i 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/NavigationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.h 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NavigationController : UINavigationController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/NavigationController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "NavigationController.h" 10 | #import "TableViewController.h" 11 | 12 | @interface NavigationController () 13 | 14 | @end 15 | 16 | @implementation NavigationController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | 22 | self.navigationBar.translucent = YES; 23 | self.navigationBar.barTintColor = [UIColor colorWithRed:0.f green:0.5 blue:1.f alpha:1.f]; 24 | self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor]}; 25 | self.navigationBar.tintColor = [UIColor colorWithWhite:1.f alpha:0.5]; 26 | } 27 | 28 | - (BOOL)shouldAutorotate 29 | { 30 | return YES; 31 | } 32 | 33 | - (BOOL)prefersStatusBarHidden 34 | { 35 | return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); 36 | } 37 | 38 | - (UIStatusBarStyle)preferredStatusBarStyle 39 | { 40 | return UIStatusBarStyleLightContent; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 18.02.15. 6 | // Copyright (c) 2015 Grigory Lutkov. All rights reserved. 7 | // 8 | 9 | #import "TableViewController.h" 10 | #import "DemoScrollViewController.h" 11 | #import "DemoTableViewController.h" 12 | #import "DemoCollectionViewController.h" 13 | #import "LGWebViewController.h" 14 | 15 | @interface TableViewController () 16 | 17 | @property (strong, nonatomic) NSArray *titlesArray; 18 | 19 | @end 20 | 21 | @implementation TableViewController 22 | 23 | - (id)init 24 | { 25 | self = [super initWithStyle:UITableViewStylePlain]; 26 | if (self) 27 | { 28 | self.title = @"LGViewControllers"; 29 | 30 | _titlesArray = @[@"LGScrollViewController", 31 | @"LGTableViewController", 32 | @"LGCollectionViewController", 33 | @"LGWebViewController"]; 34 | 35 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 36 | } 37 | return self; 38 | } 39 | 40 | #pragma mark - UITableView DataSource 41 | 42 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 43 | { 44 | return 1; 45 | } 46 | 47 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 48 | { 49 | return _titlesArray.count; 50 | } 51 | 52 | #pragma mark - UITableView Delegate 53 | 54 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 55 | { 56 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 57 | 58 | cell.textLabel.font = [UIFont systemFontOfSize:16.f]; 59 | cell.textLabel.text = _titlesArray[indexPath.row]; 60 | 61 | return cell; 62 | } 63 | 64 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 65 | { 66 | return 44.f; 67 | } 68 | 69 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 70 | { 71 | if (indexPath.row == 0) 72 | { 73 | DemoScrollViewController *viewController = [[DemoScrollViewController alloc] initWithTitle:_titlesArray[indexPath.row]]; 74 | [self.navigationController pushViewController:viewController animated:YES]; 75 | } 76 | else if (indexPath.row == 1) 77 | { 78 | DemoTableViewController *viewController = [[DemoTableViewController alloc] initWithTitle:_titlesArray[indexPath.row]]; 79 | [self.navigationController pushViewController:viewController animated:YES]; 80 | } 81 | else if (indexPath.row == 2) 82 | { 83 | DemoCollectionViewController *viewController = [[DemoCollectionViewController alloc] initWithTitle:_titlesArray[indexPath.row]]; 84 | [self.navigationController pushViewController:viewController animated:YES]; 85 | } 86 | else if (indexPath.row == 3) 87 | { 88 | LGWebViewController *viewController = [[LGWebViewController alloc] initWithTitle:_titlesArray[indexPath.row] 89 | url:[NSURL URLWithString:@"http://google.com"] 90 | placeholderViewEnabled:YES]; 91 | viewController.webView.placeholderView.backgroundColor = [UIColor whiteColor]; 92 | [self.navigationController pushViewController:viewController animated:YES]; 93 | } 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /Demo/LGViewControllersDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LGViewControllersDemo 4 | // 5 | // Created by Grigory Lutkov on 10.03.15. 6 | // Copyright (c) 2015 Grigory Lutkov. 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 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '6.0' 4 | 5 | xcodeproj 'LGViewControllersDemo' 6 | 7 | target 'LGViewControllersDemo' do 8 | pod 'LGViewControllers', :path => '../' 9 | end 10 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DACircularProgress (2.3.1) 3 | - LGPlaceholderView (1.0.2) 4 | - LGRefreshView (1.0.3): 5 | - DACircularProgress (~> 2.3.0) 6 | - LGViewControllers (1.0.0): 7 | - LGPlaceholderView (~> 1.0.0) 8 | - LGRefreshView (~> 1.0.0) 9 | 10 | DEPENDENCIES: 11 | - LGViewControllers (from `../`) 12 | 13 | EXTERNAL SOURCES: 14 | LGViewControllers: 15 | :path: ../ 16 | 17 | SPEC CHECKSUMS: 18 | DACircularProgress: 4dd437c0fc3da5161cb289e07ac449493d41db71 19 | LGPlaceholderView: 37ae1eaca38ace1418ae74e4d9db8fa5738367a2 20 | LGRefreshView: 2f3059fcb41abe8a3e032b8f5ea8b9b345869a64 21 | LGViewControllers: 7c46dc5105d1872105a787db00900a439793448d 22 | 23 | COCOAPODS: 0.39.0 24 | -------------------------------------------------------------------------------- /Demo/podsInstall.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(dirname "$0")" 4 | 5 | pod install 6 | -------------------------------------------------------------------------------- /Demo/podsUpdate.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(dirname "$0")" 4 | 5 | pod update 6 | -------------------------------------------------------------------------------- /LGViewControllers.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = 'LGViewControllers' 4 | s.version = '1.0.0' 5 | s.platform = :ios, '6.0' 6 | s.license = 'MIT' 7 | s.homepage = 'https://github.com/Friend-LGA/LGViewControllers' 8 | s.author = { 'Grigory Lutkov' => 'Friend.LGA@gmail.com' } 9 | s.source = { :git => 'https://github.com/Friend-LGA/LGViewControllers.git', :tag => s.version } 10 | s.summary = 'Classes extends abilities of UITableViewController, UICollectionViewController, and more' 11 | 12 | s.requires_arc = true 13 | 14 | s.source_files = 'LGViewControllers/*.{h,m}' 15 | s.source_files = 'LGViewControllers/**/*.{h,m}' 16 | 17 | s.dependency 'LGRefreshView', '~> 1.0.0' 18 | s.dependency 'LGPlaceholderView', '~> 1.0.0' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /LGViewControllers/LGCollectionViewController/LGCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGCollectionView.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGPlaceholderView.h" 32 | #import "LGRefreshView.h" 33 | 34 | @interface LGCollectionView : UICollectionView 35 | 36 | @property (assign, nonatomic, readonly, getter=isRefreshViewEnabled) BOOL refreshViewEnabled; 37 | @property (assign, nonatomic, getter=isPlaceholderViewEnabled) BOOL placeholderViewEnabled; 38 | 39 | @property (strong, nonatomic) LGRefreshView *refreshView; 40 | @property (strong, nonatomic) LGPlaceholderView *placeholderView; 41 | 42 | /** Do not forget about weak referens to self for refreshHandler block */ 43 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler; 44 | 45 | /** Do not forget about weak referens to self for refreshHandler block */ 46 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler; 47 | - (void)setRefreshViewDisabled; 48 | 49 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets; 50 | 51 | - (void)setViewWidth:(CGFloat)viewWidth 52 | cellAspect:(CGFloat)cellAspect 53 | numberOfCellsInARow:(NSUInteger)numberOfCellsInARow 54 | cellInsets:(CGFloat)cellInsets 55 | sectionInsets:(UIEdgeInsets)sectionInsets 56 | headerReferenceSize:(CGSize)headerReferenceSize 57 | footerReferenceSize:(CGSize)footerReferenceSize 58 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection; 59 | 60 | - (void)setViewWidth:(CGFloat)viewWidth 61 | cellHeight:(CGFloat)cellHeight 62 | numberOfCellsInARow:(NSUInteger)numberOfCellsInARow 63 | cellInsets:(CGFloat)cellInsets 64 | sectionInsets:(UIEdgeInsets)sectionInsets 65 | headerReferenceSize:(CGSize)headerReferenceSize 66 | footerReferenceSize:(CGSize)footerReferenceSize 67 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection; 68 | 69 | - (void)setCellWidth:(CGFloat)cellWidth 70 | cellHeight:(CGFloat)cellHeight 71 | cellInsets:(CGFloat)cellInsets 72 | sectionInsets:(UIEdgeInsets)sectionInsets 73 | headerReferenceSize:(CGSize)headerReferenceSize 74 | footerReferenceSize:(CGSize)footerReferenceSize 75 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection; 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /LGViewControllers/LGCollectionViewController/LGCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGCollectionView.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGCollectionView.h" 31 | 32 | @interface LGCollectionView () 33 | 34 | @property (strong, nonatomic) UIView *topSeparatorView; 35 | 36 | @end 37 | 38 | @implementation LGCollectionView 39 | 40 | - (instancetype)init 41 | { 42 | self = [super initWithFrame:CGRectZero collectionViewLayout:[UICollectionViewFlowLayout new]]; 43 | if (self) 44 | { 45 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 46 | } 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithFrame:(CGRect)frame 51 | { 52 | self = [super initWithFrame:frame collectionViewLayout:[UICollectionViewFlowLayout new]]; 53 | if (self) 54 | { 55 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 56 | } 57 | return self; 58 | } 59 | 60 | - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout 61 | { 62 | self = [super initWithFrame:frame collectionViewLayout:(layout ? layout : [UICollectionViewFlowLayout new])]; 63 | if (self) 64 | { 65 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 66 | } 67 | return self; 68 | } 69 | 70 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 71 | { 72 | self = [super initWithFrame:CGRectZero collectionViewLayout:[UICollectionViewFlowLayout new]]; 73 | if (self) 74 | { 75 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled refreshHandler:refreshHandler]; 76 | } 77 | return self; 78 | } 79 | 80 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 81 | { 82 | self.backgroundColor = [UIColor clearColor]; 83 | 84 | self.placeholderViewEnabled = placeholderViewEnabled; 85 | 86 | if (refreshHandler) 87 | [self setRefreshViewEnabledWithHandler:refreshHandler]; 88 | } 89 | 90 | #pragma mark - Dealloc 91 | 92 | - (void)dealloc 93 | { 94 | #if DEBUG 95 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 96 | #endif 97 | } 98 | 99 | #pragma mark - 100 | 101 | - (void)setFrame:(CGRect)frame 102 | { 103 | if (_topSeparatorView && CGSizeEqualToSize(self.frame.size, frame.size)) 104 | { 105 | CGFloat widthDif = frame.size.width-self.frame.size.width; 106 | 107 | _topSeparatorView.frame = CGRectMake(_topSeparatorView.frame.origin.x, _topSeparatorView.frame.origin.y, _topSeparatorView.frame.size.width+widthDif, _topSeparatorView.frame.size.height); 108 | } 109 | 110 | [super setFrame:frame]; 111 | } 112 | 113 | #pragma mark - 114 | 115 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler 116 | { 117 | if (!_refreshViewEnabled && !_refreshView) 118 | { 119 | _refreshViewEnabled = YES; 120 | 121 | _refreshView = [LGRefreshView refreshViewWithScrollView:self 122 | refreshHandler:refreshHandler]; 123 | } 124 | } 125 | 126 | - (void)setRefreshViewDisabled 127 | { 128 | if (_refreshViewEnabled && _refreshView) 129 | { 130 | _refreshViewEnabled = NO; 131 | 132 | if (_refreshView.superview) 133 | [_refreshView removeFromSuperview]; 134 | 135 | _refreshView = nil; 136 | } 137 | } 138 | 139 | - (void)setPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 140 | { 141 | if (_placeholderViewEnabled != placeholderViewEnabled) 142 | { 143 | _placeholderViewEnabled = placeholderViewEnabled; 144 | 145 | if (_placeholderViewEnabled && !_placeholderView) 146 | _placeholderView = [LGPlaceholderView placeholderViewWithView:self]; 147 | else if (!_placeholderViewEnabled && _placeholderView) 148 | { 149 | if (_placeholderView.superview) 150 | [_placeholderView removeFromSuperview]; 151 | 152 | _placeholderView = nil; 153 | } 154 | } 155 | } 156 | 157 | #pragma mark - 158 | 159 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets 160 | { 161 | if (!_topSeparatorView) 162 | { 163 | _topSeparatorView = [UIView new]; 164 | [self addSubview:_topSeparatorView]; 165 | } 166 | 167 | _topSeparatorView.backgroundColor = color; 168 | _topSeparatorView.frame = CGRectMake(edgeInsets.left, -thinckness, self.frame.size.width-edgeInsets.left-edgeInsets.right, thinckness); 169 | } 170 | 171 | #pragma mark - UICollectionViewLayout 172 | 173 | - (void)setViewWidth:(CGFloat)viewWidth 174 | cellAspect:(CGFloat)cellAspect 175 | numberOfCellsInARow:(NSUInteger)numberOfCellsInARow 176 | cellInsets:(CGFloat)cellInsets 177 | sectionInsets:(UIEdgeInsets)sectionInsets 178 | headerReferenceSize:(CGSize)headerReferenceSize 179 | footerReferenceSize:(CGSize)footerReferenceSize 180 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection 181 | { 182 | CGFloat cellWidth = (viewWidth-sectionInsets.left-sectionInsets.right-cellInsets*(numberOfCellsInARow-1))/numberOfCellsInARow; 183 | CGFloat cellHeight = cellWidth/cellAspect; 184 | 185 | [self setCellWidth:cellWidth 186 | cellHeight:cellHeight 187 | cellInsets:cellInsets 188 | sectionInsets:sectionInsets 189 | headerReferenceSize:headerReferenceSize 190 | footerReferenceSize:footerReferenceSize 191 | scrollDirection:scrollDirection]; 192 | } 193 | 194 | - (void)setViewWidth:(CGFloat)viewWidth 195 | cellHeight:(CGFloat)cellHeight 196 | numberOfCellsInARow:(NSUInteger)numberOfCellsInARow 197 | cellInsets:(CGFloat)cellInsets 198 | sectionInsets:(UIEdgeInsets)sectionInsets 199 | headerReferenceSize:(CGSize)headerReferenceSize 200 | footerReferenceSize:(CGSize)footerReferenceSize 201 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection 202 | { 203 | CGFloat cellWidth = (viewWidth-sectionInsets.left-sectionInsets.right-cellInsets*(numberOfCellsInARow-1))/numberOfCellsInARow; 204 | 205 | [self setCellWidth:cellWidth 206 | cellHeight:cellHeight 207 | cellInsets:cellInsets 208 | sectionInsets:sectionInsets 209 | headerReferenceSize:headerReferenceSize 210 | footerReferenceSize:footerReferenceSize 211 | scrollDirection:scrollDirection]; 212 | } 213 | 214 | - (void)setCellWidth:(CGFloat)cellWidth 215 | cellHeight:(CGFloat)cellHeight 216 | cellInsets:(CGFloat)cellInsets 217 | sectionInsets:(UIEdgeInsets)sectionInsets 218 | headerReferenceSize:(CGSize)headerReferenceSize 219 | footerReferenceSize:(CGSize)footerReferenceSize 220 | scrollDirection:(UICollectionViewScrollDirection)scrollDirection 221 | { 222 | UICollectionViewFlowLayout *collectionViewLayout = [UICollectionViewFlowLayout new]; 223 | collectionViewLayout.sectionInset = sectionInsets; 224 | collectionViewLayout.itemSize = CGSizeMake(cellWidth, cellHeight); 225 | collectionViewLayout.minimumLineSpacing = cellInsets; //*(sectionInsets.left || sectionInsets.right ? 1 : 2); 226 | collectionViewLayout.minimumInteritemSpacing = 0; 227 | collectionViewLayout.scrollDirection = scrollDirection; 228 | collectionViewLayout.headerReferenceSize = headerReferenceSize; 229 | collectionViewLayout.footerReferenceSize = footerReferenceSize; 230 | 231 | [self setCollectionViewLayout:collectionViewLayout animated:NO]; 232 | } 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /LGViewControllers/LGCollectionViewController/LGCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGCollectionViewController.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGCollectionView.h" 32 | 33 | @interface LGCollectionViewController : UIViewController 34 | 35 | @property (strong, nonatomic) LGCollectionView *collectionView; 36 | 37 | @property (assign, nonatomic, getter=isKeyboardShowHideObserverEnabled) BOOL keyboardShowHideObserverEnabled; 38 | 39 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled; 40 | 41 | - (void)refreshActions; 42 | 43 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification; 44 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LGViewControllers/LGCollectionViewController/LGCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGCollectionViewController.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGCollectionViewController.h" 31 | 32 | @interface LGCollectionViewController () 33 | 34 | @property (assign, nonatomic) CGFloat bottomContentInset; 35 | @property (assign, nonatomic) CGFloat bottomScrollIndicatorInsets; 36 | 37 | @end 38 | 39 | @implementation LGCollectionViewController 40 | 41 | - (instancetype)init 42 | { 43 | self = [super init]; 44 | if (self) 45 | { 46 | [self initializeWithPlaceholderViewEnabled:NO refreshViewEnabled:NO]; 47 | } 48 | return self; 49 | } 50 | 51 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 52 | { 53 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 54 | if (self) 55 | { 56 | [self initializeWithPlaceholderViewEnabled:NO refreshViewEnabled:NO]; 57 | } 58 | return self; 59 | } 60 | 61 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 62 | { 63 | self = [super init]; 64 | if (self) 65 | { 66 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled refreshViewEnabled:refreshViewEnabled]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 72 | { 73 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0) 74 | self.wantsFullScreenLayout = YES; 75 | else 76 | { 77 | self.extendedLayoutIncludesOpaqueBars = YES; 78 | self.automaticallyAdjustsScrollViewInsets = NO; 79 | } 80 | 81 | if (refreshViewEnabled) 82 | { 83 | __weak typeof(self) wself = self; 84 | 85 | _collectionView = [[LGCollectionView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled 86 | refreshHandler:^(void) 87 | { 88 | if (wself) 89 | { 90 | __strong typeof(wself) self = wself; 91 | 92 | [self refreshActions]; 93 | } 94 | }]; 95 | } 96 | else _collectionView = [[LGCollectionView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled 97 | refreshHandler:nil]; 98 | _collectionView.dataSource = self; 99 | _collectionView.delegate = self; 100 | [self.view addSubview:_collectionView]; 101 | } 102 | 103 | #pragma mark - Dealloc 104 | 105 | - (void)dealloc 106 | { 107 | #if DEBUG 108 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 109 | #endif 110 | 111 | if (self.isKeyboardShowHideObserverEnabled) 112 | _keyboardShowHideObserverEnabled = NO; 113 | } 114 | 115 | #pragma mark - Appearing 116 | 117 | - (void)viewWillLayoutSubviews 118 | { 119 | CGFloat topInset = 0.f; 120 | topInset += (self.navigationController.navigationBarHidden ? 0.f : MIN(self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height)); 121 | topInset += ([UIApplication sharedApplication].statusBarHidden ? 0.f : MIN([UIApplication sharedApplication].statusBarFrame.size.width, [UIApplication sharedApplication].statusBarFrame.size.height)); 122 | 123 | CGFloat bottomInset = 0.f; 124 | bottomInset += (self.navigationController.isToolbarHidden ? 0.f : MIN(self.navigationController.toolbar.frame.size.width, self.navigationController.toolbar.frame.size.height)); 125 | 126 | CGFloat topShift = 0.f; 127 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0 && 128 | !self.navigationController.isNavigationBarHidden && 129 | self.navigationController.navigationBar.isOpaque) 130 | topShift = topInset; 131 | 132 | CGRect newFrame = CGRectMake(0.f, -topShift, self.view.frame.size.width, self.view.frame.size.height+topShift); 133 | 134 | if (!CGSizeEqualToSize(_collectionView.frame.size, newFrame.size)) 135 | { 136 | _collectionView.frame = newFrame; 137 | _collectionView.contentInset = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 138 | _collectionView.scrollIndicatorInsets = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 139 | } 140 | } 141 | 142 | #pragma mark - Refresh 143 | 144 | - (void)refreshActions 145 | { 146 | // 147 | } 148 | 149 | #pragma mark - Keyboard notification 150 | 151 | - (void)setKeyboardShowHideObserverEnabled:(BOOL)keyboardShowHideObserverEnabled 152 | { 153 | if (_keyboardShowHideObserverEnabled != keyboardShowHideObserverEnabled) 154 | { 155 | _keyboardShowHideObserverEnabled = keyboardShowHideObserverEnabled; 156 | 157 | if (_keyboardShowHideObserverEnabled) 158 | { 159 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillShowNotification object:nil]; 160 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillHideNotification object:nil]; 161 | } 162 | else 163 | { 164 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 165 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 166 | } 167 | } 168 | } 169 | 170 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification 171 | { 172 | [LGCollectionViewController keyboardAnimateWithNotificationUserInfo:notification.userInfo 173 | animations:^(CGFloat keyboardHeight) 174 | { 175 | BOOL appear = (notification.name == UIKeyboardWillShowNotification); 176 | 177 | [self keyboardWillShowHideActionsAppear:appear keyboardHeight:keyboardHeight]; 178 | }]; 179 | } 180 | 181 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight 182 | { 183 | if (appear) 184 | { 185 | _bottomContentInset = _collectionView.contentInset.bottom; 186 | _bottomScrollIndicatorInsets = _collectionView.scrollIndicatorInsets.bottom; 187 | } 188 | 189 | CGFloat bottomContentInset = (appear ? keyboardHeight : _bottomContentInset); 190 | CGFloat bottomScrollIndicatorInsets = (appear ? keyboardHeight : _bottomScrollIndicatorInsets); 191 | 192 | UIEdgeInsets contentInset = _collectionView.contentInset; 193 | UIEdgeInsets scrollIndicatorInsets = _collectionView.scrollIndicatorInsets; 194 | 195 | contentInset.bottom = bottomContentInset; 196 | scrollIndicatorInsets.bottom = bottomScrollIndicatorInsets; 197 | 198 | _collectionView.contentInset = contentInset; 199 | _collectionView.scrollIndicatorInsets = scrollIndicatorInsets; 200 | } 201 | 202 | #pragma mark - UICollectionView Data Source 203 | 204 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 205 | { 206 | return 0; 207 | } 208 | 209 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 210 | { 211 | return 0; 212 | } 213 | 214 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 215 | { 216 | return nil; 217 | } 218 | 219 | #pragma mark - Support 220 | 221 | + (void)keyboardAnimateWithNotificationUserInfo:(NSDictionary *)notificationUserInfo animations:(void(^)(CGFloat keyboardHeight))animations 222 | { 223 | CGFloat keyboardHeight = (notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] ? [notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] CGRectValue].size.height : 0.f); 224 | if (!keyboardHeight) 225 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height : 0.f); 226 | if (!keyboardHeight) 227 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameEndUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height : 0.f); 228 | if (!keyboardHeight) 229 | return; 230 | 231 | NSTimeInterval animationDuration = [notificationUserInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 232 | int animationCurve = [notificationUserInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 233 | 234 | [UIView beginAnimations:nil context:NULL]; 235 | [UIView setAnimationDuration:animationDuration]; 236 | [UIView setAnimationCurve:animationCurve]; 237 | 238 | if (animations) animations(keyboardHeight); 239 | 240 | [UIView commitAnimations]; 241 | } 242 | 243 | @end 244 | -------------------------------------------------------------------------------- /LGViewControllers/LGScrollViewController/LGScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGScrollView.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGPlaceholderView.h" 32 | #import "LGRefreshView.h" 33 | 34 | @interface LGScrollView : UIScrollView 35 | 36 | @property (assign, nonatomic, readonly, getter=isRefreshViewEnabled) BOOL refreshViewEnabled; 37 | @property (assign, nonatomic, getter=isPlaceholderViewEnabled) BOOL placeholderViewEnabled; 38 | 39 | @property (strong, nonatomic) LGRefreshView *refreshView; 40 | @property (strong, nonatomic) LGPlaceholderView *placeholderView; 41 | 42 | /** Do not forget about weak referens to self for refreshHandler block */ 43 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler; 44 | 45 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets; 46 | 47 | /** Do not forget about weak referens to self for refreshHandler block */ 48 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler; 49 | - (void)setRefreshViewDisabled; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /LGViewControllers/LGScrollViewController/LGScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGScrollView.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGScrollView.h" 31 | 32 | @interface LGScrollView () 33 | 34 | @property (strong, nonatomic) UIView *topSeparatorView; 35 | 36 | @end 37 | 38 | @implementation LGScrollView 39 | 40 | - (instancetype)init 41 | { 42 | self = [super init]; 43 | if (self) 44 | { 45 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 46 | } 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithFrame:(CGRect)frame 51 | { 52 | self = [super initWithFrame:frame]; 53 | if (self) 54 | { 55 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 56 | } 57 | return self; 58 | } 59 | 60 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 61 | { 62 | self = [super init]; 63 | if (self) 64 | { 65 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled refreshHandler:refreshHandler]; 66 | } 67 | return self; 68 | } 69 | 70 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 71 | { 72 | self.backgroundColor = [UIColor clearColor]; 73 | 74 | self.placeholderViewEnabled = placeholderViewEnabled; 75 | 76 | if (refreshHandler) 77 | [self setRefreshViewEnabledWithHandler:refreshHandler]; 78 | } 79 | 80 | #pragma mark - Dealloc 81 | 82 | - (void)dealloc 83 | { 84 | #if DEBUG 85 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 86 | #endif 87 | } 88 | 89 | #pragma mark - 90 | 91 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler 92 | { 93 | if (!_refreshViewEnabled && !_refreshView) 94 | { 95 | _refreshViewEnabled = YES; 96 | 97 | _refreshView = [LGRefreshView refreshViewWithScrollView:self 98 | refreshHandler:refreshHandler]; 99 | } 100 | } 101 | 102 | - (void)setRefreshViewDisabled 103 | { 104 | if (_refreshViewEnabled && _refreshView) 105 | { 106 | _refreshViewEnabled = NO; 107 | 108 | if (_refreshView.superview) 109 | [_refreshView removeFromSuperview]; 110 | 111 | _refreshView = nil; 112 | } 113 | } 114 | 115 | - (void)setPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 116 | { 117 | if (_placeholderViewEnabled != placeholderViewEnabled) 118 | { 119 | _placeholderViewEnabled = placeholderViewEnabled; 120 | 121 | if (_placeholderViewEnabled && !_placeholderView) 122 | _placeholderView = [LGPlaceholderView placeholderViewWithView:self]; 123 | else if (!_placeholderViewEnabled && _placeholderView) 124 | { 125 | if (_placeholderView.superview) 126 | [_placeholderView removeFromSuperview]; 127 | 128 | _placeholderView = nil; 129 | } 130 | } 131 | } 132 | 133 | #pragma mark - 134 | 135 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets 136 | { 137 | if (_topSeparatorView) 138 | { 139 | [_topSeparatorView removeFromSuperview]; 140 | _topSeparatorView = nil; 141 | } 142 | 143 | _topSeparatorView = [UIView new]; 144 | _topSeparatorView.backgroundColor = color; 145 | _topSeparatorView.frame = CGRectMake(edgeInsets.left, -thinckness, self.frame.size.width-edgeInsets.left-edgeInsets.right, thinckness); 146 | [self addSubview:_topSeparatorView]; 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /LGViewControllers/LGScrollViewController/LGScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGScrollViewController.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGScrollView.h" 32 | 33 | @interface LGScrollViewController : UIViewController 34 | 35 | @property (strong, nonatomic) LGScrollView *scrollView; 36 | 37 | @property (assign, nonatomic, getter=isKeyboardShowHideObserverEnabled) BOOL keyboardShowHideObserverEnabled; 38 | 39 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled; 40 | 41 | - (void)refreshActions; 42 | 43 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification; 44 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LGViewControllers/LGScrollViewController/LGScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGScrollViewController.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGScrollViewController.h" 31 | 32 | @interface LGScrollViewController () 33 | 34 | @property (assign, nonatomic) CGFloat bottomContentInset; 35 | @property (assign, nonatomic) CGFloat bottomScrollIndicatorInsets; 36 | 37 | @end 38 | 39 | @implementation LGScrollViewController 40 | 41 | - (instancetype)init 42 | { 43 | self = [super init]; 44 | if (self) 45 | { 46 | [self initializeWithPlaceholderViewEnabled:NO refreshViewEnabled:NO]; 47 | } 48 | return self; 49 | } 50 | 51 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 52 | { 53 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 54 | if (self) 55 | { 56 | [self initializeWithPlaceholderViewEnabled:NO refreshViewEnabled:NO]; 57 | } 58 | return self; 59 | } 60 | 61 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 62 | { 63 | self = [super init]; 64 | if (self) 65 | { 66 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled refreshViewEnabled:refreshViewEnabled]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 72 | { 73 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0) 74 | self.wantsFullScreenLayout = YES; 75 | else 76 | { 77 | self.extendedLayoutIncludesOpaqueBars = YES; 78 | self.automaticallyAdjustsScrollViewInsets = NO; 79 | } 80 | 81 | if (refreshViewEnabled) 82 | { 83 | __weak typeof(self) wself = self; 84 | 85 | _scrollView = [[LGScrollView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled 86 | refreshHandler:^(void) 87 | { 88 | if (wself) 89 | { 90 | __strong typeof(wself) self = wself; 91 | 92 | [self refreshActions]; 93 | } 94 | }]; 95 | } 96 | else _scrollView = [[LGScrollView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled 97 | refreshHandler:nil]; 98 | _scrollView.delegate = self; 99 | [self.view addSubview:_scrollView]; 100 | } 101 | 102 | #pragma mark - Dealloc 103 | 104 | - (void)dealloc 105 | { 106 | #if DEBUG 107 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 108 | #endif 109 | 110 | if (self.isKeyboardShowHideObserverEnabled) 111 | _keyboardShowHideObserverEnabled = NO; 112 | } 113 | 114 | #pragma mark - Appearing 115 | 116 | - (void)viewWillLayoutSubviews 117 | { 118 | [super viewWillLayoutSubviews]; 119 | 120 | CGFloat topInset = 0.f; 121 | topInset += (self.navigationController.navigationBarHidden ? 0.f : MIN(self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height)); 122 | topInset += ([UIApplication sharedApplication].statusBarHidden ? 0.f : MIN([UIApplication sharedApplication].statusBarFrame.size.width, [UIApplication sharedApplication].statusBarFrame.size.height)); 123 | 124 | CGFloat bottomInset = 0.f; 125 | bottomInset += (self.navigationController.isToolbarHidden ? 0.f : MIN(self.navigationController.toolbar.frame.size.width, self.navigationController.toolbar.frame.size.height)); 126 | 127 | CGFloat topShift = 0.f; 128 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0 && 129 | !self.navigationController.isNavigationBarHidden && 130 | self.navigationController.navigationBar.isOpaque) 131 | topShift = topInset; 132 | 133 | CGRect newFrame = CGRectMake(0.f, -topShift, self.view.frame.size.width, self.view.frame.size.height+topShift); 134 | 135 | if (!CGSizeEqualToSize(_scrollView.frame.size, newFrame.size)) 136 | { 137 | _scrollView.frame = newFrame; 138 | _scrollView.contentInset = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 139 | _scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 140 | } 141 | } 142 | 143 | #pragma mark - Refresh 144 | 145 | - (void)refreshActions 146 | { 147 | // 148 | } 149 | 150 | #pragma mark - Keyboard notification 151 | 152 | - (void)setKeyboardShowHideObserverEnabled:(BOOL)keyboardShowHideObserverEnabled 153 | { 154 | if (_keyboardShowHideObserverEnabled != keyboardShowHideObserverEnabled) 155 | { 156 | _keyboardShowHideObserverEnabled = keyboardShowHideObserverEnabled; 157 | 158 | if (_keyboardShowHideObserverEnabled) 159 | { 160 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillShowNotification object:nil]; 161 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillHideNotification object:nil]; 162 | } 163 | else 164 | { 165 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 166 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 167 | } 168 | } 169 | } 170 | 171 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification 172 | { 173 | [LGScrollViewController keyboardAnimateWithNotificationUserInfo:notification.userInfo 174 | animations:^(CGFloat keyboardHeight) 175 | { 176 | BOOL appear = (notification.name == UIKeyboardWillShowNotification); 177 | 178 | [self keyboardWillShowHideActionsAppear:appear keyboardHeight:keyboardHeight]; 179 | }]; 180 | } 181 | 182 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight 183 | { 184 | if (appear) 185 | { 186 | _bottomContentInset = _scrollView.contentInset.bottom; 187 | _bottomScrollIndicatorInsets = _scrollView.scrollIndicatorInsets.bottom; 188 | } 189 | 190 | CGFloat bottomContentInset = (appear ? keyboardHeight : _bottomContentInset); 191 | CGFloat bottomScrollIndicatorInsets = (appear ? keyboardHeight : _bottomScrollIndicatorInsets); 192 | 193 | UIEdgeInsets contentInset = _scrollView.contentInset; 194 | UIEdgeInsets scrollIndicatorInsets = _scrollView.scrollIndicatorInsets; 195 | 196 | contentInset.bottom = bottomContentInset; 197 | scrollIndicatorInsets.bottom = bottomScrollIndicatorInsets; 198 | 199 | _scrollView.contentInset = contentInset; 200 | _scrollView.scrollIndicatorInsets = scrollIndicatorInsets; 201 | } 202 | 203 | #pragma mark - Support 204 | 205 | + (void)keyboardAnimateWithNotificationUserInfo:(NSDictionary *)notificationUserInfo animations:(void(^)(CGFloat keyboardHeight))animations 206 | { 207 | CGFloat keyboardHeight = (notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] ? [notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] CGRectValue].size.height : 0.f); 208 | if (!keyboardHeight) 209 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height : 0.f); 210 | if (!keyboardHeight) 211 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameEndUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height : 0.f); 212 | if (!keyboardHeight) 213 | return; 214 | 215 | NSTimeInterval animationDuration = [notificationUserInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 216 | int animationCurve = [notificationUserInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 217 | 218 | [UIView beginAnimations:nil context:NULL]; 219 | [UIView setAnimationDuration:animationDuration]; 220 | [UIView setAnimationCurve:animationCurve]; 221 | 222 | if (animations) animations(keyboardHeight); 223 | 224 | [UIView commitAnimations]; 225 | } 226 | 227 | @end 228 | -------------------------------------------------------------------------------- /LGViewControllers/LGTableViewController/LGTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGTableView.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGPlaceholderView.h" 32 | #import "LGRefreshView.h" 33 | 34 | @class LGTableView; 35 | 36 | @protocol LGTableViewDelegate 37 | 38 | @required 39 | 40 | - (CGFloat)tableView:(LGTableView *)tableView heightForRowAtIndexPathAsync:(NSIndexPath *)indexPath; 41 | 42 | @end 43 | 44 | @interface LGTableView : UITableView 45 | 46 | @property (strong, nonatomic, readonly) NSMutableArray *heightForRowsArray; 47 | 48 | @property (assign, nonatomic) id delegateLG; 49 | 50 | @property (assign, nonatomic, readonly, getter=isRefreshViewEnabled) BOOL refreshViewEnabled; 51 | @property (assign, nonatomic, getter=isPlaceholderViewEnabled) BOOL placeholderViewEnabled; 52 | 53 | @property (strong, nonatomic) LGRefreshView *refreshView; 54 | @property (strong, nonatomic) LGPlaceholderView *placeholderView; 55 | 56 | - (instancetype)initWithStyle:(UITableViewStyle)style; 57 | 58 | /** Do not forget about weak referens to self for refreshHandler block */ 59 | - (instancetype)initWithStyle:(UITableViewStyle)style placeholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler; 60 | 61 | /** Do not forget about weak referens to self for refreshHandler block */ 62 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler; 63 | - (void)setRefreshViewDisabled; 64 | 65 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets; 66 | 67 | #pragma mark - 68 | 69 | - (void)reloadDataWithCompletionHandler:(void(^)())completionHandler; 70 | - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 71 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 72 | 73 | #pragma mark - 74 | 75 | - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 76 | - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 77 | 78 | #pragma mark - 79 | 80 | - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 81 | - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler; 82 | 83 | #pragma mark - 84 | 85 | - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath completionHandler:(void(^)())completionHandler; 86 | - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection completionHandler:(void(^)())completionHandler; 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /LGViewControllers/LGTableViewController/LGTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGTableView.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGTableView.h" 31 | 32 | @interface LGTableView () 33 | 34 | @property (strong, nonatomic) NSMutableArray *heightForRowsArray; 35 | 36 | @property (strong, nonatomic) UIView *topSeparatorView; 37 | 38 | @end 39 | 40 | @implementation LGTableView 41 | 42 | - (instancetype)init 43 | { 44 | self = [super initWithFrame:CGRectZero style:UITableViewStylePlain]; 45 | if (self) 46 | { 47 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 48 | } 49 | return self; 50 | } 51 | 52 | - (instancetype)initWithStyle:(UITableViewStyle)style 53 | { 54 | self = [super initWithFrame:CGRectZero style:style]; 55 | if (self) 56 | { 57 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 58 | } 59 | return self; 60 | } 61 | 62 | - (instancetype)initWithFrame:(CGRect)frame 63 | { 64 | self = [super initWithFrame:frame style:UITableViewStylePlain]; 65 | if (self) 66 | { 67 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 68 | } 69 | return self; 70 | } 71 | 72 | - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style 73 | { 74 | self = [super initWithFrame:frame style:style]; 75 | if (self) 76 | { 77 | [self initializeWithPlaceholderViewEnabled:NO refreshHandler:nil]; 78 | } 79 | return self; 80 | } 81 | 82 | - (instancetype)initWithStyle:(UITableViewStyle)style placeholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 83 | { 84 | self = [super initWithFrame:CGRectZero style:style]; 85 | if (self) 86 | { 87 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled refreshHandler:refreshHandler]; 88 | } 89 | return self; 90 | } 91 | 92 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled refreshHandler:(void(^)())refreshHandler 93 | { 94 | self.backgroundColor = [UIColor clearColor]; 95 | 96 | self.placeholderViewEnabled = placeholderViewEnabled; 97 | 98 | if (refreshHandler) 99 | [self setRefreshViewEnabledWithHandler:refreshHandler]; 100 | } 101 | 102 | #pragma mark - Dealloc 103 | 104 | - (void)dealloc 105 | { 106 | #if DEBUG 107 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 108 | #endif 109 | 110 | self.delegateLG = nil; 111 | } 112 | 113 | #pragma mark - 114 | 115 | - (void)setFrame:(CGRect)frame 116 | { 117 | if (_topSeparatorView && CGSizeEqualToSize(self.frame.size, frame.size)) 118 | { 119 | CGFloat widthDif = frame.size.width-self.frame.size.width; 120 | 121 | _topSeparatorView.frame = CGRectMake(_topSeparatorView.frame.origin.x, _topSeparatorView.frame.origin.y, _topSeparatorView.frame.size.width+widthDif, _topSeparatorView.frame.size.height); 122 | } 123 | 124 | [super setFrame:frame]; 125 | } 126 | 127 | #pragma mark - 128 | 129 | - (void)setDelegateLG:(id)delegateLG 130 | { 131 | _delegateLG = delegateLG; 132 | 133 | if (_delegateLG) _heightForRowsArray = [NSMutableArray new]; 134 | else _heightForRowsArray = nil; 135 | } 136 | 137 | #pragma mark - 138 | 139 | - (void)setRefreshViewEnabledWithHandler:(void(^)())refreshHandler 140 | { 141 | if (!_refreshViewEnabled && !_refreshView) 142 | { 143 | _refreshViewEnabled = YES; 144 | 145 | _refreshView = [LGRefreshView refreshViewWithScrollView:self 146 | refreshHandler:refreshHandler]; 147 | } 148 | } 149 | 150 | - (void)setRefreshViewDisabled 151 | { 152 | if (_refreshViewEnabled && _refreshView) 153 | { 154 | _refreshViewEnabled = NO; 155 | 156 | if (_refreshView.superview) 157 | [_refreshView removeFromSuperview]; 158 | 159 | _refreshView = nil; 160 | } 161 | } 162 | 163 | - (void)setPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 164 | { 165 | if (_placeholderViewEnabled != placeholderViewEnabled) 166 | { 167 | _placeholderViewEnabled = placeholderViewEnabled; 168 | 169 | if (_placeholderViewEnabled && !_placeholderView) 170 | _placeholderView = [LGPlaceholderView placeholderViewWithView:self]; 171 | else if (!_placeholderViewEnabled && _placeholderView) 172 | { 173 | if (_placeholderView.superview) 174 | [_placeholderView removeFromSuperview]; 175 | 176 | _placeholderView = nil; 177 | } 178 | } 179 | } 180 | 181 | #pragma mark - 182 | 183 | - (void)addTopSeparatorViewWithColor:(UIColor *)color thinckness:(CGFloat)thinckness edgeInsets:(UIEdgeInsets)edgeInsets 184 | { 185 | if (_topSeparatorView) 186 | { 187 | [_topSeparatorView removeFromSuperview]; 188 | _topSeparatorView = nil; 189 | } 190 | 191 | _topSeparatorView = [UIView new]; 192 | _topSeparatorView.backgroundColor = color; 193 | _topSeparatorView.frame = CGRectMake(edgeInsets.left, -thinckness, self.frame.size.width-edgeInsets.left-edgeInsets.right, thinckness); 194 | [self addSubview:_topSeparatorView]; 195 | } 196 | 197 | #pragma mark - 198 | 199 | - (void)reloadData 200 | { 201 | [self reloadDataWithCompletionHandler:nil]; 202 | } 203 | 204 | - (void)reloadDataWithCompletionHandler:(void(^)())completionHandler 205 | { 206 | if (_delegateLG && [_delegateLG respondsToSelector:@selector(tableView:heightForRowAtIndexPathAsync:)]) 207 | { 208 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) 209 | { 210 | NSMutableArray *heightForRowsArray = [NSMutableArray new]; 211 | 212 | for (NSUInteger section=0; section<[self.dataSource numberOfSectionsInTableView:self]; section++) 213 | { 214 | NSMutableArray *sectionArray = [NSMutableArray new]; 215 | 216 | for (NSUInteger row=0; row<[self.dataSource tableView:self numberOfRowsInSection:section]; row++) 217 | { 218 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 219 | 220 | CGFloat height = [_delegateLG tableView:self heightForRowAtIndexPathAsync:indexPath]; 221 | 222 | NSNumber *heightObject = [NSNumber numberWithFloat:height]; 223 | 224 | [sectionArray addObject:heightObject]; 225 | } 226 | 227 | [heightForRowsArray addObject:sectionArray]; 228 | } 229 | 230 | dispatch_async(dispatch_get_main_queue(), ^(void) 231 | { 232 | _heightForRowsArray = heightForRowsArray; 233 | 234 | [super reloadData]; 235 | 236 | if (completionHandler) completionHandler(); 237 | }); 238 | }); 239 | } 240 | else 241 | { 242 | [super reloadData]; 243 | 244 | if (completionHandler) completionHandler(); 245 | } 246 | } 247 | 248 | - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 249 | { 250 | [self reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation completionHandler:nil]; 251 | } 252 | 253 | - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler 254 | { 255 | if (_delegateLG && [_delegateLG respondsToSelector:@selector(tableView:heightForRowAtIndexPathAsync:)]) 256 | { 257 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) 258 | { 259 | NSMutableArray *heightForRowsArray = _heightForRowsArray.mutableCopy; 260 | 261 | for (NSUInteger section=0; section<[self.dataSource numberOfSectionsInTableView:self]; section++) 262 | { 263 | NSMutableArray *sectionArray = heightForRowsArray[section]; 264 | 265 | for (NSUInteger row=0; row<[self.dataSource tableView:self numberOfRowsInSection:section]; row++) 266 | { 267 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 268 | 269 | if ([indexPaths containsObject:indexPath]) 270 | { 271 | CGFloat height = [_delegateLG tableView:self heightForRowAtIndexPathAsync:indexPath]; 272 | 273 | NSNumber *heightObject = [NSNumber numberWithFloat:height]; 274 | 275 | [sectionArray replaceObjectAtIndex:row withObject:heightObject]; 276 | } 277 | } 278 | 279 | [heightForRowsArray replaceObjectAtIndex:section withObject:sectionArray]; 280 | } 281 | 282 | dispatch_async(dispatch_get_main_queue(), ^(void) 283 | { 284 | _heightForRowsArray = heightForRowsArray; 285 | 286 | [super reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 287 | 288 | if (completionHandler) completionHandler(); 289 | }); 290 | }); 291 | } 292 | else 293 | { 294 | [super reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 295 | 296 | if (completionHandler) completionHandler(); 297 | } 298 | } 299 | 300 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 301 | { 302 | [self reloadSections:sections withRowAnimation:animation completionHandler:nil]; 303 | } 304 | 305 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler 306 | { 307 | if (_delegateLG && [_delegateLG respondsToSelector:@selector(tableView:heightForRowAtIndexPathAsync:)]) 308 | { 309 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) 310 | { 311 | NSMutableArray *heightForRowsArray = _heightForRowsArray.mutableCopy; 312 | 313 | for (NSUInteger section=0; section<[self.dataSource numberOfSectionsInTableView:self]; section++) 314 | if ([sections containsIndex:section]) 315 | { 316 | NSMutableArray *sectionArray = heightForRowsArray[section]; 317 | 318 | for (NSUInteger row=0; row<[self.dataSource tableView:self numberOfRowsInSection:section]; row++) 319 | { 320 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 321 | 322 | CGFloat height = [_delegateLG tableView:self heightForRowAtIndexPathAsync:indexPath]; 323 | 324 | NSNumber *heightObject = [NSNumber numberWithFloat:height]; 325 | 326 | [sectionArray replaceObjectAtIndex:row withObject:heightObject]; 327 | } 328 | 329 | [heightForRowsArray replaceObjectAtIndex:section withObject:sectionArray]; 330 | } 331 | 332 | dispatch_async(dispatch_get_main_queue(), ^(void) 333 | { 334 | _heightForRowsArray = heightForRowsArray; 335 | 336 | [super reloadSections:sections withRowAnimation:animation]; 337 | 338 | if (completionHandler) completionHandler(); 339 | }); 340 | }); 341 | } 342 | else 343 | { 344 | [super reloadSections:sections withRowAnimation:animation]; 345 | 346 | if (completionHandler) completionHandler(); 347 | } 348 | } 349 | 350 | #pragma mark - 351 | 352 | - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 353 | { 354 | [self insertRowsAtIndexPaths:indexPaths withRowAnimation:animation completionHandler:nil]; 355 | } 356 | 357 | - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler 358 | { 359 | if (_delegateLG && [_delegateLG respondsToSelector:@selector(tableView:heightForRowAtIndexPathAsync:)]) 360 | { 361 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) 362 | { 363 | NSArray *heightForRowsArray = [self insertRowsAtIndexPaths:indexPaths inArray:_heightForRowsArray]; 364 | 365 | dispatch_async(dispatch_get_main_queue(), ^(void) 366 | { 367 | _heightForRowsArray = heightForRowsArray.mutableCopy; 368 | 369 | [super insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 370 | 371 | if (completionHandler) completionHandler(); 372 | }); 373 | }); 374 | } 375 | else 376 | { 377 | [super insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 378 | 379 | if (completionHandler) completionHandler(); 380 | } 381 | } 382 | 383 | - (NSMutableArray *)insertRowsAtIndexPaths:(NSArray *)indexPaths inArray:(NSArray *)array 384 | { 385 | NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"section" ascending:YES]; 386 | NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"row" ascending:YES]; 387 | 388 | NSArray *indexPathsSorted = [indexPaths sortedArrayUsingDescriptors:@[sortDescriptor1, sortDescriptor2]]; 389 | 390 | NSMutableArray *heightForRowsArray = array.mutableCopy; 391 | 392 | for (NSIndexPath *indexPath in indexPathsSorted) 393 | { 394 | CGFloat height = [_delegateLG tableView:self heightForRowAtIndexPathAsync:indexPath]; 395 | 396 | NSNumber *heightObject = [NSNumber numberWithFloat:height]; 397 | 398 | if (heightForRowsArray.count > indexPath.section) 399 | { 400 | NSMutableArray *sectionArray = heightForRowsArray[indexPath.section]; 401 | 402 | if (sectionArray.count > indexPath.row) 403 | [sectionArray insertObject:heightObject atIndex:indexPath.row]; 404 | else 405 | [sectionArray addObject:heightObject]; 406 | } 407 | else 408 | { 409 | NSMutableArray *sectionArray = [NSMutableArray new]; 410 | 411 | [sectionArray addObject:heightObject]; 412 | 413 | [heightForRowsArray addObject:sectionArray]; 414 | } 415 | } 416 | 417 | return heightForRowsArray; 418 | } 419 | 420 | - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 421 | { 422 | [self insertSections:sections withRowAnimation:animation completionHandler:nil]; 423 | } 424 | 425 | - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation completionHandler:(void(^)())completionHandler 426 | { 427 | if (_delegateLG && [_delegateLG respondsToSelector:@selector(tableView:heightForRowAtIndexPathAsync:)]) 428 | { 429 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) 430 | { 431 | NSArray *heightForRowsArray = [self insertSections:sections inArray:_heightForRowsArray]; 432 | 433 | dispatch_async(dispatch_get_main_queue(), ^(void) 434 | { 435 | _heightForRowsArray = heightForRowsArray.mutableCopy; 436 | 437 | [super insertSections:sections withRowAnimation:animation]; 438 | 439 | if (completionHandler) completionHandler(); 440 | }); 441 | }); 442 | } 443 | else 444 | { 445 | [super insertSections:sections withRowAnimation:animation]; 446 | 447 | if (completionHandler) completionHandler(); 448 | } 449 | } 450 | 451 | - (NSMutableArray *)insertSections:(NSIndexSet *)sections inArray:(NSArray *)array 452 | { 453 | NSMutableIndexSet *sectionsSorted = [NSMutableIndexSet new]; 454 | 455 | for (NSUInteger i=0; i 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGTableView.h" 32 | 33 | @interface LGTableViewController : UIViewController 34 | 35 | @property (strong, nonatomic) LGTableView *tableView; 36 | 37 | @property (assign, nonatomic, getter=isKeyboardShowHideObserverEnabled) BOOL keyboardShowHideObserverEnabled; 38 | 39 | - (instancetype)initWithStyle:(UITableViewStyle)style; 40 | - (instancetype)initWithStyle:(UITableViewStyle)style asyncCalculatingHeightForRows:(BOOL)asyncCalculatingHeightForRows; 41 | - (instancetype)initWithStyle:(UITableViewStyle)style asyncCalculatingHeightForRows:(BOOL)asyncCalculatingHeightForRows placeholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled; 42 | 43 | - (void)refreshActions; 44 | 45 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification; 46 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /LGViewControllers/LGTableViewController/LGTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGTableViewController.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGTableViewController.h" 31 | 32 | @interface LGTableViewController () 33 | 34 | @property (assign, nonatomic, getter=isCalculating) BOOL calculating; 35 | 36 | @property (assign, nonatomic) CGFloat bottomContentInset; 37 | @property (assign, nonatomic) CGFloat bottomScrollIndicatorInsets; 38 | 39 | @end 40 | 41 | @implementation LGTableViewController 42 | 43 | - (instancetype)init 44 | { 45 | self = [super init]; 46 | if (self) 47 | { 48 | [self initializeWithStyle:UITableViewStylePlain asyncCalculatingHeightForRows:NO placeholderViewEnabled:NO refreshViewEnabled:NO]; 49 | } 50 | return self; 51 | } 52 | 53 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 54 | { 55 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 56 | if (self) 57 | { 58 | [self initializeWithStyle:UITableViewStylePlain asyncCalculatingHeightForRows:NO placeholderViewEnabled:NO refreshViewEnabled:NO]; 59 | } 60 | return self; 61 | } 62 | 63 | - (instancetype)initWithStyle:(UITableViewStyle)style 64 | { 65 | self = [super init]; 66 | if (self) 67 | { 68 | [self initializeWithStyle:style asyncCalculatingHeightForRows:NO placeholderViewEnabled:NO refreshViewEnabled:NO]; 69 | } 70 | return self; 71 | } 72 | 73 | - (instancetype)initWithStyle:(UITableViewStyle)style asyncCalculatingHeightForRows:(BOOL)asyncCalculatingHeightForRows 74 | { 75 | self = [super init]; 76 | if (self) 77 | { 78 | [self initializeWithStyle:style asyncCalculatingHeightForRows:asyncCalculatingHeightForRows placeholderViewEnabled:NO refreshViewEnabled:NO]; 79 | } 80 | return self; 81 | } 82 | 83 | - (instancetype)initWithStyle:(UITableViewStyle)style asyncCalculatingHeightForRows:(BOOL)asyncCalculatingHeightForRows placeholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 84 | { 85 | self = [super init]; 86 | if (self) 87 | { 88 | [self initializeWithStyle:style asyncCalculatingHeightForRows:asyncCalculatingHeightForRows placeholderViewEnabled:placeholderViewEnabled refreshViewEnabled:refreshViewEnabled]; 89 | } 90 | return self; 91 | } 92 | 93 | - (void)initializeWithStyle:(UITableViewStyle)style asyncCalculatingHeightForRows:(BOOL)asyncCalculatingHeightForRows placeholderViewEnabled:(BOOL)placeholderViewEnabled refreshViewEnabled:(BOOL)refreshViewEnabled 94 | { 95 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0) 96 | self.wantsFullScreenLayout = YES; 97 | else 98 | { 99 | self.extendedLayoutIncludesOpaqueBars = YES; 100 | self.automaticallyAdjustsScrollViewInsets = NO; 101 | } 102 | 103 | if (refreshViewEnabled) 104 | { 105 | __weak typeof(self) wself = self; 106 | 107 | _tableView = [[LGTableView alloc] initWithStyle:style 108 | placeholderViewEnabled:placeholderViewEnabled 109 | refreshHandler:^(void) 110 | { 111 | if (wself) 112 | { 113 | __strong typeof(wself) self = wself; 114 | 115 | [self refreshActions]; 116 | } 117 | }]; 118 | } 119 | else _tableView = [[LGTableView alloc] initWithStyle:style 120 | placeholderViewEnabled:placeholderViewEnabled 121 | refreshHandler:nil]; 122 | _tableView.dataSource = self; 123 | _tableView.delegate = self; 124 | if (asyncCalculatingHeightForRows) 125 | _tableView.delegateLG = self; 126 | 127 | [self.view addSubview:_tableView]; 128 | } 129 | 130 | #pragma mark - Dealloc 131 | 132 | - (void)dealloc 133 | { 134 | #if DEBUG 135 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 136 | #endif 137 | 138 | if (self.isKeyboardShowHideObserverEnabled) 139 | _keyboardShowHideObserverEnabled = NO; 140 | } 141 | 142 | #pragma mark - Appearing 143 | 144 | - (void)viewWillLayoutSubviews 145 | { 146 | [super viewWillLayoutSubviews]; 147 | 148 | CGFloat topInset = 0.f; 149 | topInset += (self.navigationController.navigationBarHidden ? 0.f : MIN(self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height)); 150 | topInset += ([UIApplication sharedApplication].statusBarHidden ? 0.f : MIN([UIApplication sharedApplication].statusBarFrame.size.width, [UIApplication sharedApplication].statusBarFrame.size.height)); 151 | 152 | CGFloat bottomInset = 0.f; 153 | bottomInset += (self.navigationController.isToolbarHidden ? 0.f : MIN(self.navigationController.toolbar.frame.size.width, self.navigationController.toolbar.frame.size.height)); 154 | 155 | CGFloat topShift = 0.f; 156 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0 && 157 | !self.navigationController.isNavigationBarHidden && 158 | self.navigationController.navigationBar.isOpaque) 159 | topShift = topInset; 160 | 161 | CGRect newFrame = CGRectMake(0.f, -topShift, self.view.frame.size.width, self.view.frame.size.height+topShift); 162 | 163 | if (!CGSizeEqualToSize(_tableView.frame.size, newFrame.size)) 164 | { 165 | _tableView.frame = newFrame; 166 | _tableView.contentInset = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 167 | _tableView.scrollIndicatorInsets = UIEdgeInsetsMake(topInset, 0.f, bottomInset, 0.f); 168 | } 169 | } 170 | 171 | #pragma mark - Refresh 172 | 173 | - (void)refreshActions 174 | { 175 | // 176 | } 177 | 178 | #pragma mark - Keyboard notification 179 | 180 | - (void)setKeyboardShowHideObserverEnabled:(BOOL)keyboardShowHideObserverEnabled 181 | { 182 | if (_keyboardShowHideObserverEnabled != keyboardShowHideObserverEnabled) 183 | { 184 | _keyboardShowHideObserverEnabled = keyboardShowHideObserverEnabled; 185 | 186 | if (_keyboardShowHideObserverEnabled) 187 | { 188 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillShowNotification object:nil]; 189 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowHideNotification:) name:UIKeyboardWillHideNotification object:nil]; 190 | } 191 | else 192 | { 193 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 194 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 195 | } 196 | } 197 | } 198 | 199 | - (void)keyboardWillShowHideNotification:(NSNotification *)notification 200 | { 201 | [LGTableViewController keyboardAnimateWithNotificationUserInfo:notification.userInfo 202 | animations:^(CGFloat keyboardHeight) 203 | { 204 | BOOL appear = (notification.name == UIKeyboardWillShowNotification); 205 | 206 | [self keyboardWillShowHideActionsAppear:appear keyboardHeight:keyboardHeight]; 207 | }]; 208 | } 209 | 210 | - (void)keyboardWillShowHideActionsAppear:(BOOL)appear keyboardHeight:(CGFloat)keyboardHeight 211 | { 212 | if (appear) 213 | { 214 | _bottomContentInset = _tableView.contentInset.bottom; 215 | _bottomScrollIndicatorInsets = _tableView.scrollIndicatorInsets.bottom; 216 | } 217 | 218 | CGFloat bottomContentInset = (appear ? keyboardHeight : _bottomContentInset); 219 | CGFloat bottomScrollIndicatorInsets = (appear ? keyboardHeight : _bottomScrollIndicatorInsets); 220 | 221 | UIEdgeInsets contentInset = _tableView.contentInset; 222 | UIEdgeInsets scrollIndicatorInsets = _tableView.scrollIndicatorInsets; 223 | 224 | contentInset.bottom = bottomContentInset; 225 | scrollIndicatorInsets.bottom = bottomScrollIndicatorInsets; 226 | 227 | _tableView.contentInset = contentInset; 228 | _tableView.scrollIndicatorInsets = scrollIndicatorInsets; 229 | } 230 | 231 | #pragma mark - UITableView Data Source 232 | 233 | - (NSInteger)numberOfSectionsInTableView:(LGTableView *)tableView 234 | { 235 | return 0; 236 | } 237 | 238 | - (NSInteger)tableView:(LGTableView *)tableView numberOfRowsInSection:(NSInteger)section 239 | { 240 | return 0; 241 | } 242 | 243 | - (UITableViewCell *)tableView:(LGTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 244 | { 245 | return nil; 246 | } 247 | 248 | #pragma mark - UITableView Delegate 249 | 250 | - (CGFloat)tableView:(LGTableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 251 | { 252 | if (tableView.heightForRowsArray && tableView.heightForRowsArray.count > indexPath.section && [tableView.heightForRowsArray[indexPath.section] count] > indexPath.row) 253 | return [tableView.heightForRowsArray[indexPath.section][indexPath.row] floatValue]; 254 | else 255 | return 0.f; 256 | } 257 | 258 | #pragma mark - LGTableView Delegate 259 | 260 | - (CGFloat)tableView:(LGTableView *)tableView heightForRowAtIndexPathAsync:(NSIndexPath *)indexPath 261 | { 262 | return 0.f; 263 | } 264 | 265 | #pragma mark - Support 266 | 267 | + (void)keyboardAnimateWithNotificationUserInfo:(NSDictionary *)notificationUserInfo animations:(void(^)(CGFloat keyboardHeight))animations 268 | { 269 | CGFloat keyboardHeight = (notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] ? [notificationUserInfo[@"UIKeyboardBoundsUserInfoKey"] CGRectValue].size.height : 0.f); 270 | if (!keyboardHeight) 271 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height : 0.f); 272 | if (!keyboardHeight) 273 | keyboardHeight = (notificationUserInfo[UIKeyboardFrameEndUserInfoKey] ? [notificationUserInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height : 0.f); 274 | if (!keyboardHeight) 275 | return; 276 | 277 | NSTimeInterval animationDuration = [notificationUserInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 278 | int animationCurve = [notificationUserInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 279 | 280 | [UIView beginAnimations:nil context:NULL]; 281 | [UIView setAnimationDuration:animationDuration]; 282 | [UIView setAnimationCurve:animationCurve]; 283 | 284 | if (animations) animations(keyboardHeight); 285 | 286 | [UIView commitAnimations]; 287 | } 288 | 289 | @end 290 | -------------------------------------------------------------------------------- /LGViewControllers/LGViewControllerAnimator/LGViewControllerAnimator.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGViewControllerAnimator.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | @interface LGViewControllerAnimator : NSObject 33 | 34 | @property (assign, nonatomic, getter=isGoingRight) BOOL goingRight; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LGViewControllers/LGViewControllerAnimator/LGViewControllerAnimator.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGViewControllerAnimator.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGViewControllerAnimator.h" 31 | 32 | static CGFloat const kChildViewPadding = 0.f; 33 | static CGFloat const kSpringDamping = 1.f; 34 | static CGFloat const kSpringVelocity = 0.5f; 35 | 36 | @implementation LGViewControllerAnimator 37 | 38 | - (NSTimeInterval)transitionDuration:(id)transitionContext 39 | { 40 | return 0.5; 41 | } 42 | 43 | - (void)animateTransition:(id)transitionContext 44 | { 45 | UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 46 | UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 47 | 48 | CGFloat travelDistance = [transitionContext containerView].bounds.size.width + kChildViewPadding; 49 | CGAffineTransform travel = CGAffineTransformMakeTranslation (_goingRight ? travelDistance : -travelDistance, 0); 50 | 51 | [[transitionContext containerView] addSubview:toViewController.view]; 52 | toViewController.view.alpha = 0; 53 | toViewController.view.transform = CGAffineTransformInvert(travel); 54 | 55 | [UIView animateWithDuration:[self transitionDuration:transitionContext] 56 | delay:0.0 57 | usingSpringWithDamping:kSpringDamping 58 | initialSpringVelocity:kSpringVelocity 59 | options:0 60 | animations:^(void) 61 | { 62 | fromViewController.view.transform = travel; 63 | fromViewController.view.alpha = 0; 64 | toViewController.view.transform = CGAffineTransformIdentity; 65 | toViewController.view.alpha = 1; 66 | } 67 | completion:^(BOOL finished) 68 | { 69 | fromViewController.view.transform = CGAffineTransformIdentity; 70 | [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 71 | }]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /LGViewControllers/LGViewControllers.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGViewControllers.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGScrollViewController.h" 31 | #import "LGTableViewController.h" 32 | #import "LGCollectionViewController.h" 33 | #import "LGWebViewController.h" 34 | #import "LGViewControllerAnimator.h" 35 | -------------------------------------------------------------------------------- /LGViewControllers/LGWebViewController/LGWebView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGWebView.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGPlaceholderView.h" 32 | 33 | @interface LGWebView : UIWebView 34 | 35 | @property (assign, nonatomic, getter=isPlaceholderViewEnabled) BOOL placeholderViewEnabled; 36 | 37 | @property (strong, nonatomic) LGPlaceholderView *placeholderView; 38 | 39 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled; 40 | 41 | - (void)removeBackgroundImages; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /LGViewControllers/LGWebViewController/LGWebView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGWebView.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGWebView.h" 31 | 32 | @implementation LGWebView 33 | 34 | - (instancetype)init 35 | { 36 | self = [super init]; 37 | if (self) 38 | { 39 | [self initializeWithPlaceholderViewEnabled:NO]; 40 | } 41 | return self; 42 | } 43 | 44 | - (instancetype)initWithFrame:(CGRect)frame 45 | { 46 | self = [super initWithFrame:frame]; 47 | if (self) 48 | { 49 | [self initializeWithPlaceholderViewEnabled:NO]; 50 | } 51 | return self; 52 | } 53 | 54 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 55 | { 56 | self = [super init]; 57 | if (self) 58 | { 59 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled]; 60 | } 61 | return self; 62 | } 63 | 64 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 65 | { 66 | self.placeholderViewEnabled = placeholderViewEnabled; 67 | } 68 | 69 | #pragma mark - Dealloc 70 | 71 | - (void)dealloc 72 | { 73 | #if DEBUG 74 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 75 | #endif 76 | } 77 | 78 | #pragma mark - 79 | 80 | - (void)removeBackgroundImages 81 | { 82 | if ([UIDevice currentDevice].systemVersion.floatValue < 7) 83 | for (UIImageView *imageView in self.scrollView.subviews) 84 | if ([imageView isKindOfClass:[UIImageView class]] && imageView.image.size.width == 1) 85 | imageView.hidden = YES; 86 | } 87 | 88 | - (void)setPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 89 | { 90 | if (_placeholderViewEnabled != placeholderViewEnabled) 91 | { 92 | _placeholderViewEnabled = placeholderViewEnabled; 93 | 94 | if (_placeholderViewEnabled && !_placeholderView) 95 | _placeholderView = [LGPlaceholderView placeholderViewWithView:self.scrollView]; 96 | else if (!_placeholderViewEnabled && _placeholderView) 97 | { 98 | if (_placeholderView.superview) 99 | [_placeholderView removeFromSuperview]; 100 | 101 | _placeholderView = nil; 102 | } 103 | } 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /LGViewControllers/LGWebViewController/LGWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LGWebViewController.h 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import 31 | #import "LGWebView.h" 32 | 33 | @interface LGWebViewController : UIViewController 34 | 35 | @property (strong, nonatomic) LGWebView *webView; 36 | 37 | @property (assign, nonatomic, getter=isOpenLinksInside) BOOL openLinksInside; 38 | 39 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled; 40 | 41 | - (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url; 42 | - (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url placeholderViewEnabled:(BOOL)placeholderViewEnabled; 43 | 44 | - (void)loadingDidFinishWithError:(NSError *)error; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LGViewControllers/LGWebViewController/LGWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LGWebViewController.m 3 | // LGViewControllers 4 | // 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (c) 2015 Grigory Lutkov 9 | // (https://github.com/Friend-LGA/LGViewControllers) 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | #import "LGWebViewController.h" 31 | 32 | @interface LGWebViewController () 33 | 34 | @property (assign, nonatomic, getter=isInitialized) BOOL initialized; 35 | 36 | @property (assign, nonatomic) UIEdgeInsets contentInset; 37 | 38 | @end 39 | 40 | @implementation LGWebViewController 41 | 42 | - (instancetype)init 43 | { 44 | self = [super init]; 45 | if (self) 46 | { 47 | [self initializeWithPlaceholderViewEnabled:NO]; 48 | } 49 | return self; 50 | } 51 | 52 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 53 | { 54 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 55 | if (self) 56 | { 57 | [self initializeWithPlaceholderViewEnabled:NO]; 58 | } 59 | return self; 60 | } 61 | 62 | - (instancetype)initWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 63 | { 64 | self = [super init]; 65 | if (self) 66 | { 67 | [self initializeWithPlaceholderViewEnabled:placeholderViewEnabled]; 68 | } 69 | return self; 70 | } 71 | 72 | - (void)initializeWithPlaceholderViewEnabled:(BOOL)placeholderViewEnabled 73 | { 74 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0) 75 | self.wantsFullScreenLayout = YES; 76 | else 77 | { 78 | self.extendedLayoutIncludesOpaqueBars = YES; 79 | self.automaticallyAdjustsScrollViewInsets = NO; 80 | } 81 | 82 | _webView = [[LGWebView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled]; 83 | _webView.delegate = self; 84 | [self.view addSubview:_webView]; 85 | } 86 | 87 | #pragma mark - 88 | 89 | - (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url 90 | { 91 | self = [super init]; 92 | if (self) 93 | { 94 | [self initializeWithTitle:title url:url placeholderViewEnabled:NO]; 95 | } 96 | return self; 97 | } 98 | 99 | - (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url placeholderViewEnabled:(BOOL)placeholderViewEnabled 100 | { 101 | self = [super init]; 102 | if (self) 103 | { 104 | [self initializeWithTitle:title url:url placeholderViewEnabled:placeholderViewEnabled]; 105 | } 106 | return self; 107 | } 108 | 109 | - (void)initializeWithTitle:(NSString *)title url:(NSURL *)url placeholderViewEnabled:(BOOL)placeholderViewEnabled 110 | { 111 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0) 112 | self.wantsFullScreenLayout = YES; 113 | else 114 | { 115 | self.extendedLayoutIncludesOpaqueBars = YES; 116 | self.automaticallyAdjustsScrollViewInsets = NO; 117 | } 118 | 119 | self.title = title; 120 | 121 | if ([url isKindOfClass:[NSString class]]) url = [NSURL URLWithString:(NSString *)url]; 122 | 123 | NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0]; 124 | 125 | _webView = [[LGWebView alloc] initWithPlaceholderViewEnabled:placeholderViewEnabled]; 126 | _webView.delegate = self; 127 | [_webView loadRequest:request]; 128 | _webView.clipsToBounds = NO; 129 | [self.view addSubview:_webView]; 130 | 131 | if (_webView.isPlaceholderViewEnabled) 132 | [_webView.placeholderView showActivityIndicatorAnimated:NO completionHandler:nil]; 133 | } 134 | 135 | #pragma mark - Dealloc 136 | 137 | - (void)dealloc 138 | { 139 | #if DEBUG 140 | NSLog(@"%s [Line %d]", __PRETTY_FUNCTION__, __LINE__); 141 | #endif 142 | } 143 | 144 | #pragma mark - Appearing 145 | 146 | - (void)viewWillLayoutSubviews 147 | { 148 | [super viewWillLayoutSubviews]; 149 | 150 | CGFloat topInset = 0.f; 151 | topInset += (self.navigationController.navigationBarHidden ? 0.f : MIN(self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height)); 152 | topInset += ([UIApplication sharedApplication].statusBarHidden ? 0.f : MIN([UIApplication sharedApplication].statusBarFrame.size.width, [UIApplication sharedApplication].statusBarFrame.size.height)); 153 | 154 | CGFloat bottomInset = 0.f; 155 | bottomInset += (self.navigationController.isToolbarHidden ? 0.f : MIN(self.navigationController.toolbar.frame.size.width, self.navigationController.toolbar.frame.size.height)); 156 | 157 | CGFloat topShift = 0.f; 158 | if ([UIDevice currentDevice].systemVersion.floatValue < 7.0 && 159 | !self.navigationController.isNavigationBarHidden && 160 | self.navigationController.navigationBar.isOpaque) 161 | topShift = topInset; 162 | 163 | CGRect newFrame = CGRectMake(0.f, topInset, self.view.frame.size.width, self.view.frame.size.height-topInset-bottomInset); 164 | 165 | if (!CGSizeEqualToSize(_webView.frame.size, newFrame.size)) 166 | _webView.frame = newFrame; 167 | } 168 | 169 | #pragma mark - UIWebView Delegate 170 | 171 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 172 | { 173 | [self loadingDidFinishWithError:error]; 174 | } 175 | 176 | - (void)webViewDidFinishLoad:(UIWebView *)webView 177 | { 178 | [self loadingDidFinishWithError:nil]; 179 | } 180 | 181 | - (void)loadingDidFinishWithError:(NSError *)error 182 | { 183 | if (_webView.isPlaceholderViewEnabled) 184 | { 185 | if (error) 186 | [_webView.placeholderView showText:error.localizedDescription animated:YES completionHandler:nil]; 187 | else 188 | [_webView.placeholderView dismissAnimated:YES completionHandler:nil]; 189 | } 190 | } 191 | 192 | - (BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType 193 | { 194 | if (inType == UIWebViewNavigationTypeLinkClicked && !self.isOpenLinksInside) 195 | { 196 | [[UIApplication sharedApplication] openURL:[inRequest URL]]; 197 | 198 | return NO; 199 | } 200 | 201 | return YES; 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Grigory Lutkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LGViewControllers 2 | 3 | Classes extends abilities of UITableViewController, UICollectionViewController, and more. 4 | - [LGScrollViewController](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGScrollViewController/LGScrollViewController.h) is view controller with LGScrollView as root view, that has LGPlaceholderView and LGRefreshView by default. View controller can watch for show/hide keyboard actions and scroll to first responder view without any manipulation. 5 | - [LGTableViewController](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGTableViewController/LGTableViewController.h) can do everything like LGScrollViewController, more than that it can reload table view asynchronously, without freezing UI. 6 | - [LGCollectionViewController](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGCollectionViewController/LGCollectionViewController.h) can do everything like LGScrollViewController, more than that it has easy initialization methods, to faster setup layout. 7 | - [LGWebViewController](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGWebViewController/LGWebViewController.h) is view controller with LGWebView as root view, that has LGPlaceholderView by default. View controller has easy initialization methods, to use it without subclassing. 8 | - [LGViewControllerAnimator](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGViewControllerAnimator/LGViewControllerAnimator.h) is class that implement slide animation between view controllers. 9 | 10 | ## Installation 11 | 12 | ### With source code 13 | 14 | - [Download repository](https://github.com/Friend-LGA/LGViewControllers/archive/master.zip), then add [LGViewControllers directory](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/) to your project. 15 | - Also you need to install libraries: 16 | - [LGPlaceholderView](https://github.com/Friend-LGA/LGPlaceholderView) 17 | - [LGRefreshView](https://github.com/Friend-LGA/LGRefreshView) 18 | 19 | ### With CocoaPods 20 | 21 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries in your projects. See the "Get Started" section for more details. 22 | 23 | #### Podfile 24 | ``` 25 | platform :ios, '6.0' 26 | pod 'LGViewControllers', '~> 1.0.0' 27 | ``` 28 | 29 | ## Usage 30 | 31 | In the source files where you need to use the library, import the header file: 32 | 33 | ```objective-c 34 | #import "LGViewControllers.h" 35 | ``` 36 | 37 | Or you can use sublibraries separately, depend of your needs: 38 | 39 | ```objective-c 40 | #import "LGScrollViewController.h" 41 | #import "LGTableViewController.h" 42 | #import "LGCollectionViewController.h" 43 | #import "LGWebViewController.h" 44 | 45 | #import "LGScrollView.h" 46 | #import "LGTableView.h" 47 | #import "LGCollectionView.h" 48 | #import "LGWebView.h" 49 | 50 | #import "LGViewControllerAnimator.h" 51 | ``` 52 | 53 | ### More 54 | 55 | For more details try Xcode [Demo project](https://github.com/Friend-LGA/LGViewControllers/blob/master/Demo) and see files: 56 | - [LGScrollViewController.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGScrollViewController/LGScrollViewController.h) and [LGScrollView.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGScrollViewController/LGScrollView.h) 57 | - [LGTableViewController.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGTableViewController/LGTableViewController.h) and [LGTableView.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGTableViewController/LGTableView.h) 58 | - [LGCollectionViewController.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGCollectionViewController/LGCollectionViewController.h) and [LGCollectionView.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGCollectionViewController/LGCollectionView.h) 59 | - [LGWebViewController.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGWebViewController/LGWebViewController.h) and [LGWebView.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGWebViewController/LGWebView.h) 60 | - [LGViewControllerAnimator.h](https://github.com/Friend-LGA/LGViewControllers/blob/master/LGViewControllers/LGViewControllerAnimator/LGViewControllerAnimator.h) 61 | 62 | ## License 63 | 64 | LGViewControllers is released under the MIT license. See [LICENSE](https://raw.githubusercontent.com/Friend-LGA/LGViewControllers/master/LICENSE) for details. 65 | --------------------------------------------------------------------------------