├── .slather.yml ├── .travis.yml ├── CHANGELOG.md ├── Example ├── GliderSample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── GliderSample.xcscheme ├── GliderSample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ContentViewController.h │ ├── ContentViewController.m │ ├── ContentViewController.xib │ ├── Helpers │ │ ├── RDButton.h │ │ ├── RDButton.m │ │ ├── RDGradientView.h │ │ └── RDGradientView.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── GliderSampleTests │ ├── Info.plist │ ├── RDGliderViewControllerTests.m │ └── RDScrollViewTests.m ├── Gemfile ├── LICENSE ├── RDGliderViewController.podspec ├── RDGliderViewController ├── RDGliderContentViewController.h ├── RDGliderContentViewController.m ├── RDGliderViewController.h ├── RDGliderViewController.m ├── RDScrollView.h └── RDScrollView.m ├── README.md └── img ├── 1.gif ├── 1.png ├── 2.gif ├── 2.png ├── 3.gif ├── 3.png ├── 4.gif └── 4.png /.slather.yml: -------------------------------------------------------------------------------- 1 | # .slather.yml 2 | 3 | coverage_service: coveralls 4 | xcodeproj: Example/GliderSample.xcodeproj 5 | scheme: GliderSample 6 | ignore: 7 | - ../* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8.3 3 | 4 | before_install: 5 | rvm install 2.3.0 6 | 7 | script: 8 | set -o pipefail; xcodebuild -project Example/GliderSample.xcodeproj -scheme GliderSample clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO; 9 | set -o pipefail; xcodebuild test -project Example/GliderSample.xcodeproj -scheme GliderSample -destination "platform=iOS Simulator,name=iPhone 6,OS=latest"; 10 | after_success: 11 | slather 12 | 13 | notifications: 14 | email: false 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## CHANGELOG 2 | 3 | #### Latest version - 0.1.76 4 | + 2017-11-22 *[FIX] Fixed issue happening with changing margin and starting from different offset than 0* 5 | + 2017-05-02 *[ENV] Added a before_install instruction to travis yml* 6 | + 2017-05-02 *[ENV] Updated ruby version required in gem file* 7 | + 2017-04-27 *[REF] refactored Scrollview to be aligned with the Swift project* 8 | 9 | #### Previously released: 10 | + 2017-04-21 *[REF] created a base RDGliderContentViewController meant to be the base case for any content view controller* 11 | + 2017-04-21 *[FIX] Fixed hidden behaviour with inverted orientationTypes* 12 | + 2017-04-21 *[POD] updated podspec* 13 | + 2017-04-21 *[DOC] Completed a basic documentation to the README and added a CHANGELOG* 14 | + 2017-04-20 *[DOC] Update README with CocoaPods and Carthage support* 15 | + 2017-04-20 *[ENV] preparing project for a pod lib* 16 | + 2017-04-18 *[ENV] Added code coverage support* 17 | + 2017-04-18 *[ENV] added yml build file for Travis CI* 18 | + 2017-04-18 *[TEST] Added unit tests for GVScrollView* 19 | + 2017-04-14 *[REF] enhanced naming* 20 | + 2017-04-14 *[FIX] issue with rendering shadow when rotating* 21 | + 2017-04-14 *[FIX] horizontal offset broken* 22 | + 2017-04-13 *[DEV] first implementation of Left To Right side* 23 | + 2017-04-13 *[REF] cleaned constraints of scrollview* 24 | + 2017-04-13 *[DEV] limited max content scroll to largest offset percent of content length* 25 | + 2017-04-13 *[DEV] added margins to reversed positions* 26 | + 2017-04-13 *[DEV] first implementation for TopToBottom GlideView* 27 | + 2017-04-12 *[FIX] fixed release notes not recognised characters* 28 | + 2017-04-12 *[DOC] added ReleaseNotes to Readme file* 29 | + 2017-04-07 *[DEV] enhanced threshold calculation for boundaries* 30 | + 2017-04-07 *[DOC] enhanced documentation on headers* 31 | + 2017-04-07 *[DEV] improved constructor of GlideViewController* 32 | + 2017-04-07 *[FIX] offset changes where altered while rotating* 33 | + 2017-04-06 *[REF] refactored contentSize handling when rotating* 34 | + 2017-04-06 *[FIX] offset is affected by the margin when rotating a rather large view* 35 | + 2017-04-06 *[DEV] enable the possibility to change multiple offsets by dragging* 36 | + 2017-04-06 *[REF] changed offsets approach to % dependant on content’s view* 37 | + 2017-04-06 *[REF] refactor usage of glideView types* 38 | + 2017-04-05 *[DEV] enhanced recalculation of offset when rotating scrollview* 39 | + 2017-04-05 *[FIX] landscape margins offset is off* 40 | + 2017-04-05 *[FIX] landscape to portrait rotation changes offset of scrollview* 41 | + 2017-04-05 *[FIX] landscape margins offset is off* 42 | -------------------------------------------------------------------------------- /Example/GliderSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C7174C321EAA0AEC000A32F3 /* RDGliderContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */; }; 11 | C78A83011EA1041C008F5878 /* RDGliderViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */; }; 12 | C79985461E92332900B80DA5 /* RDButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C79985451E92332900B80DA5 /* RDButton.m */; }; 13 | C799854D1E9246BA00B80DA5 /* RDGliderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */; }; 14 | C79985511E92480D00B80DA5 /* RDScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = C79985501E92480D00B80DA5 /* RDScrollView.m */; }; 15 | C79DCBE51E925EE000E1EF1A /* ContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C79DCBE31E925EE000E1EF1A /* ContentViewController.m */; }; 16 | C79DCBE61E925EE000E1EF1A /* ContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */; }; 17 | C79DCBE91E92677700E1EF1A /* RDGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = C79DCBE81E92677700E1EF1A /* RDGradientView.m */; }; 18 | C7D9C61E1E9220A000F80609 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C61D1E9220A000F80609 /* main.m */; }; 19 | C7D9C6211E9220A000F80609 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6201E9220A000F80609 /* AppDelegate.m */; }; 20 | C7D9C6241E9220A000F80609 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6231E9220A000F80609 /* ViewController.m */; }; 21 | C7D9C6271E9220A000F80609 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C6251E9220A000F80609 /* Main.storyboard */; }; 22 | C7D9C6291E9220A000F80609 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C6281E9220A000F80609 /* Assets.xcassets */; }; 23 | C7D9C62C1E9220A000F80609 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */; }; 24 | C7D9C6371E9220A000F80609 /* RDScrollViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | C7D9C6331E9220A000F80609 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = C7D9C6111E9220A000F80609 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = C7D9C6181E9220A000F80609; 33 | remoteInfo = GliderSample; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | C7174C301EAA0AEC000A32F3 /* RDGliderContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDGliderContentViewController.h; sourceTree = ""; }; 39 | C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDGliderContentViewController.m; sourceTree = ""; }; 40 | C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGliderViewControllerTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 41 | C79985441E92332900B80DA5 /* RDButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDButton.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 42 | C79985451E92332900B80DA5 /* RDButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDButton.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 43 | C79985491E9246BA00B80DA5 /* RDGliderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDGliderViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 44 | C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGliderViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 45 | C799854F1E92480D00B80DA5 /* RDScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDScrollView.h; sourceTree = ""; }; 46 | C79985501E92480D00B80DA5 /* RDScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDScrollView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 47 | C79DCBE21E925EE000E1EF1A /* ContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentViewController.h; sourceTree = ""; }; 48 | C79DCBE31E925EE000E1EF1A /* ContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContentViewController.m; sourceTree = ""; }; 49 | C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContentViewController.xib; sourceTree = ""; }; 50 | C79DCBE71E92677700E1EF1A /* RDGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RDGradientView.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 51 | C79DCBE81E92677700E1EF1A /* RDGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDGradientView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 52 | C7D9C6191E9220A000F80609 /* GliderSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GliderSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | C7D9C61D1E9220A000F80609 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = main.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 54 | C7D9C61F1E9220A000F80609 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 55 | C7D9C6201E9220A000F80609 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 56 | C7D9C6221E9220A000F80609 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 57 | C7D9C6231E9220A000F80609 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 58 | C7D9C6261E9220A000F80609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | C7D9C6281E9220A000F80609 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60 | C7D9C62B1E9220A000F80609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 61 | C7D9C62D1E9220A000F80609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GliderSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RDScrollViewTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 64 | C7D9C6381E9220A000F80609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | C7D9C6161E9220A000F80609 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | C7D9C62F1E9220A000F80609 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | C79985471E92462500B80DA5 /* Helpers */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | C79985441E92332900B80DA5 /* RDButton.h */, 89 | C79985451E92332900B80DA5 /* RDButton.m */, 90 | C79DCBE71E92677700E1EF1A /* RDGradientView.h */, 91 | C79DCBE81E92677700E1EF1A /* RDGradientView.m */, 92 | ); 93 | path = Helpers; 94 | sourceTree = ""; 95 | }; 96 | C79985481E92469300B80DA5 /* RDGliderViewController */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | C7174C301EAA0AEC000A32F3 /* RDGliderContentViewController.h */, 100 | C7174C311EAA0AEC000A32F3 /* RDGliderContentViewController.m */, 101 | C79985491E9246BA00B80DA5 /* RDGliderViewController.h */, 102 | C799854A1E9246BA00B80DA5 /* RDGliderViewController.m */, 103 | C799854F1E92480D00B80DA5 /* RDScrollView.h */, 104 | C79985501E92480D00B80DA5 /* RDScrollView.m */, 105 | ); 106 | name = RDGliderViewController; 107 | path = ../../RDGliderViewController; 108 | sourceTree = ""; 109 | }; 110 | C7D9C6101E9220A000F80609 = { 111 | isa = PBXGroup; 112 | children = ( 113 | C7D9C61B1E9220A000F80609 /* GliderSample */, 114 | C7D9C6351E9220A000F80609 /* GliderSampleTests */, 115 | C7D9C61A1E9220A000F80609 /* Products */, 116 | ); 117 | sourceTree = ""; 118 | }; 119 | C7D9C61A1E9220A000F80609 /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | C7D9C6191E9220A000F80609 /* GliderSample.app */, 123 | C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | C7D9C61B1E9220A000F80609 /* GliderSample */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | C79985481E92469300B80DA5 /* RDGliderViewController */, 132 | C7D9C61F1E9220A000F80609 /* AppDelegate.h */, 133 | C7D9C6201E9220A000F80609 /* AppDelegate.m */, 134 | C7D9C6221E9220A000F80609 /* ViewController.h */, 135 | C7D9C6231E9220A000F80609 /* ViewController.m */, 136 | C79DCBE21E925EE000E1EF1A /* ContentViewController.h */, 137 | C79DCBE31E925EE000E1EF1A /* ContentViewController.m */, 138 | C79DCBE41E925EE000E1EF1A /* ContentViewController.xib */, 139 | C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */, 140 | C7D9C6251E9220A000F80609 /* Main.storyboard */, 141 | C79985471E92462500B80DA5 /* Helpers */, 142 | C7D9C61C1E9220A000F80609 /* Supporting Files */, 143 | ); 144 | path = GliderSample; 145 | sourceTree = ""; 146 | }; 147 | C7D9C61C1E9220A000F80609 /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | C7D9C6281E9220A000F80609 /* Assets.xcassets */, 151 | C7D9C62D1E9220A000F80609 /* Info.plist */, 152 | C7D9C61D1E9220A000F80609 /* main.m */, 153 | ); 154 | name = "Supporting Files"; 155 | sourceTree = ""; 156 | }; 157 | C7D9C6351E9220A000F80609 /* GliderSampleTests */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | C7D9C6361E9220A000F80609 /* RDScrollViewTests.m */, 161 | C7D9C6381E9220A000F80609 /* Info.plist */, 162 | C78A83001EA1041C008F5878 /* RDGliderViewControllerTests.m */, 163 | ); 164 | path = GliderSampleTests; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | C7D9C6181E9220A000F80609 /* GliderSample */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = C7D9C63B1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSample" */; 173 | buildPhases = ( 174 | C7D9C6151E9220A000F80609 /* Sources */, 175 | C7D9C6161E9220A000F80609 /* Frameworks */, 176 | C7D9C6171E9220A000F80609 /* Resources */, 177 | C77AEA7B1E9E0AE700A1C586 /* Build number */, 178 | C77AEA7A1E9E099F00A1C586 /* CHANGELOG */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = GliderSample; 185 | productName = GliderSample; 186 | productReference = C7D9C6191E9220A000F80609 /* GliderSample.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | C7D9C6311E9220A000F80609 /* GliderSampleTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = C7D9C63E1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSampleTests" */; 192 | buildPhases = ( 193 | C7D9C62E1E9220A000F80609 /* Sources */, 194 | C7D9C62F1E9220A000F80609 /* Frameworks */, 195 | C7D9C6301E9220A000F80609 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | C7D9C6341E9220A000F80609 /* PBXTargetDependency */, 201 | ); 202 | name = GliderSampleTests; 203 | productName = GliderSampleTests; 204 | productReference = C7D9C6321E9220A000F80609 /* GliderSampleTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | C7D9C6111E9220A000F80609 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | CLASSPREFIX = RD; 214 | LastUpgradeCheck = 0820; 215 | ORGANIZATIONNAME = "Guillermo Delgado"; 216 | TargetAttributes = { 217 | C7D9C6181E9220A000F80609 = { 218 | CreatedOnToolsVersion = 8.2.1; 219 | ProvisioningStyle = Automatic; 220 | }; 221 | C7D9C6311E9220A000F80609 = { 222 | CreatedOnToolsVersion = 8.2.1; 223 | DevelopmentTeam = RT5Y8QLUYK; 224 | ProvisioningStyle = Automatic; 225 | TestTargetID = C7D9C6181E9220A000F80609; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = C7D9C6141E9220A000F80609 /* Build configuration list for PBXProject "GliderSample" */; 230 | compatibilityVersion = "Xcode 3.2"; 231 | developmentRegion = English; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = C7D9C6101E9220A000F80609; 238 | productRefGroup = C7D9C61A1E9220A000F80609 /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | C7D9C6181E9220A000F80609 /* GliderSample */, 243 | C7D9C6311E9220A000F80609 /* GliderSampleTests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | C7D9C6171E9220A000F80609 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | C79DCBE61E925EE000E1EF1A /* ContentViewController.xib in Resources */, 254 | C7D9C62C1E9220A000F80609 /* LaunchScreen.storyboard in Resources */, 255 | C7D9C6291E9220A000F80609 /* Assets.xcassets in Resources */, 256 | C7D9C6271E9220A000F80609 /* Main.storyboard in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | C7D9C6301E9220A000F80609 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | /* End PBXResourcesBuildPhase section */ 268 | 269 | /* Begin PBXShellScriptBuildPhase section */ 270 | C77AEA7A1E9E099F00A1C586 /* CHANGELOG */ = { 271 | isa = PBXShellScriptBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | inputPaths = ( 276 | ); 277 | name = CHANGELOG; 278 | outputPaths = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | shellPath = /bin/sh; 282 | shellScript = "FILE='../CHANGELOG.md'\nbranch=$(git branch | sed -n -e 's/^\\* \\(.*\\)/\\1/p')\nLATEST_TAG=$(git describe --abbrev=0)\n\nBUILD_NUM=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nBUILD_VER=$(/usr/libexec/Plistbuddy -c \"Print CFBundleShortVersionString\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\n\n> $FILE\necho '## CHANGELOG' >> $FILE\necho '' >> $FILE\necho '#### Latest version - ' $BUILD_VER.$BUILD_NUM >> $FILE\ngit log $LATEST_TAG..HEAD --no-merges --grep=\"^\\[\" --pretty=format:\"+ %ad *%s*\" -50 --date=short | sed '/^\\s*$/d' >> $FILE\necho '' >> $FILE\necho '#### Previously released:' >> $FILE\ngit log INIT..$LATEST_TAG --no-merges --grep=\"^\\[\" --pretty=format:\"+ %ad *%s*\" -50 --date=short | sed '/^\\s*$/d' >> $FILE"; 283 | }; 284 | C77AEA7B1E9E0AE700A1C586 /* Build number */ = { 285 | isa = PBXShellScriptBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | inputPaths = ( 290 | ); 291 | name = "Build number"; 292 | outputPaths = ( 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | shellPath = /bin/sh; 296 | shellScript = "#Update build number with number of git commits if in release mode\nbuildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\""; 297 | }; 298 | /* End PBXShellScriptBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | C7D9C6151E9220A000F80609 /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | C79985511E92480D00B80DA5 /* RDScrollView.m in Sources */, 306 | C79DCBE91E92677700E1EF1A /* RDGradientView.m in Sources */, 307 | C7D9C6241E9220A000F80609 /* ViewController.m in Sources */, 308 | C799854D1E9246BA00B80DA5 /* RDGliderViewController.m in Sources */, 309 | C79DCBE51E925EE000E1EF1A /* ContentViewController.m in Sources */, 310 | C79985461E92332900B80DA5 /* RDButton.m in Sources */, 311 | C7174C321EAA0AEC000A32F3 /* RDGliderContentViewController.m in Sources */, 312 | C7D9C6211E9220A000F80609 /* AppDelegate.m in Sources */, 313 | C7D9C61E1E9220A000F80609 /* main.m in Sources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | C7D9C62E1E9220A000F80609 /* Sources */ = { 318 | isa = PBXSourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | C78A83011EA1041C008F5878 /* RDGliderViewControllerTests.m in Sources */, 322 | C7D9C6371E9220A000F80609 /* RDScrollViewTests.m in Sources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | /* End PBXSourcesBuildPhase section */ 327 | 328 | /* Begin PBXTargetDependency section */ 329 | C7D9C6341E9220A000F80609 /* PBXTargetDependency */ = { 330 | isa = PBXTargetDependency; 331 | target = C7D9C6181E9220A000F80609 /* GliderSample */; 332 | targetProxy = C7D9C6331E9220A000F80609 /* PBXContainerItemProxy */; 333 | }; 334 | /* End PBXTargetDependency section */ 335 | 336 | /* Begin PBXVariantGroup section */ 337 | C7D9C6251E9220A000F80609 /* Main.storyboard */ = { 338 | isa = PBXVariantGroup; 339 | children = ( 340 | C7D9C6261E9220A000F80609 /* Base */, 341 | ); 342 | name = Main.storyboard; 343 | sourceTree = ""; 344 | }; 345 | C7D9C62A1E9220A000F80609 /* LaunchScreen.storyboard */ = { 346 | isa = PBXVariantGroup; 347 | children = ( 348 | C7D9C62B1E9220A000F80609 /* Base */, 349 | ); 350 | name = LaunchScreen.storyboard; 351 | sourceTree = ""; 352 | }; 353 | /* End PBXVariantGroup section */ 354 | 355 | /* Begin XCBuildConfiguration section */ 356 | C7D9C6391E9220A000F80609 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ALWAYS_SEARCH_USER_PATHS = NO; 360 | CLANG_ANALYZER_NONNULL = YES; 361 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 362 | CLANG_CXX_LIBRARY = "libc++"; 363 | CLANG_ENABLE_MODULES = YES; 364 | CLANG_ENABLE_OBJC_ARC = YES; 365 | CLANG_WARN_BOOL_CONVERSION = YES; 366 | CLANG_WARN_CONSTANT_CONVERSION = YES; 367 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 368 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 369 | CLANG_WARN_EMPTY_BODY = YES; 370 | CLANG_WARN_ENUM_CONVERSION = YES; 371 | CLANG_WARN_INFINITE_RECURSION = YES; 372 | CLANG_WARN_INT_CONVERSION = YES; 373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 374 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = dwarf; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | ENABLE_TESTABILITY = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu99; 383 | GCC_DYNAMIC_NO_PIC = NO; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | GCC_OPTIMIZATION_LEVEL = 0; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 397 | MTL_ENABLE_DEBUG_INFO = YES; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = iphoneos; 400 | TARGETED_DEVICE_FAMILY = "1,2"; 401 | }; 402 | name = Debug; 403 | }; 404 | C7D9C63A1E9220A000F80609 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_ANALYZER_NONNULL = YES; 409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 410 | CLANG_CXX_LIBRARY = "libc++"; 411 | CLANG_ENABLE_MODULES = YES; 412 | CLANG_ENABLE_OBJC_ARC = YES; 413 | CLANG_WARN_BOOL_CONVERSION = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 416 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 417 | CLANG_WARN_EMPTY_BODY = YES; 418 | CLANG_WARN_ENUM_CONVERSION = YES; 419 | CLANG_WARN_INFINITE_RECURSION = YES; 420 | CLANG_WARN_INT_CONVERSION = YES; 421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 422 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 423 | CLANG_WARN_UNREACHABLE_CODE = YES; 424 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 425 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 426 | COPY_PHASE_STRIP = NO; 427 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 428 | ENABLE_NS_ASSERTIONS = NO; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | GCC_C_LANGUAGE_STANDARD = gnu99; 431 | GCC_NO_COMMON_BLOCKS = YES; 432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 434 | GCC_WARN_UNDECLARED_SELECTOR = YES; 435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 436 | GCC_WARN_UNUSED_FUNCTION = YES; 437 | GCC_WARN_UNUSED_VARIABLE = YES; 438 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 439 | MTL_ENABLE_DEBUG_INFO = NO; 440 | SDKROOT = iphoneos; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | VALIDATE_PRODUCT = YES; 443 | }; 444 | name = Release; 445 | }; 446 | C7D9C63C1E9220A000F80609 /* Debug */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | CLANG_WARN_ASSIGN_ENUM = YES; 451 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 452 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 453 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 454 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 455 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 456 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; 457 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; 458 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 459 | CODE_SIGN_IDENTITY = "iPhone Developer"; 460 | DEVELOPMENT_TEAM = ""; 461 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 462 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 463 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 464 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 465 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 466 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 467 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 468 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 469 | GCC_WARN_SHADOW = YES; 470 | GCC_WARN_SIGN_COMPARE = YES; 471 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 472 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 473 | GCC_WARN_UNUSED_LABEL = YES; 474 | GCC_WARN_UNUSED_PARAMETER = NO; 475 | INFOPLIST_FILE = GliderSample/Info.plist; 476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 477 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSample; 478 | PRODUCT_NAME = "$(TARGET_NAME)"; 479 | }; 480 | name = Debug; 481 | }; 482 | C7D9C63D1E9220A000F80609 /* Release */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | CLANG_WARN_ASSIGN_ENUM = YES; 487 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 488 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 489 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 490 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 491 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 492 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; 493 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; 494 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 495 | CODE_SIGN_IDENTITY = "iPhone Developer"; 496 | DEVELOPMENT_TEAM = ""; 497 | GCC_GENERATE_TEST_COVERAGE_FILES = NO; 498 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 499 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 500 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 501 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 502 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 503 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 504 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 505 | GCC_WARN_SHADOW = YES; 506 | GCC_WARN_SIGN_COMPARE = YES; 507 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 508 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 509 | GCC_WARN_UNUSED_LABEL = YES; 510 | GCC_WARN_UNUSED_PARAMETER = NO; 511 | INFOPLIST_FILE = GliderSample/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 513 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSample; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | }; 516 | name = Release; 517 | }; 518 | C7D9C63F1E9220A000F80609 /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | BUNDLE_LOADER = "$(TEST_HOST)"; 522 | DEVELOPMENT_TEAM = RT5Y8QLUYK; 523 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 524 | INFOPLIST_FILE = GliderSampleTests/Info.plist; 525 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 526 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSampleTests; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GliderSample.app/GliderSample"; 529 | }; 530 | name = Debug; 531 | }; 532 | C7D9C6401E9220A000F80609 /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | BUNDLE_LOADER = "$(TEST_HOST)"; 536 | DEVELOPMENT_TEAM = RT5Y8QLUYK; 537 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 538 | INFOPLIST_FILE = GliderSampleTests/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = GuillermoRD.GliderSampleTests; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GliderSample.app/GliderSample"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | C7D9C6141E9220A000F80609 /* Build configuration list for PBXProject "GliderSample" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | C7D9C6391E9220A000F80609 /* Debug */, 553 | C7D9C63A1E9220A000F80609 /* Release */, 554 | ); 555 | defaultConfigurationIsVisible = 0; 556 | defaultConfigurationName = Release; 557 | }; 558 | C7D9C63B1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSample" */ = { 559 | isa = XCConfigurationList; 560 | buildConfigurations = ( 561 | C7D9C63C1E9220A000F80609 /* Debug */, 562 | C7D9C63D1E9220A000F80609 /* Release */, 563 | ); 564 | defaultConfigurationIsVisible = 0; 565 | defaultConfigurationName = Release; 566 | }; 567 | C7D9C63E1E9220A000F80609 /* Build configuration list for PBXNativeTarget "GliderSampleTests" */ = { 568 | isa = XCConfigurationList; 569 | buildConfigurations = ( 570 | C7D9C63F1E9220A000F80609 /* Debug */, 571 | C7D9C6401E9220A000F80609 /* Release */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | /* End XCConfigurationList section */ 577 | }; 578 | rootObject = C7D9C6111E9220A000F80609 /* Project object */; 579 | } 580 | -------------------------------------------------------------------------------- /Example/GliderSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/GliderSample.xcodeproj/xcshareddata/xcschemes/GliderSample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Example/GliderSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/GliderSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | //- (void)applicationWillResignActive:(UIApplication *)application { 25 | // // 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. 26 | // // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | //} 28 | // 29 | // 30 | //- (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // // 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. 32 | // // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | //} 34 | // 35 | // 36 | //- (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | //} 39 | // 40 | // 41 | //- (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // // 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. 43 | //} 44 | // 45 | // 46 | //- (void)applicationWillTerminate:(UIApplication *)application { 47 | // // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | //} 49 | // 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Example/GliderSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Example/GliderSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/GliderSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 141 | 154 | 166 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /Example/GliderSample/ContentViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.h 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RDGliderContentViewController.h" 11 | 12 | @interface ContentViewController : RDGliderContentViewController 13 | 14 | - (void)setIndex:(NSUInteger)index ofMax:(NSUInteger)max; 15 | - (void)setOffset:(NSString *)offset; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/GliderSample/ContentViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 7 | // 8 | 9 | #import "ContentViewController.h" 10 | 11 | @interface ContentViewController () 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *indexTopLabel; 14 | @property (weak, nonatomic) IBOutlet UILabel *offsetTopLabel; 15 | @property (weak, nonatomic) IBOutlet UILabel *indexBottomLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *offsetBottomLabel; 17 | 18 | @end 19 | 20 | @implementation ContentViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | self.showShadow = YES; 26 | self.cornerRadius = 10.0f; 27 | } 28 | 29 | - (void)setIndex:(NSUInteger)index ofMax:(NSUInteger)max { 30 | [self.indexTopLabel setText:[NSString stringWithFormat:@"Index (%lu of %lu)",(unsigned long)index , (unsigned long)max]]; 31 | [self.indexBottomLabel setText:[NSString stringWithFormat:@"Index (%lu of %lu)",(unsigned long)index , (unsigned long)max]]; 32 | } 33 | 34 | - (void)setOffset:(NSString *)offset { 35 | [self.offsetTopLabel setText:[NSString stringWithFormat:@"offset %@", offset]]; 36 | [self.offsetBottomLabel setText:[NSString stringWithFormat:@"offset %@", offset]]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Example/GliderSample/ContentViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 41 | 47 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Example/GliderSample/Helpers/RDButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // GAButton.h 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RDButton : UIButton 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/GliderSample/Helpers/RDButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // GAButton.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import "RDButton.h" 10 | 11 | @interface RDButton() 12 | 13 | @property (nonatomic) UIColor *bgColor; 14 | 15 | @end 16 | 17 | @implementation RDButton 18 | 19 | - (void)drawRect:(CGRect)rect { 20 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 21 | CGContextAddEllipseInRect(ctx, rect); 22 | CGContextSetFillColor(ctx, CGColorGetComponents([self.bgColor CGColor])); 23 | CGContextFillPath(ctx); 24 | } 25 | 26 | - (void)setBackgroundColor:(UIColor *)backgroundColor { 27 | _bgColor = [backgroundColor copy]; 28 | super.backgroundColor = [UIColor clearColor]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Example/GliderSample/Helpers/RDGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDGradientView.h 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RDGradientView : UIView 12 | 13 | @property (nonatomic, strong, readonly) CAGradientLayer *layer; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/GliderSample/Helpers/RDGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDGradientView.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 7 | // 8 | 9 | #import "RDGradientView.h" 10 | 11 | @implementation RDGradientView 12 | 13 | @dynamic layer; 14 | 15 | + (Class)layerClass { 16 | return [CAGradientLayer class]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Example/GliderSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.1 19 | CFBundleVersion 20 | 76 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | UIInterfaceOrientationPortraitUpsideDown 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationPortraitUpsideDown 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/GliderSample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /Example/GliderSample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "RDGliderViewController.h" 11 | #import "ContentViewController.h" 12 | #import "RDGradientView.h" 13 | 14 | @interface ViewController () 15 | 16 | @property (nonatomic) RDGliderViewController *leftToRightGlideVC; 17 | @property (nonatomic) RDGliderViewController *bottomToTopGlideVC; 18 | @property (nonatomic) RDGliderViewController *topToBottomGlideVC; 19 | @property (nonatomic) RDGliderViewController *rightToLeftGlideVC; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | 28 | RDGradientView *bgGradient = [[RDGradientView alloc] initWithFrame:self.view.bounds]; 29 | bgGradient.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 30 | bgGradient.layer.colors = @[(id)[UIColor colorWithRed:(CGFloat)0.643 green:(CGFloat)0.569 blue:(CGFloat)0.776 alpha:1].CGColor, 31 | (id)[UIColor colorWithRed:(CGFloat)(CGFloat)0.573 green:(CGFloat)0.875 blue:(CGFloat)0.678 alpha:1].CGColor]; 32 | [self.view insertSubview:bgGradient atIndex:0]; 33 | 34 | [self initRightToLeftGlideView]; 35 | [self initBottomToTopGlideView]; 36 | [self initTopToBottomGlideView]; 37 | [self initLeftToRightGlideView]; 38 | } 39 | 40 | - (void)initRightToLeftGlideView{ 41 | self.rightToLeftGlideVC = [[RDGliderViewController alloc] initOn:self 42 | WithContent:[[ContentViewController alloc] initWithLength:200.0f] 43 | type:RDScrollViewOrientationRightToLeft 44 | AndOffsets:@[@(0), @(1)]]; 45 | self.rightToLeftGlideVC.delegate = self; 46 | } 47 | 48 | - (void)initBottomToTopGlideView { 49 | self.bottomToTopGlideVC = [[RDGliderViewController alloc] initOn:self 50 | WithContent:[ContentViewController new] 51 | type:RDScrollViewOrientationBottomToTop 52 | AndOffsets:@[@(0), 53 | @(0.6), 54 | @(0.2), 55 | @(0.4), 56 | @(0.8)]]; 57 | self.bottomToTopGlideVC.delegate = self; 58 | self.bottomToTopGlideVC.marginOffset = 30; 59 | } 60 | 61 | - (void)initTopToBottomGlideView { 62 | self.topToBottomGlideVC = [[RDGliderViewController alloc] initOn:self 63 | WithContent:[[ContentViewController alloc] initWithLength:400.0f] 64 | type:RDScrollViewOrientationTopToBottom 65 | AndOffsets:@[@(0), 66 | @(0.5), 67 | @(1)]]; 68 | self.topToBottomGlideVC.delegate = self; 69 | } 70 | 71 | - (void)initLeftToRightGlideView { 72 | self.leftToRightGlideVC = [[RDGliderViewController alloc] initOn:self 73 | WithContent:[ContentViewController new] 74 | type:RDScrollViewOrientationLeftToRight 75 | AndOffsets:@[@(0), 76 | @(0.6), 77 | @(0.2), 78 | @(0.4), 79 | @(0.8), 80 | @(1)]]; 81 | self.leftToRightGlideVC.delegate = self; 82 | self.leftToRightGlideVC.marginOffset = 10; 83 | } 84 | 85 | #pragma mark - Actions 86 | 87 | - (IBAction)bottomToTopButtonPressed:(id)sender { 88 | if ([self.bottomToTopGlideVC currentOffsetIndex] < [[self.bottomToTopGlideVC offsets] count] - 1) { 89 | [self.bottomToTopGlideVC expand]; 90 | } else { 91 | [self.bottomToTopGlideVC shake]; 92 | } 93 | } 94 | 95 | - (IBAction)leftToRightButtonPressed:(id)sender { 96 | if ([self.leftToRightGlideVC currentOffsetIndex] < [[self.leftToRightGlideVC offsets] count] - 1) { 97 | [self.leftToRightGlideVC expand]; 98 | } else { 99 | [self.leftToRightGlideVC shake]; 100 | } 101 | } 102 | 103 | - (IBAction)topToBottomButtonPressed:(id)sender { 104 | if ([self.topToBottomGlideVC currentOffsetIndex] < [[self.topToBottomGlideVC offsets] count] - 1) { 105 | [self.topToBottomGlideVC expand]; 106 | } else { 107 | [self.topToBottomGlideVC shake]; 108 | } 109 | } 110 | 111 | - (IBAction)rightToLeftButtonPressed:(id)sender { 112 | if ([self.rightToLeftGlideVC currentOffsetIndex] < [[self.rightToLeftGlideVC offsets] count] - 1) { 113 | [self.rightToLeftGlideVC expand]; 114 | } else { 115 | [self.rightToLeftGlideVC shake]; 116 | } 117 | } 118 | 119 | #pragma mark - GlideViewControllerDelegate 120 | 121 | - (void)glideViewController:(RDGliderViewController *)glideViewController hasChangedOffsetOfContent:(CGPoint)offset { 122 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController; 123 | [vc setOffset:NSStringFromCGPoint(offset)]; 124 | } 125 | 126 | - (void)glideViewControllerDidExpand:(RDGliderViewController *)glideViewController { 127 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController; 128 | [vc setIndex:glideViewController.currentOffsetIndex ofMax:[glideViewController.offsets count] - 1]; 129 | } 130 | 131 | - (void)glideViewControllerDidCollapse:(RDGliderViewController *)glideViewController { 132 | ContentViewController *vc = (ContentViewController *)glideViewController.contentViewController; 133 | [vc setIndex:glideViewController.currentOffsetIndex ofMax:[glideViewController.offsets count] - 1]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /Example/GliderSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. 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 | -------------------------------------------------------------------------------- /Example/GliderSampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/GliderSampleTests/RDGliderViewControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDGliderViewController.m 3 | // GliderSample 4 | // 5 | // Created by Guillermo Delgado on 14/04/2017. 6 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RDGliderViewController.h" 11 | 12 | @interface RDGliderViewController () 13 | 14 | @property (nonatomic) RDScrollView *scrollView; 15 | 16 | - (NSUInteger)nearestOffsetIndexTo:(CGPoint)contentOffset; 17 | 18 | @end 19 | 20 | @interface RDGliderViewControllerTests : XCTestCase { 21 | XCTestExpectation *_testExpWill; 22 | XCTestExpectation *_testExpDid; 23 | } 24 | 25 | @property (nonatomic) RDGliderViewController *gliderVC; 26 | 27 | @end 28 | 29 | @implementation RDGliderViewControllerTests 30 | 31 | - (void)setUp { 32 | [super setUp]; 33 | 34 | self.gliderVC = [[RDGliderViewController alloc] initOn:[UIViewController new] 35 | WithContent:[UIViewController new] 36 | type:RDScrollViewOrientationRightToLeft 37 | AndOffsets:@[@0, @1]]; 38 | self.gliderVC.delegate = self; 39 | XCTAssertNotNil(self.gliderVC); 40 | } 41 | 42 | - (void)tearDown { 43 | self.gliderVC = nil; 44 | [super tearDown]; 45 | } 46 | 47 | - (void)testOffsets { 48 | NSArray *ar = @[@0, @1]; 49 | XCTAssertTrue([[self.gliderVC offsets] isEqualToArray:ar]); 50 | XCTAssertThrows([self.gliderVC setOffsets:@[]]); 51 | 52 | [self.gliderVC setOffsets:@[@0, @0.5, @1]]; 53 | XCTAssertTrue(self.gliderVC.marginOffset == self.gliderVC.scrollView.margin); 54 | 55 | 56 | self.gliderVC.scrollView = nil; 57 | XCTAssertThrows([self.gliderVC setOffsets:@[@0]]); 58 | XCTAssertThrows([self.gliderVC offsets]); 59 | XCTAssertTrue(self.gliderVC.currentOffsetIndex == 0); 60 | XCTAssertTrue(self.gliderVC.marginOffset == 0.0f); 61 | } 62 | 63 | - (void)testOrientationType { 64 | XCTAssertTrue([self.gliderVC orientationType] == RDScrollViewOrientationRightToLeft); 65 | 66 | self.gliderVC.scrollView = nil; 67 | XCTAssertTrue([self.gliderVC orientationType] == RDScrollViewOrientationUnknown); 68 | } 69 | 70 | - (void)testContentViewController { 71 | 72 | XCTAssertThrows([self.gliderVC setContentViewController:[self nilReturnMethod] 73 | type:RDScrollViewOrientationUnknown 74 | offsets:@[@1]]); 75 | 76 | XCTAssertThrows([self.gliderVC setContentViewController:[UIViewController new] 77 | type:RDScrollViewOrientationUnknown 78 | offsets:[self nilReturnMethod]]); 79 | 80 | UIViewController *contentVC = [UIViewController new]; 81 | [self.gliderVC setContentViewController:contentVC 82 | type:RDScrollViewOrientationRightToLeft 83 | offsets:@[@1]]; 84 | XCTAssertTrue(self.gliderVC.scrollView.orientationType == RDScrollViewOrientationRightToLeft); 85 | XCTAssertTrue([self.gliderVC.scrollView.offsets isEqualToArray:@[@1]]); 86 | } 87 | 88 | - (void)testShake { 89 | CGRect frame = self.gliderVC.scrollView.frame; 90 | 91 | [self.gliderVC shake]; 92 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame)); 93 | 94 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationLeftToRight; 95 | [self.gliderVC shake]; 96 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame)); 97 | 98 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationBottomToTop; 99 | [self.gliderVC shake]; 100 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame)); 101 | 102 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationTopToBottom; 103 | [self.gliderVC shake]; 104 | XCTAssertTrue(CGRectEqualToRect(frame, self.gliderVC.scrollView.frame)); 105 | } 106 | 107 | - (void)testExpand { 108 | _testExpWill = [self expectationWithDescription:@"Will"]; 109 | _testExpDid = [self expectationWithDescription:@"Did"]; 110 | 111 | _testExpWill.assertForOverFulfill = NO; 112 | _testExpDid.assertForOverFulfill = NO; 113 | 114 | [self.gliderVC expand]; 115 | 116 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2]; 117 | XCTAssertTrue([self.gliderVC isOpen]); 118 | } 119 | 120 | - (void)testCollapse { 121 | _testExpWill = [self expectationWithDescription:@"Will"]; 122 | _testExpDid = [self expectationWithDescription:@"Did"]; 123 | 124 | _testExpWill.assertForOverFulfill = NO; 125 | _testExpDid.assertForOverFulfill = NO; 126 | 127 | [self.gliderVC collapse]; 128 | 129 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2]; 130 | } 131 | 132 | - (void)testClose { 133 | _testExpWill = [self expectationWithDescription:@"Will"]; 134 | _testExpDid = [self expectationWithDescription:@"Did"]; 135 | 136 | _testExpWill.assertForOverFulfill = NO; 137 | _testExpDid.assertForOverFulfill = NO; 138 | 139 | [self.gliderVC close]; 140 | 141 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2]; 142 | XCTAssertFalse([self.gliderVC isOpen]); 143 | } 144 | 145 | - (void)testChangeOffset { 146 | _testExpWill = [self expectationWithDescription:@"Will"]; 147 | _testExpDid = [self expectationWithDescription:@"Did"]; 148 | 149 | _testExpWill.assertForOverFulfill = NO; 150 | _testExpDid.assertForOverFulfill = NO; 151 | 152 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 0); 153 | 154 | [self.gliderVC setOffsets:@[@1, @0.5, @0]]; 155 | [self.gliderVC changeOffsetTo:2 animated:NO]; 156 | 157 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2]; 158 | XCTAssertTrue([self.gliderVC isOpen]); 159 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 2); 160 | 161 | _testExpWill = [self expectationWithDescription:@"Will"]; 162 | _testExpDid = [self expectationWithDescription:@"Did"]; 163 | 164 | [self.gliderVC setOffsets:@[@1, @0.5, @0]]; 165 | [self.gliderVC changeOffsetTo:0 animated:NO]; 166 | 167 | [self waitForExpectations:@[_testExpDid, _testExpWill] timeout:2]; 168 | XCTAssertFalse([self.gliderVC isOpen]); 169 | XCTAssertTrue([self.gliderVC currentOffsetIndex] == 0); 170 | } 171 | 172 | - (void)testNearestOffsetIndex { 173 | CGPoint point = CGPointMake(0, 0); 174 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationRightToLeft; 175 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 0); 176 | 177 | point = CGPointMake(self.gliderVC.scrollView.contentSize.width, 0); 178 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationLeftToRight; 179 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 1); 180 | 181 | point = CGPointMake(0, self.gliderVC.scrollView.contentSize.width / 2.5); 182 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationBottomToTop; 183 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 0); 184 | 185 | point = CGPointMake(0, self.gliderVC.scrollView.contentSize.width / 1.4); 186 | self.gliderVC.scrollView.orientationType = RDScrollViewOrientationTopToBottom; 187 | XCTAssertTrue([self.gliderVC nearestOffsetIndexTo:point] == 1); 188 | } 189 | 190 | #pragma mark - Helper methods 191 | 192 | - (id)nilReturnMethod { 193 | return nil; 194 | } 195 | 196 | - (void)glideViewController:(nonnull RDGliderViewController *)glideViewController 197 | hasChangedOffsetOfContent:(CGPoint)offset { 198 | XCTAssertNotNil(glideViewController); 199 | 200 | } 201 | 202 | - (void)glideViewControllerWillExpand:(nonnull RDGliderViewController *)glideViewController { 203 | XCTAssertNotNil(glideViewController); 204 | [_testExpWill fulfill]; 205 | } 206 | 207 | - (void)glideViewControllerWillCollapse:(nonnull RDGliderViewController *)glideViewController { 208 | XCTAssertNotNil(glideViewController); 209 | [_testExpWill fulfill]; 210 | } 211 | 212 | - (void)glideViewControllerDidExpand:(nonnull RDGliderViewController *)glideViewController { 213 | XCTAssertNotNil(glideViewController); 214 | [_testExpDid fulfill]; 215 | } 216 | 217 | - (void)glideViewControllerDidCollapse:(nonnull RDGliderViewController *)glideViewController { 218 | XCTAssertNotNil(glideViewController); 219 | [_testExpDid fulfill]; 220 | } 221 | 222 | 223 | @end 224 | -------------------------------------------------------------------------------- /Example/GliderSampleTests/RDScrollViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // GlideViewTests.m 3 | // GlideViewTests 4 | // 5 | // Created by Guillermo Delgado on 03/04/2017. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RDScrollView.h" 11 | 12 | @interface RDScrollView() 13 | 14 | @property (nonatomic) NSUInteger offsetIndex; 15 | 16 | @end 17 | 18 | @interface RDScrollViewTests : XCTestCase 19 | 20 | @property (nonatomic) RDScrollView *rdScrollView; 21 | 22 | @end 23 | 24 | @implementation RDScrollViewTests 25 | 26 | - (void)setUp { 27 | [super setUp]; 28 | self.rdScrollView = [[RDScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 29 | } 30 | 31 | - (void)tearDown { 32 | self.rdScrollView = nil; 33 | [super tearDown]; 34 | } 35 | 36 | - (void)testInit { 37 | XCTAssertTrue(self.rdScrollView.margin == 20); 38 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationRightToLeft); 39 | XCTAssertFalse(self.rdScrollView.showsVerticalScrollIndicator); 40 | XCTAssertFalse(self.rdScrollView.showsHorizontalScrollIndicator); 41 | XCTAssertFalse(self.rdScrollView.bounces); 42 | XCTAssertTrue(self.rdScrollView.directionalLockEnabled); 43 | XCTAssertFalse(self.rdScrollView.pagingEnabled); 44 | XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(self.rdScrollView.contentInset, UIEdgeInsetsZero)); 45 | XCTAssertTrue(self.rdScrollView.decelerationRate == UIScrollViewDecelerationRateFast); 46 | } 47 | 48 | - (void)testExpandOffset { 49 | XCTestExpectation *ex0 = [self expectationWithDescription:@"Success0"]; 50 | 51 | [self.rdScrollView expandWithCompletion:^(BOOL finished) { 52 | if (finished) { 53 | [ex0 fulfill]; 54 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 55 | } 56 | }]; 57 | 58 | [self.rdScrollView setOffsets:@[@0,@.5,@1]]; 59 | 60 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3); 61 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 62 | 63 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"]; 64 | 65 | [self.rdScrollView expandWithCompletion:^(BOOL finished) { 66 | if (finished) { 67 | [ex1 fulfill]; 68 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1); 69 | } 70 | }]; 71 | 72 | [self waitForExpectations:@[ex0, ex1] timeout:2]; 73 | 74 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1); 75 | 76 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"]; 77 | 78 | [self.rdScrollView expandWithCompletion:^(BOOL finished) { 79 | if (finished) { 80 | [ex2 fulfill]; 81 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2); 82 | } 83 | }]; 84 | 85 | [self waitForExpectations:@[ex2] timeout:2]; 86 | 87 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2); 88 | 89 | XCTestExpectation *ex3 = [self expectationWithDescription:@"Success3"]; 90 | 91 | [self.rdScrollView expandWithCompletion:^(BOOL finished) { 92 | if (finished) { 93 | [ex3 fulfill]; 94 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2); 95 | } 96 | }]; 97 | 98 | [self waitForExpectations:@[ex3] timeout:2]; 99 | } 100 | 101 | - (void)testCollapseOffset { 102 | [self.rdScrollView setOffsets:@[@0,@.5,@1]]; 103 | [self.rdScrollView setOffsetIndex:[[self.rdScrollView offsets] count] - 1]; 104 | 105 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3); 106 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2); 107 | 108 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"]; 109 | 110 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) { 111 | if (finished) { 112 | [ex1 fulfill]; 113 | XCTAssertTrue([self.rdScrollView offsetIndex] == 1); 114 | } 115 | }]; 116 | 117 | [self waitForExpectations:@[ex1] timeout:2]; 118 | 119 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"]; 120 | 121 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) { 122 | if (finished) { 123 | [ex2 fulfill]; 124 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 125 | } 126 | }]; 127 | 128 | [self waitForExpectations:@[ex2] timeout:2]; 129 | XCTestExpectation *ex3 = [self expectationWithDescription:@"Success3"]; 130 | 131 | [self.rdScrollView collapseWithCompletion:^(BOOL finished) { 132 | if (finished) { 133 | [ex3 fulfill]; 134 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 135 | } 136 | }]; 137 | 138 | [self waitForExpectations:@[ex3] timeout:2]; 139 | } 140 | 141 | - (void)testCloseOffset { 142 | XCTestExpectation *ex0 = [self expectationWithDescription:@"Success0"]; 143 | 144 | [self.rdScrollView closeWithCompletion:^(BOOL finished) { 145 | if (finished) { 146 | [ex0 fulfill]; 147 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 148 | } 149 | }]; 150 | 151 | [self.rdScrollView setOffsets:@[@0,@.5,@1]]; 152 | 153 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3); 154 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 155 | 156 | XCTestExpectation *ex1 = [self expectationWithDescription:@"Success1"]; 157 | 158 | [self.rdScrollView closeWithCompletion:^(BOOL finished) { 159 | if (finished) { 160 | [ex1 fulfill]; 161 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 162 | } 163 | }]; 164 | 165 | [self waitForExpectations:@[ex0, ex1] timeout:2]; 166 | 167 | [self.rdScrollView setOffsetIndex:[[self.rdScrollView offsets] count] - 1]; 168 | 169 | XCTAssertTrue([[self.rdScrollView offsets] count] == 3); 170 | XCTAssertTrue([self.rdScrollView offsetIndex] == 2); 171 | 172 | XCTestExpectation *ex2 = [self expectationWithDescription:@"Success2"]; 173 | 174 | [self.rdScrollView closeWithCompletion:^(BOOL finished) { 175 | if (finished) { 176 | [ex2 fulfill]; 177 | XCTAssertTrue([self.rdScrollView offsetIndex] == 0); 178 | } 179 | }]; 180 | 181 | [self waitForExpectations:@[ex2] timeout:2]; 182 | } 183 | 184 | - (void)testOffsets { 185 | NSArray *ar = @[@0, @3, @0.123]; 186 | XCTAssertThrows([self.rdScrollView setOffsets:ar]); 187 | 188 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationRightToLeft); 189 | 190 | ar = @[@1, @0.6, @0.3, @0]; 191 | [self.rdScrollView setOffsets:ar]; 192 | 193 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray: 194 | [ar sortedArrayUsingSelector:@selector(compare:)]]); 195 | 196 | [self.rdScrollView setOrientationType:RDScrollViewOrientationBottomToTop]; 197 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationBottomToTop); 198 | 199 | ar = @[@1, @0.4, @0.3, @0.4]; 200 | [self.rdScrollView setOffsets:ar]; 201 | 202 | NSArray *aux = @[@0.3, @0.4, @1]; 203 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray:aux]); 204 | 205 | [self.rdScrollView setOrientationType:RDScrollViewOrientationLeftToRight]; 206 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationLeftToRight); 207 | 208 | [self.rdScrollView setOffsets:ar]; 209 | 210 | aux = @[@0.7, @0.6, @0]; 211 | 212 | for (int i = 0 ; i < [self.rdScrollView.offsets count] - 1 ; i++) { 213 | XCTAssertTrue(self.rdScrollView.offsets[i].floatValue == aux[i].floatValue); 214 | } 215 | 216 | [self.rdScrollView setOrientationType:RDScrollViewOrientationTopToBottom]; 217 | XCTAssertTrue(self.rdScrollView.orientationType == RDScrollViewOrientationTopToBottom); 218 | 219 | ar = @[@0, @0.5, @1]; 220 | [self.rdScrollView setOffsets:ar]; 221 | 222 | aux = @[@1, @0.5, @0]; 223 | XCTAssertTrue([[self.rdScrollView offsets] isEqualToArray:aux]); 224 | } 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source "https://rubygems.org" 3 | ruby '~> 2.3.0' 4 | 5 | gem 'coveralls', require: false 6 | gem 'slather', require: false 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Guillermo RD 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 | -------------------------------------------------------------------------------- /RDGliderViewController.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint RDGliderViewController.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'RDGliderViewController' 11 | s.version = '0.1.69' 12 | s.summary = 'Control for a draggable ViewController gliding over another ViewController.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | RDGliderViewController is a view controller that manages a scrollable view using one side as a sliding point, 22 | choosing between Left-to-Right, Top-to-Bottom, Right-to-Left and Bottom-to-Top, 23 | and setting an array of offsets as percent values to determine the gliding view steps. 24 | DESC 25 | 26 | s.homepage = 'https://github.com/gelemias/RDGliderViewController' 27 | s.screenshots = 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/1.png', 28 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/2.png', 29 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/3.png', 30 | 'https://raw.githubusercontent.com/gelemias/RDGliderViewController/develop/img/4.png' 31 | s.license = { :type => 'MIT', :file => 'LICENSE' } 32 | s.author = { 'Guillermo RD' => 'gelemias@gmail.com' } 33 | s.source = { :git => 'https://github.com/gelemias/RDGliderViewController.git', :tag => s.version.to_s } 34 | 35 | s.ios.deployment_target = '8.0' 36 | 37 | s.source_files = 'RDGliderViewController/**/*' 38 | 39 | end 40 | -------------------------------------------------------------------------------- /RDGliderViewController/RDGliderContentViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.h 3 | // 4 | // Created by Guillermo Delgado on 03/04/2017. 5 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface RDGliderContentViewController : UIViewController 11 | 12 | /** 13 | shows a shadow behind the view, default value is NO 14 | */ 15 | @property (nonatomic) BOOL showShadow; 16 | 17 | /** 18 | Sets a corner radius to the view, default value is 0.0f 19 | */ 20 | @property (nonatomic) CGFloat cornerRadius; 21 | 22 | 23 | /** 24 | Constructor to define a fixed length view. 25 | */ 26 | - (instancetype)initWithLength:(CGFloat)lenght; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /RDGliderViewController/RDGliderContentViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.m 3 | // 4 | // Created by Guillermo Delgado on 03/04/2017. 5 | // Copyright © 2017 Guillermo Delgado. All rights reserved. 6 | // 7 | 8 | #import "RDGliderContentViewController.h" 9 | 10 | @interface RDGliderContentViewController () 11 | 12 | @property (nonatomic) CGFloat lenght; 13 | 14 | @end 15 | 16 | @implementation RDGliderContentViewController 17 | 18 | - (instancetype)initWithLength:(CGFloat)lenght { 19 | if (self = [self init]) { 20 | _lenght = lenght; 21 | } 22 | 23 | return self; 24 | } 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | self.view.frame = CGRectMake(0, 0, self.lenght, self.lenght); 29 | 30 | [self setCornerRadius:0.0f]; 31 | [self setShowShadow:NO]; 32 | } 33 | 34 | - (void)setShowShadow:(BOOL)showShadow { 35 | if (showShadow) { 36 | self.view.layer.shadowColor = [UIColor blackColor].CGColor; 37 | self.view.layer.shadowOpacity = 0.5f; 38 | self.view.layer.shadowRadius = 5.0f; 39 | self.view.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); 40 | } 41 | } 42 | 43 | - (void)setCornerRadius:(CGFloat)cornerRadius { 44 | self.view.layer.cornerRadius = cornerRadius; 45 | [self.view.subviews firstObject].layer.cornerRadius = cornerRadius; 46 | 47 | } 48 | @end 49 | -------------------------------------------------------------------------------- /RDGliderViewController/RDGliderViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDGliderViewController.h 3 | // GliderSample 4 | // 5 | // Created by GuillermoD on 8/4/16. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RDScrollView.h" 11 | #import "RDGliderContentViewController.h" 12 | 13 | @class RDGliderViewController; 14 | 15 | @protocol RDGliderViewControllerDelegate 16 | 17 | @optional 18 | 19 | /** 20 | Delegate method to notify invoke object when offset has changed 21 | */ 22 | - (void)glideViewController:(nonnull RDGliderViewController *)glideViewController hasChangedOffsetOfContent:(CGPoint)offset; 23 | 24 | /** 25 | Delegate method to notify invoke object when glideView will increase offset by one 26 | */ 27 | - (void)glideViewControllerWillExpand:(nonnull RDGliderViewController *)glideViewController; 28 | 29 | /** 30 | Delegate method to notify invoke object when glideView will decrease offset by one 31 | */ 32 | - (void)glideViewControllerWillCollapse:(nonnull RDGliderViewController *)glideViewController; 33 | 34 | /** 35 | Delegate method to notify invoke object when glideView did increase offset by one 36 | */ 37 | - (void)glideViewControllerDidExpand:(nonnull RDGliderViewController *)glideViewController; 38 | 39 | /** 40 | Delegate method to notify invoke object when glideView did decrease offset by one 41 | */ 42 | - (void)glideViewControllerDidCollapse:(nonnull RDGliderViewController *)glideViewController; 43 | 44 | @end 45 | 46 | 47 | @interface RDGliderViewController : UIViewController 48 | 49 | @property (nonatomic, weak, nullable) id delegate; 50 | 51 | @property (nonatomic, readonly, nonnull) UIViewController *contentViewController; 52 | 53 | /** 54 | Margin of elastic animation default is 20px; 55 | */ 56 | @property (nonatomic) CGFloat marginOffset; 57 | 58 | /** 59 | Sorted list of offsets in % of contentVC view. from 0 to 1 60 | */ 61 | @property (nonatomic, copy, nonnull) NSArray *offsets; 62 | 63 | /** 64 | Orientation type of the glide view 65 | */ 66 | @property (nonatomic, readonly) RDScrollViewOrientationType orientationType; 67 | 68 | /** 69 | Current offset of the glide view 70 | */ 71 | @property (nonatomic, readonly) NSUInteger currentOffsetIndex; 72 | 73 | /** 74 | Returns a bool for determining if the glide view isn't closed, is different than offset % 0. 75 | */ 76 | @property (nonatomic, readonly) BOOL isOpen; 77 | 78 | /** 79 | Bool meant for enabling the posibility to close the glide view dragging, Default value is NO; 80 | */ 81 | @property (nonatomic) BOOL disableDraggingToClose; 82 | 83 | /** 84 | Change contentViewController type and offsets after the VC has been initialized. 85 | * @param contentViewController external ViewController placed as a content of the GlideView 86 | * @param type of GlideView Left to Right, Right to Left, Bottom To Top and Top to Bottom. 87 | * @param offsets Array of offsets in % (0 to 1) dependent of Content size if not expecified UIScreen */ 88 | - (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController 89 | type:(RDScrollViewOrientationType)type 90 | offsets:(nonnull NSArray *)offsets; 91 | 92 | /** 93 | This method gives a shake to the Gliver view, is meant to grap users atention. 94 | */ 95 | - (void)shake; 96 | 97 | /** 98 | Increase the position of the Gliver view by one in the list of offsets 99 | */ 100 | - (void)expand; 101 | 102 | /** 103 | Decrease the position of the Gliver view by one in the list of offsets 104 | */ 105 | - (void)collapse; 106 | 107 | /** 108 | Change offset of view. 109 | * @param offsetIndex setNew Offset of GlideView, parameter needs to be within offsets Array count list. 110 | * @param animated animates the offset change 111 | */ 112 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated; 113 | 114 | /** 115 | This method moves the View directly to the first offset which is the default position. 116 | */ 117 | - (void)close; 118 | 119 | /** 120 | Initializator of the object, it requires the parent view controller to build its components 121 | * @param parent Parent Class of this instance 122 | * @param content external ViewController placed as a content of the GlideView 123 | * @param type of GlideView Left to Right, Right to Left, Bottom To Top and Top to Bottom. 124 | * @param offsets Array of offsets in % (0 to 1) dependent of Content size if not expecified UIScreen bounds. 125 | * @return A newly created RDGliderViewController instance 126 | */ 127 | - (nullable instancetype)initOn:(nonnull UIViewController *)parent 128 | WithContent:(nonnull RDGliderContentViewController *)content 129 | type:(RDScrollViewOrientationType)type 130 | AndOffsets:(nonnull NSArray *)offsets; 131 | 132 | #pragma mark - Unavailable methods 133 | 134 | - (nullable instancetype)init NS_UNAVAILABLE; 135 | - (nullable instancetype)initWithCoder:(NSCoder *_Nullable)aDecoder NS_UNAVAILABLE; 136 | - (nonnull instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /RDGliderViewController/RDGliderViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDGliderViewController.m 3 | // GliderSample 4 | // 5 | // Created by GuillermoD on 8/4/16. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import "RDGliderViewController.h" 10 | 11 | @interface RDGliderViewController () 12 | 13 | @property (nonatomic, nonnull) RDScrollView *scrollView; 14 | @property (nonatomic, nonnull) UIViewController *contentViewController; 15 | @property (nonatomic) BOOL isObservingOffsets; 16 | 17 | @end 18 | 19 | @implementation RDGliderViewController 20 | 21 | - (instancetype)initOn:(nonnull UIViewController *)parent 22 | WithContent:(nonnull RDGliderContentViewController *)content 23 | type:(RDScrollViewOrientationType)type 24 | AndOffsets:(nonnull NSArray *)offsets { 25 | if (self = [super init]) { 26 | 27 | if (!parent ) { 28 | [NSException raise:@"Invalid parentViewController" format:@"parentViewController cannot be nil"]; 29 | } 30 | 31 | [parent addChildViewController:self]; 32 | 33 | _scrollView = [[RDScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(parent.view.frame), CGRectGetHeight(parent.view.frame))]; 34 | 35 | [parent.view addSubview:self.scrollView]; 36 | 37 | [self setContentViewController:content type:type offsets:offsets]; 38 | } 39 | 40 | return self; 41 | } 42 | 43 | - (NSArray *)offsets { 44 | if (!self.scrollView) { 45 | [NSException raise:@"Invalid request" format:@"RDGliderViewController have to instantiate first on a viewController"]; 46 | } 47 | 48 | return self.scrollView.offsets; 49 | 50 | } 51 | 52 | - (void)setOffsets:(NSArray *)offsets { 53 | if (offsets.count > 0) { 54 | if (self.scrollView) { 55 | [self.scrollView setOffsets:offsets]; 56 | } 57 | else { 58 | [NSException raise:@"Internal Inconsistency" format:@"RDGliderViewController requires an scrollview to be instanciated."]; 59 | } 60 | } 61 | else { 62 | [NSException raise:@"Invalid Offsets" format:@"Array of offsets cannot be Zero"]; 63 | } 64 | } 65 | 66 | - (NSUInteger)currentOffsetIndex { 67 | if (self.scrollView) { 68 | return self.scrollView.offsetIndex; 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | - (RDScrollViewOrientationType)orientationType { 75 | if (self.scrollView) { 76 | return self.scrollView.orientationType; 77 | } 78 | 79 | return RDScrollViewOrientationUnknown; 80 | } 81 | 82 | - (void)setMarginOffset:(CGFloat)marginOffset { 83 | [self.scrollView setMargin:marginOffset]; 84 | } 85 | 86 | - (CGFloat)marginOffset { 87 | if (!self.scrollView) { 88 | return 0.0f; 89 | } 90 | 91 | return [self.scrollView margin]; 92 | } 93 | 94 | - (NSUInteger)nearestOffsetIndexTo:(CGPoint)contentOffset { 95 | NSUInteger index = 0; 96 | CGFloat offset = contentOffset.x; 97 | CGFloat threshold = CGRectGetWidth(self.scrollView.content.frame); 98 | 99 | if (self.orientationType == RDScrollViewOrientationBottomToTop || 100 | self.orientationType == RDScrollViewOrientationTopToBottom) { 101 | offset = contentOffset.y; 102 | threshold = CGRectGetHeight(self.scrollView.content.frame); 103 | } 104 | 105 | NSUInteger distance = INT_MAX; 106 | for (NSUInteger i = 0 ; i < [self.offsets count] ; i++) { 107 | CGFloat transformedOffset = [[self.scrollView offsets] objectAtIndex:i].floatValue * threshold; 108 | NSUInteger distToAnchor = (NSUInteger)fabs(offset - transformedOffset); 109 | if (distToAnchor < distance) { 110 | distance = distToAnchor; 111 | index = i; 112 | } 113 | } 114 | 115 | return (index == 0 && self.disableDraggingToClose) ? 1 : index; 116 | } 117 | 118 | #pragma mark - Public methods 119 | 120 | - (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController 121 | type:(RDScrollViewOrientationType)type 122 | offsets:(nonnull NSArray *)offsets { 123 | 124 | if (!contentViewController) { 125 | [NSException raise:@"Invalid contentViewController" format:@"ViewController cannot be nil"]; 126 | } 127 | 128 | if (self.scrollView && !CGRectIsNull(self.scrollView.frame)) { 129 | 130 | self.parentViewController.automaticallyAdjustsScrollViewInsets = NO; 131 | self.automaticallyAdjustsScrollViewInsets = NO; 132 | 133 | [self.scrollView setOrientationType:type]; 134 | [self.scrollView setOffsets:offsets]; 135 | 136 | self.contentViewController = contentViewController; 137 | [self.scrollView setContent:self.contentViewController.view]; 138 | 139 | self.scrollView.delegate = self; 140 | 141 | [self close]; 142 | } 143 | } 144 | 145 | - (void)shake { 146 | 147 | if (!self.scrollView) { 148 | return; 149 | } 150 | 151 | CGFloat shakeMargin = 10.0f; 152 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 153 | [animation setDuration:0.05]; 154 | [animation setRepeatCount:2]; 155 | [animation setAutoreverses:YES]; 156 | [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; 157 | 158 | if (self.orientationType == RDScrollViewOrientationRightToLeft) { 159 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 160 | [self.scrollView center].y)]]; 161 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x + shakeMargin, 162 | [self.scrollView center].y)]]; 163 | } 164 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) { 165 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 166 | [self.scrollView center].y)]]; 167 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x - shakeMargin, 168 | [self.scrollView center].y)]]; 169 | } 170 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) { 171 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 172 | [self.scrollView center].y)]]; 173 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 174 | [self.scrollView center].y + shakeMargin)]]; 175 | } 176 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) { 177 | [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 178 | [self.scrollView center].y)]]; 179 | [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([self.scrollView center].x, 180 | [self.scrollView center].y - shakeMargin)]]; 181 | } 182 | 183 | [[self.scrollView layer] addAnimation:animation forKey:@"position"]; 184 | } 185 | 186 | - (void)expand { 187 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillExpand:)]) { 188 | [self.delegate glideViewControllerWillExpand:self]; 189 | } 190 | 191 | [self.scrollView expandWithCompletion:^(BOOL finished) { 192 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidExpand:)]) { 193 | [self.delegate glideViewControllerDidExpand:self]; 194 | } 195 | }]; 196 | } 197 | 198 | - (void)collapse { 199 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) { 200 | [self.delegate glideViewControllerWillCollapse:self]; 201 | } 202 | 203 | [self.scrollView collapseWithCompletion:^(BOOL finished) { 204 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) { 205 | [self.delegate glideViewControllerDidCollapse:self]; 206 | } 207 | }]; 208 | } 209 | 210 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated { 211 | if (self.currentOffsetIndex < offsetIndex) { 212 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillExpand:)]) { 213 | [self.delegate glideViewControllerWillExpand:self]; 214 | } 215 | } else if (offsetIndex < self.currentOffsetIndex) { 216 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) { 217 | [self.delegate glideViewControllerWillCollapse:self]; 218 | } 219 | 220 | } 221 | 222 | [self.scrollView changeOffsetTo:offsetIndex 223 | animated:animated 224 | completion:^(BOOL finished) { 225 | if (self.currentOffsetIndex < offsetIndex) { 226 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidExpand:)]) { 227 | [self.delegate glideViewControllerDidExpand:self]; 228 | } 229 | } else { 230 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) { 231 | [self.delegate glideViewControllerDidCollapse:self]; 232 | } 233 | } 234 | }]; 235 | } 236 | 237 | - (void)close { 238 | if ([self.delegate respondsToSelector:@selector(glideViewControllerWillCollapse:)]) { 239 | [self.delegate glideViewControllerWillCollapse:self]; 240 | } 241 | 242 | [self.scrollView closeWithCompletion:^(BOOL finished) { 243 | if ([self.delegate respondsToSelector:@selector(glideViewControllerDidCollapse:)]) { 244 | [self.delegate glideViewControllerDidCollapse:self]; 245 | } 246 | }]; 247 | } 248 | 249 | - (BOOL)isOpen { 250 | return self.scrollView.isOpen; 251 | } 252 | 253 | #pragma mark - UIScrollView delegate 254 | 255 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 256 | if ([self.delegate respondsToSelector:@selector(glideViewController:hasChangedOffsetOfContent:)]) { 257 | [self.delegate glideViewController:self hasChangedOffsetOfContent:scrollView.contentOffset]; 258 | } 259 | } 260 | 261 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 262 | [self changeOffsetTo:[self nearestOffsetIndexTo:scrollView.contentOffset] 263 | animated:NO]; 264 | } 265 | 266 | - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ 267 | [self.scrollView setContentOffset:self.scrollView.contentOffset animated:NO]; 268 | } 269 | 270 | #pragma mark - Rotation event 271 | 272 | - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { 273 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; 274 | if (self.contentViewController) { 275 | [self.contentViewController viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; 276 | } 277 | 278 | [coordinator animateAlongsideTransition:^(id _Nonnull context) { 279 | [self changeOffsetTo:self.currentOffsetIndex animated:YES]; 280 | } completion:^(id _Nonnull context) { 281 | [self.scrollView recalculateContentSize]; 282 | }]; 283 | } 284 | 285 | @end 286 | -------------------------------------------------------------------------------- /RDGliderViewController/RDScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDScrollView.h 3 | // GliderSample 4 | // 5 | // Created by GuillermoD on 8/3/16. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import 10 | typedef NS_ENUM(unsigned int, RDScrollViewOrientationType) { 11 | RDScrollViewOrientationUnknown = 0, 12 | 13 | RDScrollViewOrientationLeftToRight, 14 | RDScrollViewOrientationBottomToTop, 15 | RDScrollViewOrientationRightToLeft, 16 | RDScrollViewOrientationTopToBottom 17 | 18 | }; 19 | 20 | @interface RDScrollView : UIScrollView 21 | 22 | /** 23 | DraggableContainer 24 | */ 25 | @property (nonatomic) UIView *content; 26 | 27 | /** 28 | Orientation for draggable container. 29 | Default value : RDScrollViewOrientationLeftToRight 30 | */ 31 | @property (nonatomic) RDScrollViewOrientationType orientationType; 32 | 33 | /** 34 | Expandable offset in % of content view. from 0 to 1. 35 | */ 36 | @property (nonatomic, copy) NSArray *offsets; 37 | 38 | /** 39 | Determines whether the element's offset is different than % 0. 40 | */ 41 | @property (nonatomic, readonly) BOOL isOpen; 42 | 43 | /** 44 | Returns the position of open Offsets. 45 | */ 46 | @property (nonatomic, readonly) NSUInteger offsetIndex; 47 | 48 | /** 49 | Margin of elastic animation default is 20px. 50 | */ 51 | @property (nonatomic) CGFloat margin; 52 | 53 | /** 54 | Consider subviews of the content as part of the content, used when dragging. 55 | Default Value is False 56 | */ 57 | @property (nonatomic) BOOL selectContentSubViews; 58 | 59 | /** 60 | Duration of animation for changing offset, default vaule is 0.3 61 | */ 62 | @property (nonatomic) CGFloat duration; 63 | 64 | /** 65 | Delay of animation for changing offset, default vaule is 0.0 66 | */ 67 | @property (nonatomic) CGFloat delay; 68 | 69 | /** 70 | Damping of animation for changing offset, default vaule is 0.7 71 | */ 72 | @property (nonatomic) CGFloat damping; 73 | 74 | /** 75 | Damping of animation for changing offset, default vaule is 0.6 76 | */ 77 | @property (nonatomic) CGFloat velocity; 78 | 79 | /** 80 | Call this method to force recalculation of contentSize in ScrollView, i.e. when content changes. 81 | */ 82 | - (void)recalculateContentSize; 83 | 84 | // Methods to Increase or decrease offset of content within RDScrollView. 85 | 86 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; 87 | - (void)expandWithCompletion:(void (^)(BOOL finished))completion; 88 | - (void)collapseWithCompletion:(void (^)(BOOL finished))completion; 89 | - (void)closeWithCompletion:(void (^)(BOOL finished))completion; 90 | 91 | // Unavailable constructors. 92 | 93 | - (instancetype)init NS_UNAVAILABLE; 94 | - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /RDGliderViewController/RDScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDScrollView.m 3 | // GliderSample 4 | // 5 | // Created by GuillermoD on 8/3/16. 6 | // Copyright © 2017. All rights reserved. 7 | // 8 | 9 | #import "RDScrollView.h" 10 | 11 | @interface RDScrollView() 12 | 13 | @property (nonatomic) NSUInteger offsetIndex; 14 | @property (nonatomic) BOOL isOpen; 15 | @property (nonatomic) NSLayoutConstraint *leftToRightLeadingContraint; 16 | @property (nonatomic) NSLayoutConstraint *topToBottomTopContraint; 17 | 18 | @end 19 | 20 | #define kDefaultMargin 20 21 | 22 | #define kAniDuration 0.3 23 | #define kAniDelay 0.0 24 | #define kAniDamping 0.7 25 | #define kAniVelocity 0.6 26 | 27 | @implementation RDScrollView 28 | 29 | - (instancetype)initWithFrame:(CGRect)frame { 30 | if (self = [super initWithFrame:frame]) { 31 | [self initializeByDefault]; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | - (void)recalculateContentSize { 38 | 39 | CGSize size = CGSizeZero; 40 | 41 | if (self.orientationType == RDScrollViewOrientationBottomToTop) { 42 | size.height = CGRectGetHeight(self.frame) + (CGRectGetHeight(self.content.frame) * [self.offsets lastObject].floatValue) + self.margin; 43 | } 44 | else if (self.orientationType == RDScrollViewOrientationTopToBottom){ 45 | size.height = CGRectGetHeight(self.frame) + (CGRectGetHeight(self.content.frame) * [self.offsets firstObject].floatValue) + self.margin; 46 | } 47 | else if (self.orientationType == RDScrollViewOrientationRightToLeft) { 48 | size.width = CGRectGetWidth(self.frame) + (CGRectGetWidth(self.content.frame) * [self.offsets lastObject].floatValue) + self.margin; 49 | } 50 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) { 51 | size.width = CGRectGetWidth(self.frame) + (CGRectGetWidth(self.content.frame) * [self.offsets firstObject].floatValue) + self.margin; 52 | } 53 | 54 | [self setContentSize:size]; 55 | [self layoutIfNeeded]; 56 | 57 | dispatch_async (dispatch_get_main_queue (), ^{ 58 | [self changeOffsetTo:self.offsetIndex animated:NO completion:nil]; 59 | }); 60 | } 61 | 62 | - (void)setMargin:(CGFloat)margin { 63 | _margin = margin; 64 | 65 | if (self.orientationType == RDScrollViewOrientationTopToBottom) { 66 | self.topToBottomTopContraint.constant = margin; 67 | } 68 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) { 69 | self.leftToRightLeadingContraint.constant = margin; 70 | } 71 | 72 | [self recalculateContentSize]; 73 | } 74 | 75 | - (void)setContent:(UIView *)content { 76 | _content = content; 77 | [self addContent:_content]; 78 | } 79 | 80 | - (void)setOffsets:(NSArray *)offsets { 81 | if (!offsets) { 82 | [NSException raise:@"Invalid offsets array" format:@"offsets array cannot be nil"]; 83 | } 84 | 85 | NSArray *clearOffsets = [offsets valueForKeyPath:@"@distinctUnionOfObjects.self"]; 86 | NSMutableArray *reversedOffsets = [NSMutableArray new]; 87 | 88 | for (NSNumber *number in clearOffsets) { 89 | if ([number floatValue] > 1.0) { 90 | [NSException raise:@"Invalid offset value" format:@"offset represents a %% of contentView to be shown i.e. 0.5 of a contentView of 100px will show 50px"]; 91 | } 92 | else if (self.orientationType == RDScrollViewOrientationTopToBottom || 93 | self.orientationType == RDScrollViewOrientationLeftToRight) { 94 | [reversedOffsets addObject:@(1 - number.floatValue)]; 95 | } 96 | } 97 | 98 | NSArray *newOffsets = [clearOffsets sortedArrayUsingSelector: @selector(compare:)]; 99 | 100 | if (reversedOffsets.count > 0) { 101 | newOffsets = [reversedOffsets sortedArrayUsingSelector: @selector(compare:)]; 102 | newOffsets = [[newOffsets reverseObjectEnumerator] allObjects]; 103 | } 104 | 105 | if (newOffsets && ![_offsets isEqualToArray:newOffsets]) { 106 | [self recalculateContentSize]; 107 | } 108 | _offsets = [newOffsets copy]; 109 | 110 | } 111 | 112 | - (void)expandWithCompletion:(void (^)(BOOL finished))completion { 113 | NSUInteger nextIndex = (self.offsetIndex + 1 < [[self offsets] count]) ? self.offsetIndex + 1 : self.offsetIndex; 114 | [self changeOffsetTo:nextIndex animated:NO completion:completion]; 115 | } 116 | 117 | - (void)collapseWithCompletion:(void (^)(BOOL finished))completion { 118 | 119 | NSUInteger nextIndex = self.offsetIndex == 0 ? 0 : self.offsetIndex - 1; 120 | [self changeOffsetTo:nextIndex animated:NO completion:completion]; 121 | } 122 | 123 | - (void)closeWithCompletion:(void (^)(BOOL finished))completion { 124 | [self changeOffsetTo:0 animated:NO completion:completion]; 125 | } 126 | 127 | - (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { 128 | self.panGestureRecognizer.enabled = NO; 129 | [UIView animateWithDuration:self.duration delay:self.delay usingSpringWithDamping:self.damping 130 | initialSpringVelocity:self.velocity options:UIViewAnimationOptionCurveEaseOut animations:^{ 131 | 132 | [self.content setHidden:NO]; 133 | 134 | if (self.orientationType == RDScrollViewOrientationLeftToRight) { 135 | CGFloat margin = (offsetIndex == 0 || offsetIndex == self.offsets.count - 1) ? self.margin : 0; 136 | [self setContentOffset:CGPointMake(([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetWidth(self.content.frame)) + margin, self.contentOffset.y) animated:animated]; 137 | } 138 | else if (self.orientationType == RDScrollViewOrientationRightToLeft) { 139 | [self setContentOffset:CGPointMake([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetWidth(self.content.frame), self.contentOffset.y) animated:animated]; 140 | } 141 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) { 142 | [self setContentOffset:CGPointMake(self.contentOffset.x, [[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetHeight(self.content.frame)) animated:animated]; 143 | } 144 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) { 145 | CGFloat margin = (offsetIndex == 0 || offsetIndex == self.offsets.count - 1) ? self.margin : 0; 146 | [self setContentOffset:CGPointMake(self.contentOffset.x, ([[self offsets] objectAtIndex:offsetIndex].floatValue * CGRectGetHeight(self.content.frame)) + margin) animated:animated]; 147 | } 148 | } completion:^(BOOL finished) { 149 | self.offsetIndex = offsetIndex; 150 | 151 | if (self.orientationType == RDScrollViewOrientationLeftToRight || 152 | self.orientationType == RDScrollViewOrientationTopToBottom) { 153 | self.isOpen = ([[self offsets] objectAtIndex:offsetIndex].floatValue == 1) ? NO : YES; 154 | } else { 155 | self.isOpen = ([[self offsets] objectAtIndex:offsetIndex].floatValue == 0) ? NO : YES; 156 | } 157 | 158 | [self.content setHidden:!self.isOpen]; 159 | 160 | self.panGestureRecognizer.enabled = YES; 161 | 162 | if (completion) { 163 | completion(finished); 164 | } 165 | }]; 166 | } 167 | 168 | #pragma mark - Private Methods 169 | 170 | - (BOOL)viewContainsPoint:(CGPoint)point inView:(UIView *)content { 171 | if (CGRectContainsPoint(_content.frame, point)) { 172 | return YES; 173 | } 174 | 175 | if (self.selectContentSubViews) { 176 | for (UIView *subView in content.subviews) { 177 | if (CGRectContainsPoint(subView.frame, point)) { 178 | return YES; 179 | } 180 | } 181 | } 182 | 183 | return NO; 184 | } 185 | 186 | - (void)initializeByDefault { 187 | self.margin = (CGFloat)kDefaultMargin; 188 | 189 | self.duration = (CGFloat)kAniDuration; 190 | self.delay = (CGFloat)kAniDelay; 191 | self.damping = (CGFloat)kAniDamping; 192 | self.velocity = (CGFloat)kAniVelocity; 193 | 194 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 195 | 196 | self.showsVerticalScrollIndicator = NO; 197 | self.showsHorizontalScrollIndicator = NO; 198 | 199 | [self setBounces:NO]; 200 | [self setDirectionalLockEnabled:YES]; 201 | [self setScrollsToTop:NO]; 202 | [self setPagingEnabled:NO]; 203 | [self setContentInset:UIEdgeInsetsZero]; 204 | [self setDecelerationRate:UIScrollViewDecelerationRateFast]; 205 | 206 | [self setOrientationType:RDScrollViewOrientationRightToLeft]; 207 | } 208 | 209 | - (void)addContent:(UIView *)content { 210 | if (!content && CGRectIsNull(content.frame)) { 211 | return; 212 | } 213 | 214 | [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; 215 | 216 | UIView *container = [UIView new]; 217 | 218 | [container addSubview:content]; 219 | [self addSubview:container]; 220 | 221 | container.translatesAutoresizingMaskIntoConstraints = NO; 222 | content.translatesAutoresizingMaskIntoConstraints = NO; 223 | 224 | if (self.orientationType == RDScrollViewOrientationRightToLeft) { 225 | 226 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 227 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 228 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual 229 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0], 230 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual 231 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0]]]; 232 | 233 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 234 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 235 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 236 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 237 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 238 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]]]; 239 | if (CGRectIsEmpty(_content.frame)) { 240 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 241 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0], 242 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 243 | toItem:self attribute:NSLayoutAttributeWidth multiplier:2.0 constant:0.0]]]; 244 | } else { 245 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 246 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetWidth(content.frame)]]; 247 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 248 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:CGRectGetWidth(content.frame)]]; 249 | } 250 | } 251 | else if (self.orientationType == RDScrollViewOrientationLeftToRight) { 252 | 253 | self.leftToRightLeadingContraint = [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 254 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:self.margin]; 255 | 256 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 257 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 258 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual 259 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0], self.leftToRightLeadingContraint]]; 260 | 261 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 262 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 263 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 264 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 265 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 266 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]]]; 267 | if (CGRectIsEmpty(_content.frame)) { 268 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 269 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0], 270 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 271 | toItem:self attribute:NSLayoutAttributeWidth multiplier:2.0 constant:0.0]]]; 272 | } else { 273 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 274 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetWidth(content.frame)]]; 275 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 276 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:CGRectGetWidth(content.frame)]]; 277 | } 278 | } 279 | else if (self.orientationType == RDScrollViewOrientationBottomToTop) { 280 | 281 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 282 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 283 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual 284 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], 285 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual 286 | toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]]]; 287 | 288 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 289 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 290 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 291 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 292 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 293 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]]]; 294 | 295 | if (CGRectIsEmpty(_content.frame)) { 296 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 297 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0], 298 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 299 | toItem:self attribute:NSLayoutAttributeHeight multiplier:2.0 constant:0.0]]]; 300 | } else { 301 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 302 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetHeight(content.frame)]]; 303 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 304 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:CGRectGetHeight(content.frame)]]; 305 | } 306 | } 307 | else if (self.orientationType == RDScrollViewOrientationTopToBottom) { 308 | 309 | self.topToBottomTopContraint = [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 310 | toItem:container attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.margin]; 311 | 312 | [container addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 313 | toItem:container attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 314 | [NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual 315 | toItem:container attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], self.topToBottomTopContraint]]; 316 | 317 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual 318 | toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], 319 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual 320 | toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], 321 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual 322 | toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]]]; 323 | 324 | if (CGRectIsEmpty(_content.frame)) { 325 | [self addConstraints:@[[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 326 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0], 327 | [NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 328 | toItem:self attribute:NSLayoutAttributeHeight multiplier:2.0 constant:0.0]]]; 329 | } else { 330 | [container addConstraint:[NSLayoutConstraint constraintWithItem:content attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 331 | toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:CGRectGetHeight(content.frame)]]; 332 | [self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual 333 | toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:CGRectGetHeight(content.frame)]]; 334 | } 335 | } 336 | 337 | [self layoutIfNeeded]; 338 | [self recalculateContentSize]; 339 | } 340 | 341 | #pragma mark - touch handler 342 | 343 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 344 | if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) { 345 | return nil; 346 | } 347 | 348 | if (_content && [self viewContainsPoint:point inView:_content]) { 349 | 350 | for (UIView *subview in [self.subviews reverseObjectEnumerator]) { 351 | 352 | CGPoint pt = CGPointMake((CGFloat)fabs(point.x), (CGFloat)fabs(point.y)); 353 | 354 | CGPoint convertedPoint = [subview convertPoint:pt fromView:self]; 355 | UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event]; 356 | 357 | if (hitTestView) { 358 | return hitTestView; 359 | } 360 | } 361 | } 362 | 363 | return nil; 364 | } 365 | 366 | @end 367 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🛫 RDGliderViewController 🛬 2 | 3 | [![Build Status](https://travis-ci.org/gelemias/RDGliderViewController.svg?branch=develop)](https://travis-ci.org/gelemias/RDGliderViewController) [![Coverage Status](https://coveralls.io/repos/github/gelemias/RDGliderViewController/badge.svg?branch=develop)](https://coveralls.io/github/gelemias/RDGliderViewController?branch=develop) [![License: MIT](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | 5 | 6 | Control in *Objective-C* for a draggable ViewController gliding over another ViewController. 7 | 8 | **⚠️⚠️ This project is discontinued, use [RDGliderViewController-Swift](https://github.com/gelemias/RDGliderViewController-Swift) instead ⚠️⚠️** 9 | 10 | RDGliderViewController is a view controller that manages a scrollable view using one side as a sliding point, choosing between Left-to-Right, Top-to-Bottom, Right-to-Left and Bottom-to-Top, and setting an array of offsets as percent values to determine the gliding view steps, as described bellow. 11 | 12 | 13 | 14 | ## Example 15 | 16 | A good way to learn how to use RDGliderViewController is to go through the example app. Take a look at the tests as well for a more thorough usage. 17 | 18 | To run the example project, clone the repo, and open `./Example/GliderSample.xcodeproj` 19 | 20 | ## Installation 21 | 22 | ### Using CocoaPods 23 | RDGliderViewController is available through [CocoaPods](http://cocoapods.org), a dependency manager for Swift and Objective-C Cocoa projects. To install 24 | it, simply add the following line to your [Podfile](https://guides.cocoapods.org/using/getting-started.html): 25 | 26 | ```ruby 27 | pod 'RDGliderViewController' 28 | ``` 29 | 30 | ### Using Carthage 31 | 32 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 33 | 34 | You can install Carthage with [Homebrew](http://brew.sh/) and then integrate **RDGliderViewController** into your Xcode project by adding the following line to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile): 35 | 36 | ```ruby 37 | github "gelemias/RDGliderViewController" 38 | ``` 39 | 40 | ### Manual Installation 41 | 42 | For an old fashion installation you can directly add the **header** and **implementation** files for `RDGliderViewController` and `RDScrollView` to your project. 43 | 44 | - Download the latest code version or add the repository as a git submodule to your git-tracked project. 45 | 46 | - Drag and drop the folder `RDGliderViewController/` onto your project and make sure to select **Copy items if needed**. 47 | 48 | - Add `#import "RDGliderViewController.h"` at the beginning of your class. 49 | 50 | ## Usage 51 | 52 | RDGliderViewController is a very simple to use controller that mainly depends on three values to work on, *Orientation type* which is determines the scrolling side, the *List of Offsets* which represent the steps of expansion and contraction and the content itself with is a view controller completely independent. 53 | 54 | ### Creation 55 | 56 | Use a the following custom init to instantiate RDGliderViewController: 57 | 58 | `- (instancetype)initOn:(nonnull UIViewController *)parent WithContent:(nonnull RDGliderContentViewController *)content type:(RDScrollViewOrientationType)type AndOffsets:(nonnull NSArray *)offsets { 59 | ` 60 | 61 | e.g. 62 | ```Objective-C 63 | RDGliderViewController *gliderVC = [[RDGliderViewController alloc] initOn:self 64 | WithContent:[UIViewController new] 65 | type:RDScrollViewOrientationRightToLeft 66 | AndOffsets:@[@0.2, @0.5, @1]]; 67 | ``` 68 | 69 | And that's all you need to do, if you run your app, an instance of UIViewController should be scrollable from Right to Left on top of `self` view controller. 70 | 71 | ### Content and Container 72 | 73 | The content view controller should inherit from RDGliderContentViewController to inherit the properly resize when rotating, is treated as a child view controller of this one. 74 | 75 | ```Objective-C 76 | #import "RDGliderContentViewController.h" 77 | 78 | @interface ContentViewController : RDGliderContentViewController 79 | 80 | // Your content class Here 81 | 82 | @end 83 | 84 | ``` 85 | 86 | Mentioned that not much more is required, only keep in mind that if you do not define a size for the view, RDGliderViewController will it resize it to match container's. 87 | 88 | Content view controller can be afterwards as well but always along offsets and orientation since these three properties are dependent of each other: 89 | 90 | `- (void)setContentViewController:(nonnull RDGliderContentViewController *)contentViewController 91 | type:(RDScrollViewOrientationType)type 92 | offsets:(nonnull NSArray *)offsets; 93 | ` 94 | 95 | ### Orientation types 96 | 97 | `RDScrollViewOrientationType` represent the four sides of the display plus the sliding direction. 98 | 99 | ```Objective-C 100 | typedef NS_ENUM(unsigned int, RDScrollViewOrientationType) { 101 | RDScrollViewOrientationUnknown = 0, 102 | 103 | RDScrollViewOrientationLeftToRight, 104 | RDScrollViewOrientationBottomToTop, 105 | RDScrollViewOrientationRightToLeft, 106 | RDScrollViewOrientationTopToBottom 107 | 108 | }; 109 | ``` 110 | 111 | if content view controller defines a fixed size, this size will be respected during rotation, for horizontal scrolls (Left-to-Right and Right-to-Left) will resize *Width* and for Vertical (Bottom-to-Top and Top-to-Bottom) will reize *Height* 112 | 113 | 114 | ### Offsets and OffsetIndex 115 | 116 | this `NSArray *offsets` property is a list of steps used when either expanding / collapsing or dragging the list is represented in percent % related with the length of the content view controller, e.g. given the list [0, 0.5, 1] for a lenght *400px* would be this offset steps of [0px, 200px, 400px] 117 | 118 | `offsets[OffsetIndex].floatValue * contentViewController.lenght` 119 | 120 | For inverted OrientationTypes (RDScrollViewOrientationLeftToRight and RDScrollViewOrientationTopToBottom), the offset list are also inverted so for example for a list [0.2, 0.4, 0.6, 1] will be returned [0.8, 0.6, 0.4, 0] 121 | 122 | ### Expand, Collapse and Close 123 | 124 | By default RDGliderViewController is draggable but when setting an offset of `0.0f%` it will be outside of the screen on starting point, to Increase the position of the view by one in the list of offsets use: 125 | 126 | `- (void)expand;` 127 | 128 | to Decrease the position of RDGliderViewController by one in the list of offsets: 129 | 130 | `- (void)collapse;` 131 | 132 | and *close* which moves the View directly to the first offset which is the default position: 133 | 134 | `- (void)close;` 135 | 136 | All of these methods simple change the offsetIndex when calling this method. 137 | 138 | `- (void)changeOffsetTo:(NSUInteger)offsetIndex animated:(BOOL)animated;` 139 | 140 | ## License 141 | 142 | RDGliderViewController is released under the MIT license. See [LICENSE](https://github.com/gelemias/RDGliderViewController/blob/develop/LICENSE) for details. 143 | 144 | 145 | ## Change log 146 | 147 | See [CHANGELOG](https://github.com/gelemias/RDGliderViewController/blob/develop/CHANGELOG.md) for details. 148 | -------------------------------------------------------------------------------- /img/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/1.gif -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/1.png -------------------------------------------------------------------------------- /img/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/2.gif -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/2.png -------------------------------------------------------------------------------- /img/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/3.gif -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/3.png -------------------------------------------------------------------------------- /img/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/4.gif -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gelemias/RDGliderViewController/26da9019f59467adab6192492077a6806176198c/img/4.png --------------------------------------------------------------------------------