├── .gitignore ├── AUTHORS.md ├── LICENSE ├── README.md ├── WKVerticalScrollBar.podspec ├── WKVerticalScrollBar.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── WKVerticalScrollBar ├── WKAccessoryView.h ├── WKAccessoryView.m ├── WKAppDelegate.h ├── WKAppDelegate.m ├── WKTextDemoView.h ├── WKTextDemoView.m ├── WKTextDemoViewController.h ├── WKTextDemoViewController.m ├── WKVerticalScrollBar-Info.plist ├── WKVerticalScrollBar-Prefix.pch ├── WKVerticalScrollBar.h ├── WKVerticalScrollBar.m ├── en.lproj │ └── InfoPlist.strings ├── fillerati.txt └── main.m └── images ├── demo-01.png └── demo-02.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/xcuserdata/* 2 | *.xcodeproj/project.xcworkspace/xcuserdata/* 3 | *.xcworkspace/xcuserdata/* 4 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Developed by litl 2 | 3 | This project was conceived of with the help of the full software team at litl. 4 | 5 | ## litl iOS Team 6 | 7 | * [Brad Taylor](https://github.com/btaylor) (maintainer, initial developer) 8 | * [Ash Atkins](https://github.com/aatkins) 9 | * [Clay Bridges](https://github.com/cbridges) 10 | * [Brian Connatser](https://github.com/connatser) 11 | * [Walter Hatcher](https://github.com/whatcher) 12 | * [Erik Hunter](http://github.com/ehunter) 13 | 14 | ## Patches and Suggestions 15 | 16 | Add yourself as a contributor! 17 | 18 | * (your name here) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 litl, LLC 2 | Copyright (c) 2012 WKVerticalScrollBar authors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WKVerticalScrollBar 2 | 3 | ![WKVerticalScrollBar in repose](https://raw.github.com/litl/WKVerticalScrollBar/master/images/demo-02.png) 4 | ![WKVerticalScrollBar in action with optional accessory view](https://raw.github.com/litl/WKVerticalScrollBar/master/images/demo-01.png) 5 | 6 | A traditional-style scrollbar for iOS that integrates with existing UIScrollView or UIScrollView subclasses. 7 | 8 | `WKScrollBar` draws a persistent scrollbar handle on top of a UIScrollView. When tapped and dragged, `WKScrollBar` will scroll proportionately to its position on the screen. This comes in handy with apps which present long lists of items (`UITextView`, `UITableView`, `AQGridView`, etc), as it makes it easy to navigate to any point in a list. 9 | 10 | ## Installing 11 | ### CocoaPods 12 | Installing via [cocoapods](http://cocoapods.org) is the preferred method of using `WKVerticalScrollBar`. Simply add the following line to your `Podfile`: 13 | 14 | dependency 'WKVerticalScrollBar', '0.2.0' 15 | 16 | ### Manually 17 | Copy both `WKVerticalScrollBar.h` and `WKVerticalScrollBar.m` into your project. Make sure you've linked your project with `QuartzCore.framework`. 18 | 19 | ## Usage 20 | `WKVerticalScrollBar` is meant to integrate quickly with projects using `UIScrollView` or `UIScrollView` subclasses like `AQGridView`. Getting started is easy: 21 | 22 | 1. Create a `WKVerticalScrollBar` instance either in IB or in your `-init` method. 23 | 2. Add the `WKVerticalScrollBar` to the parent `UIView`, making sure that it is the frontmost `UIView` either by adding it last, or via `-bringSubviewToFront:`. 24 | 3. Size the `WKVerticalScrollBar` so that it takes up the same area as the `UIScrollView` that it will manage. 25 | 4. Tell `WKVerticalScrollBar` which `UIScrollView` it will manage via `-setScrollView:`. 26 | 27 | ## Appearance 28 | Modifying the look and feel of `WKScrollBar` can be done via the following methods: 29 | 30 | * `-setHandleColor:forState:` 31 | 32 | Sets a color for `UIControlStateNormal` and `UIControlStateSelected` to control the normal and selected (finger down) colors of the handle. 33 | 34 | Defaults: `UIControlStateNormal`: 40% black, `UIControlStateSelected`: 60% black 35 | 36 | * `-setHandleWidth:` 37 | 38 | Sets the width of the handle in the normal state. 39 | 40 | Default: 5pt. 41 | 42 | * `-setHandleSelectedWidth:` 43 | 44 | Sets the width of the handle when selected. This allows you to grow the handle when the user's finger is over the handle. 45 | 46 | Default: 15pt. 47 | 48 | * `-setHandleHitArea:` 49 | 50 | Sets the width of the hit area for the handle. This will allow your control to have a slightly larger hit area than what is visually presented. Apple's iOS Human Interface Guidelines suggest that this be 44pt. 51 | 52 | Default: 44pt. 53 | 54 | * `-setHandleMinimumHeight:` 55 | 56 | Sets the minimum height of the handle. 57 | 58 | The height of the handle is calculated based upon the ratio of the `contentOffset` and the `frame`. If the `contentOffset` is too large, the handle may be too small to touch comfortably. Use this parameter to ensure that a minimum handle size is preserved. 59 | 60 | * `-setHandleCornerRadius:` and `-setHandleSelectedCornerRadius:` 61 | 62 | Sets the corner radius of the handle in normal and selected mode. 63 | 64 | ## Contributing 65 | Anyone who would like to contribute to the project is more than welcome. 66 | Basically, there's just a few steps to getting started: 67 | 68 | 1. Fork this repo 69 | 2. Make your changes 70 | 3. Add yourself to the AUTHORS file and submit a pull request! 71 | 72 | ## Copyright and License 73 | WKVerticalScrollBar is Copyright (c) 2012 litl, LLC and licensed under the MIT license. See the LICENSE file for full details. 74 | -------------------------------------------------------------------------------- /WKVerticalScrollBar.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'WKVerticalScrollBar' 3 | s.version = '0.3.2' 4 | s.license = 'MIT' 5 | s.platform = :ios 6 | 7 | s.summary = 'A traditional-style scrollbar for iOS that integrates with existing UIScrollView or UIScrollView subclasses.' 8 | s.homepage = 'https://github.com/litl/WKVerticalScrollBar' 9 | s.author = { 'Brad Taylor' => 'btaylor@litl.com' } 10 | s.source = { :git => 'https://github.com/litl/WKVerticalScrollBar.git' } 11 | 12 | s.source_files = 'WKVerticalScrollBar/WKVerticalScrollBar.{h,m}' 13 | 14 | s.frameworks = 'QuartzCore' 15 | end 16 | -------------------------------------------------------------------------------- /WKVerticalScrollBar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4B47E39B159B93B300D73B5D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B47E39A159B93B300D73B5D /* UIKit.framework */; }; 11 | 4B47E39D159B93B300D73B5D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B47E39C159B93B300D73B5D /* Foundation.framework */; }; 12 | 4B47E39F159B93B300D73B5D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B47E39E159B93B300D73B5D /* CoreGraphics.framework */; }; 13 | 4B47E3A5159B93B300D73B5D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4B47E3A3159B93B300D73B5D /* InfoPlist.strings */; }; 14 | 4B47E3A7159B93B300D73B5D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47E3A6159B93B300D73B5D /* main.m */; }; 15 | 4B47E3AB159B93B300D73B5D /* WKAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47E3AA159B93B300D73B5D /* WKAppDelegate.m */; }; 16 | 4B47E3B3159B965100D73B5D /* WKVerticalScrollBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47E3B2159B965100D73B5D /* WKVerticalScrollBar.m */; }; 17 | 4B47E3B5159B98AD00D73B5D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B47E3B4159B98AD00D73B5D /* QuartzCore.framework */; }; 18 | 4B47E3B8159B98E900D73B5D /* WKTextDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47E3B7159B98E900D73B5D /* WKTextDemoViewController.m */; }; 19 | 4B47E3BB159B99BE00D73B5D /* WKTextDemoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47E3BA159B99BE00D73B5D /* WKTextDemoView.m */; }; 20 | 4B47E3BD159B9DF600D73B5D /* fillerati.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4B47E3BC159B9DF600D73B5D /* fillerati.txt */; }; 21 | 4B5D137E159D329600EB3283 /* WKAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D137D159D329600EB3283 /* WKAccessoryView.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 4B47E396159B93B300D73B5D /* WKVerticalScrollBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WKVerticalScrollBar.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 4B47E39A159B93B300D73B5D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 27 | 4B47E39C159B93B300D73B5D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 28 | 4B47E39E159B93B300D73B5D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 29 | 4B47E3A2159B93B300D73B5D /* WKVerticalScrollBar-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "WKVerticalScrollBar-Info.plist"; sourceTree = ""; }; 30 | 4B47E3A4159B93B300D73B5D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 31 | 4B47E3A6159B93B300D73B5D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 4B47E3A8159B93B300D73B5D /* WKVerticalScrollBar-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WKVerticalScrollBar-Prefix.pch"; sourceTree = ""; }; 33 | 4B47E3A9159B93B300D73B5D /* WKAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKAppDelegate.h; sourceTree = ""; }; 34 | 4B47E3AA159B93B300D73B5D /* WKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WKAppDelegate.m; sourceTree = ""; }; 35 | 4B47E3B1159B965100D73B5D /* WKVerticalScrollBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKVerticalScrollBar.h; sourceTree = ""; }; 36 | 4B47E3B2159B965100D73B5D /* WKVerticalScrollBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKVerticalScrollBar.m; sourceTree = ""; }; 37 | 4B47E3B4159B98AD00D73B5D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 38 | 4B47E3B6159B98E900D73B5D /* WKTextDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKTextDemoViewController.h; sourceTree = ""; }; 39 | 4B47E3B7159B98E900D73B5D /* WKTextDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKTextDemoViewController.m; sourceTree = ""; }; 40 | 4B47E3B9159B99BE00D73B5D /* WKTextDemoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKTextDemoView.h; sourceTree = ""; }; 41 | 4B47E3BA159B99BE00D73B5D /* WKTextDemoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKTextDemoView.m; sourceTree = ""; }; 42 | 4B47E3BC159B9DF600D73B5D /* fillerati.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = fillerati.txt; sourceTree = ""; }; 43 | 4B5D137C159D329600EB3283 /* WKAccessoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKAccessoryView.h; sourceTree = ""; }; 44 | 4B5D137D159D329600EB3283 /* WKAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKAccessoryView.m; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 4B47E393159B93B300D73B5D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | 4B47E3B5159B98AD00D73B5D /* QuartzCore.framework in Frameworks */, 53 | 4B47E39B159B93B300D73B5D /* UIKit.framework in Frameworks */, 54 | 4B47E39D159B93B300D73B5D /* Foundation.framework in Frameworks */, 55 | 4B47E39F159B93B300D73B5D /* CoreGraphics.framework in Frameworks */, 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 4B47E38B159B93B300D73B5D = { 63 | isa = PBXGroup; 64 | children = ( 65 | 4B47E3A0159B93B300D73B5D /* WKVerticalScrollBar */, 66 | 4B47E399159B93B300D73B5D /* Frameworks */, 67 | 4B47E397159B93B300D73B5D /* Products */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 4B47E397159B93B300D73B5D /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 4B47E396159B93B300D73B5D /* WKVerticalScrollBar.app */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 4B47E399159B93B300D73B5D /* Frameworks */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 4B47E3B4159B98AD00D73B5D /* QuartzCore.framework */, 83 | 4B47E39A159B93B300D73B5D /* UIKit.framework */, 84 | 4B47E39C159B93B300D73B5D /* Foundation.framework */, 85 | 4B47E39E159B93B300D73B5D /* CoreGraphics.framework */, 86 | ); 87 | name = Frameworks; 88 | sourceTree = ""; 89 | }; 90 | 4B47E3A0159B93B300D73B5D /* WKVerticalScrollBar */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 4B47E3BF159BAAF800D73B5D /* Demo */, 94 | 4B47E3B1159B965100D73B5D /* WKVerticalScrollBar.h */, 95 | 4B47E3B2159B965100D73B5D /* WKVerticalScrollBar.m */, 96 | 4B47E3A1159B93B300D73B5D /* Supporting Files */, 97 | ); 98 | path = WKVerticalScrollBar; 99 | sourceTree = ""; 100 | }; 101 | 4B47E3A1159B93B300D73B5D /* Supporting Files */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 4B47E3BC159B9DF600D73B5D /* fillerati.txt */, 105 | 4B47E3A2159B93B300D73B5D /* WKVerticalScrollBar-Info.plist */, 106 | 4B47E3A3159B93B300D73B5D /* InfoPlist.strings */, 107 | 4B47E3A6159B93B300D73B5D /* main.m */, 108 | 4B47E3A8159B93B300D73B5D /* WKVerticalScrollBar-Prefix.pch */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | 4B47E3BF159BAAF800D73B5D /* Demo */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 4B47E3A9159B93B300D73B5D /* WKAppDelegate.h */, 117 | 4B47E3AA159B93B300D73B5D /* WKAppDelegate.m */, 118 | 4B5D137C159D329600EB3283 /* WKAccessoryView.h */, 119 | 4B5D137D159D329600EB3283 /* WKAccessoryView.m */, 120 | 4B47E3B9159B99BE00D73B5D /* WKTextDemoView.h */, 121 | 4B47E3BA159B99BE00D73B5D /* WKTextDemoView.m */, 122 | 4B47E3B6159B98E900D73B5D /* WKTextDemoViewController.h */, 123 | 4B47E3B7159B98E900D73B5D /* WKTextDemoViewController.m */, 124 | ); 125 | name = Demo; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 4B47E395159B93B300D73B5D /* WKVerticalScrollBar */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 4B47E3AE159B93B300D73B5D /* Build configuration list for PBXNativeTarget "WKVerticalScrollBar" */; 134 | buildPhases = ( 135 | 4B47E392159B93B300D73B5D /* Sources */, 136 | 4B47E393159B93B300D73B5D /* Frameworks */, 137 | 4B47E394159B93B300D73B5D /* Resources */, 138 | ); 139 | buildRules = ( 140 | ); 141 | dependencies = ( 142 | ); 143 | name = WKVerticalScrollBar; 144 | productName = WKVerticalScrollBar; 145 | productReference = 4B47E396159B93B300D73B5D /* WKVerticalScrollBar.app */; 146 | productType = "com.apple.product-type.application"; 147 | }; 148 | /* End PBXNativeTarget section */ 149 | 150 | /* Begin PBXProject section */ 151 | 4B47E38D159B93B300D73B5D /* Project object */ = { 152 | isa = PBXProject; 153 | attributes = { 154 | CLASSPREFIX = WK; 155 | LastUpgradeCheck = 0450; 156 | ORGANIZATIONNAME = "litl, LLC"; 157 | }; 158 | buildConfigurationList = 4B47E390159B93B300D73B5D /* Build configuration list for PBXProject "WKVerticalScrollBar" */; 159 | compatibilityVersion = "Xcode 3.2"; 160 | developmentRegion = English; 161 | hasScannedForEncodings = 0; 162 | knownRegions = ( 163 | en, 164 | ); 165 | mainGroup = 4B47E38B159B93B300D73B5D; 166 | productRefGroup = 4B47E397159B93B300D73B5D /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | 4B47E395159B93B300D73B5D /* WKVerticalScrollBar */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | 4B47E394159B93B300D73B5D /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 4B47E3A5159B93B300D73B5D /* InfoPlist.strings in Resources */, 181 | 4B47E3BD159B9DF600D73B5D /* fillerati.txt in Resources */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXResourcesBuildPhase section */ 186 | 187 | /* Begin PBXSourcesBuildPhase section */ 188 | 4B47E392159B93B300D73B5D /* Sources */ = { 189 | isa = PBXSourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 4B47E3A7159B93B300D73B5D /* main.m in Sources */, 193 | 4B47E3AB159B93B300D73B5D /* WKAppDelegate.m in Sources */, 194 | 4B47E3B3159B965100D73B5D /* WKVerticalScrollBar.m in Sources */, 195 | 4B47E3B8159B98E900D73B5D /* WKTextDemoViewController.m in Sources */, 196 | 4B47E3BB159B99BE00D73B5D /* WKTextDemoView.m in Sources */, 197 | 4B5D137E159D329600EB3283 /* WKAccessoryView.m in Sources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXSourcesBuildPhase section */ 202 | 203 | /* Begin PBXVariantGroup section */ 204 | 4B47E3A3159B93B300D73B5D /* InfoPlist.strings */ = { 205 | isa = PBXVariantGroup; 206 | children = ( 207 | 4B47E3A4159B93B300D73B5D /* en */, 208 | ); 209 | name = InfoPlist.strings; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXVariantGroup section */ 213 | 214 | /* Begin XCBuildConfiguration section */ 215 | 4B47E3AC159B93B300D73B5D /* Debug */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | ALWAYS_SEARCH_USER_PATHS = NO; 219 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 222 | COPY_PHASE_STRIP = NO; 223 | GCC_C_LANGUAGE_STANDARD = gnu99; 224 | GCC_DYNAMIC_NO_PIC = NO; 225 | GCC_OPTIMIZATION_LEVEL = 0; 226 | GCC_PREPROCESSOR_DEFINITIONS = ( 227 | "DEBUG=1", 228 | "$(inherited)", 229 | ); 230 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 235 | SDKROOT = iphoneos; 236 | TARGETED_DEVICE_FAMILY = "1,2"; 237 | }; 238 | name = Debug; 239 | }; 240 | 4B47E3AD159B93B300D73B5D /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 245 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 246 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 247 | COPY_PHASE_STRIP = YES; 248 | GCC_C_LANGUAGE_STANDARD = gnu99; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 251 | GCC_WARN_UNUSED_VARIABLE = YES; 252 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 253 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 254 | SDKROOT = iphoneos; 255 | TARGETED_DEVICE_FAMILY = "1,2"; 256 | VALIDATE_PRODUCT = YES; 257 | }; 258 | name = Release; 259 | }; 260 | 4B47E3AF159B93B300D73B5D /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 264 | GCC_PREFIX_HEADER = "WKVerticalScrollBar/WKVerticalScrollBar-Prefix.pch"; 265 | INFOPLIST_FILE = "WKVerticalScrollBar/WKVerticalScrollBar-Info.plist"; 266 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 267 | PRODUCT_NAME = "$(TARGET_NAME)"; 268 | WRAPPER_EXTENSION = app; 269 | }; 270 | name = Debug; 271 | }; 272 | 4B47E3B0159B93B300D73B5D /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 276 | GCC_PREFIX_HEADER = "WKVerticalScrollBar/WKVerticalScrollBar-Prefix.pch"; 277 | INFOPLIST_FILE = "WKVerticalScrollBar/WKVerticalScrollBar-Info.plist"; 278 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 279 | PRODUCT_NAME = "$(TARGET_NAME)"; 280 | WRAPPER_EXTENSION = app; 281 | }; 282 | name = Release; 283 | }; 284 | /* End XCBuildConfiguration section */ 285 | 286 | /* Begin XCConfigurationList section */ 287 | 4B47E390159B93B300D73B5D /* Build configuration list for PBXProject "WKVerticalScrollBar" */ = { 288 | isa = XCConfigurationList; 289 | buildConfigurations = ( 290 | 4B47E3AC159B93B300D73B5D /* Debug */, 291 | 4B47E3AD159B93B300D73B5D /* Release */, 292 | ); 293 | defaultConfigurationIsVisible = 0; 294 | defaultConfigurationName = Release; 295 | }; 296 | 4B47E3AE159B93B300D73B5D /* Build configuration list for PBXNativeTarget "WKVerticalScrollBar" */ = { 297 | isa = XCConfigurationList; 298 | buildConfigurations = ( 299 | 4B47E3AF159B93B300D73B5D /* Debug */, 300 | 4B47E3B0159B93B300D73B5D /* Release */, 301 | ); 302 | defaultConfigurationIsVisible = 0; 303 | defaultConfigurationName = Release; 304 | }; 305 | /* End XCConfigurationList section */ 306 | }; 307 | rootObject = 4B47E38D159B93B300D73B5D /* Project object */; 308 | } 309 | -------------------------------------------------------------------------------- /WKVerticalScrollBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKAccessoryView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import 29 | 30 | @interface WKAccessoryView : UIView { 31 | @private 32 | UILabel *_textLabel; 33 | } 34 | 35 | @property (nonatomic, readwrite) CGFloat arrowWidth; 36 | @property (nonatomic, readwrite, retain) UIColor *foregroundColor; 37 | 38 | @property (nonatomic, readwrite) UIEdgeInsets labelEdgeInsets; 39 | @property (nonatomic, readonly) UILabel *textLabel; 40 | 41 | @end -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKAccessoryView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import "WKAccessoryView.h" 29 | 30 | @implementation WKAccessoryView 31 | 32 | @synthesize arrowWidth = _arrowWidth; 33 | @synthesize foregroundColor = _foregroundColor; 34 | 35 | @synthesize labelEdgeInsets = _labelEdgeInsets; 36 | 37 | - (id)initWithFrame:(CGRect)frame 38 | { 39 | if ((self = [super initWithFrame:frame])) { 40 | [self setOpaque:NO]; 41 | 42 | _arrowWidth = 10; 43 | _foregroundColor = [UIColor blackColor]; 44 | _labelEdgeInsets = UIEdgeInsetsMake(4, 6, 4, 6); 45 | 46 | _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 47 | [_textLabel setBackgroundColor:[UIColor clearColor]]; 48 | [_textLabel setTextColor:[UIColor whiteColor]]; 49 | [_textLabel setMinimumFontSize:8.0f]; 50 | [self addSubview:_textLabel]; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)dealloc 56 | { 57 | [_textLabel release]; 58 | 59 | [super dealloc]; 60 | } 61 | 62 | - (UILabel *)textLabel 63 | { 64 | return _textLabel; 65 | } 66 | 67 | - (void)setTextLabel:(UILabel *)textLabel 68 | { 69 | [_textLabel release]; 70 | _textLabel = [textLabel retain]; 71 | 72 | [self setNeedsLayout]; 73 | } 74 | 75 | - (void)layoutSubviews 76 | { 77 | [super layoutSubviews]; 78 | 79 | CGRect bounds = [self bounds]; 80 | 81 | [_textLabel setFrame:UIEdgeInsetsInsetRect(bounds, _labelEdgeInsets)]; 82 | } 83 | 84 | - (void)drawRect:(CGRect)rect 85 | { 86 | CGRect bounds = [self bounds]; 87 | CGContextRef context = UIGraphicsGetCurrentContext(); 88 | 89 | // +-----------+ 90 | // | \ 91 | // | / 92 | // +-----------+ 93 | 94 | CGMutablePathRef path = CGPathCreateMutable(); 95 | CGPathMoveToPoint(path, NULL, 0, 0); 96 | CGPathAddLineToPoint(path, NULL, bounds.size.width - _arrowWidth, 0); 97 | CGPathAddLineToPoint(path, NULL, bounds.size.width, bounds.size.height / 2); 98 | CGPathAddLineToPoint(path, NULL, bounds.size.width - _arrowWidth, bounds.size.height); 99 | CGPathAddLineToPoint(path, NULL, 0, bounds.size.height); 100 | CGPathCloseSubpath(path); 101 | 102 | CGContextAddPath(context, path); 103 | CGContextSetFillColorWithColor(context, [[self foregroundColor] CGColor]); 104 | CGContextFillPath(context); 105 | 106 | CGPathRelease(path); 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import "WKTextDemoViewController.h" 29 | 30 | @interface WKAppDelegate : UIResponder 31 | 32 | @property (nonatomic, readwrite, retain) UIWindow *window; 33 | @property (nonatomic, readonly) WKTextDemoViewController *demoController; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import "WKAppDelegate.h" 29 | 30 | @implementation WKAppDelegate 31 | 32 | @synthesize window = _window; 33 | @synthesize demoController = _demoController; 34 | 35 | - (void)dealloc 36 | { 37 | [_window release]; 38 | [_demoController release]; 39 | 40 | [super dealloc]; 41 | } 42 | 43 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 44 | { 45 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 46 | _demoController = [[WKTextDemoViewController alloc] init]; 47 | 48 | [[self window] setRootViewController:_demoController]; 49 | [[self window] makeKeyAndVisible]; 50 | 51 | return YES; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKTextDemoView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import 29 | #import "WKVerticalScrollBar.h" 30 | #import "WKAccessoryView.h" 31 | 32 | @interface WKTextDemoView : UIView 33 | 34 | @property (nonatomic, readwrite) CGFloat verticalPadding; 35 | 36 | @property (nonatomic, readonly) UILabel *textLabel; 37 | @property (nonatomic, readonly) UIScrollView *scrollView; 38 | @property (nonatomic, readonly) WKVerticalScrollBar *verticalScrollBar; 39 | @property (nonatomic, readonly) WKAccessoryView *accessoryView; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKTextDemoView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import "WKTextDemoView.h" 29 | 30 | @implementation WKTextDemoView 31 | 32 | @synthesize verticalPadding = _verticalPadding; 33 | 34 | @synthesize textLabel = _textLabel; 35 | @synthesize scrollView = _scrollView; 36 | @synthesize verticalScrollBar = _verticalScrollBar; 37 | @synthesize accessoryView = _accessoryView; 38 | 39 | - (id)initWithFrame:(CGRect)frame 40 | { 41 | if ((self = [super initWithFrame:frame])) { 42 | _verticalPadding = 50.0f; 43 | 44 | _scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero]; 45 | [_scrollView setBackgroundColor:[UIColor colorWithRed:0xE4 / 255.0f 46 | green:0xE2 / 255.0f 47 | blue:0xDD / 255.0f 48 | alpha:1.0f]]; 49 | [_scrollView addObserver:self 50 | forKeyPath:@"contentOffset" 51 | options:NSKeyValueObservingOptionNew 52 | context:nil]; 53 | [self addSubview:_scrollView]; 54 | 55 | _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 56 | [_textLabel setBackgroundColor:[UIColor clearColor]]; 57 | [_textLabel setFont:[UIFont fontWithName:@"Baskerville" size:18.0f]]; 58 | [_textLabel setNumberOfLines:0]; 59 | [_textLabel setLineBreakMode:UILineBreakModeWordWrap]; 60 | [_scrollView addSubview:_textLabel]; 61 | 62 | // NOTE: Make sure vertical scroll bar is on top of the scroll view 63 | _verticalScrollBar = [[WKVerticalScrollBar alloc] initWithFrame:CGRectZero]; 64 | [_verticalScrollBar setScrollView:_scrollView]; 65 | [self addSubview:_verticalScrollBar]; 66 | 67 | _accessoryView = [[WKAccessoryView alloc] initWithFrame:CGRectMake(0, 0, 65, 30)]; 68 | [_accessoryView setForegroundColor:[UIColor colorWithWhite:0.2f alpha:1.0f]]; 69 | [_verticalScrollBar setHandleAccessoryView:_accessoryView]; 70 | } 71 | return self; 72 | } 73 | 74 | - (void)dealloc 75 | { 76 | [_textLabel release]; 77 | [_scrollView release]; 78 | [_verticalScrollBar release]; 79 | 80 | [super dealloc]; 81 | } 82 | 83 | - (void)layoutSubviews 84 | { 85 | [super layoutSubviews]; 86 | 87 | CGRect bounds = [self bounds]; 88 | 89 | CGFloat columnWidth = bounds.size.width * 0.8f; 90 | CGSize textSize = [[_textLabel text] sizeWithFont:[_textLabel font] 91 | constrainedToSize:CGSizeMake(columnWidth, FLT_MAX)]; 92 | 93 | [_scrollView setContentSize:CGSizeMake(bounds.size.width, textSize.height + (_verticalPadding * 2))]; 94 | 95 | // Center the label in the screen 96 | [_textLabel setFrame:CGRectMake((bounds.size.width / 2) - (columnWidth / 2), _verticalPadding, 97 | columnWidth, textSize.height)]; 98 | 99 | [_scrollView setFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height)]; 100 | [_verticalScrollBar setFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height)]; 101 | } 102 | 103 | - (void)observeValueForKeyPath:(NSString *)keyPath 104 | ofObject:(id)object 105 | change:(NSDictionary *)change 106 | context:(void *)context 107 | { 108 | if (![keyPath isEqualToString:@"contentOffset"]) { 109 | return; 110 | } 111 | 112 | CGFloat contentOffsetY = [_scrollView contentOffset].y; 113 | CGFloat contentHeight = [_scrollView contentSize].height; 114 | CGFloat frameHeight = [_scrollView frame].size.height; 115 | 116 | CGFloat percent = (contentOffsetY / (contentHeight - frameHeight)) * 100; 117 | [[_accessoryView textLabel] setText:[NSString stringWithFormat:@"%i%%", (int)percent]]; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKTextDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import 29 | #import "WKTextDemoView.h" 30 | 31 | @interface WKTextDemoViewController : UIViewController { 32 | WKTextDemoView *demoView; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKTextDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import "WKTextDemoViewController.h" 29 | #import "WKVerticalScrollBar.h" 30 | 31 | @implementation WKTextDemoViewController 32 | 33 | - (void)dealloc 34 | { 35 | [demoView release]; 36 | 37 | [super dealloc]; 38 | } 39 | 40 | - (void)loadView 41 | { 42 | demoView = [[WKTextDemoView alloc] initWithFrame:CGRectZero]; 43 | [self setView:demoView]; 44 | } 45 | 46 | - (void)viewDidLoad 47 | { 48 | [super viewDidLoad]; 49 | 50 | NSString *fullPath = [[NSBundle mainBundle] pathForResource:@"fillerati" ofType:@"txt"]; 51 | NSString *text = [NSString stringWithContentsOfFile:fullPath 52 | encoding:NSUTF8StringEncoding 53 | error:nil]; 54 | 55 | [[demoView textLabel] setText:text]; 56 | } 57 | 58 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 59 | { 60 | return YES; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKVerticalScrollBar-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.litl.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKVerticalScrollBar-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'WKVerticalScrollBar' target in the 'WKVerticalScrollBar' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKVerticalScrollBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import 29 | #import 30 | 31 | @interface WKVerticalScrollBar : UIControl { 32 | @protected 33 | CALayer *handle; 34 | BOOL handleDragged; 35 | CGRect handleHitArea; 36 | 37 | UIColor *normalColor; 38 | UIColor *selectedColor; 39 | 40 | CGFloat _handleCornerRadius; 41 | CGFloat _handleSelectedCornerRadius; 42 | 43 | CGPoint lastTouchPoint; 44 | 45 | UIScrollView *_scrollView; 46 | UIView *_handleAccessoryView; 47 | } 48 | 49 | @property (nonatomic, readwrite) CGFloat handleWidth; 50 | @property (nonatomic, readwrite) CGFloat handleHitWidth; 51 | @property (nonatomic, readwrite) CGFloat handleSelectedWidth; 52 | 53 | @property (nonatomic, readwrite) CGFloat handleCornerRadius; 54 | @property (nonatomic, readwrite) CGFloat handleSelectedCornerRadius; 55 | 56 | @property (nonatomic, readwrite) CGFloat handleMinimumHeight; 57 | 58 | @property (nonatomic, readwrite, retain) UIScrollView *scrollView; 59 | @property (nonatomic, readwrite, retain) UIView *handleAccessoryView; 60 | 61 | - (void)setHandleColor:(UIColor *)color forState:(UIControlState)state; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/WKVerticalScrollBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | 28 | #import "WKVerticalScrollBar.h" 29 | 30 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 31 | 32 | @interface WKVerticalScrollBar () 33 | - (void)commonInit; 34 | @end 35 | 36 | @implementation WKVerticalScrollBar 37 | 38 | @synthesize handleWidth = _handleWidth; 39 | @synthesize handleHitWidth = _handleHitWidth; 40 | @synthesize handleSelectedWidth = _handleSelectedWidth; 41 | 42 | @synthesize handleMinimumHeight = _handleMinimumHeight; 43 | 44 | - (id)initWithFrame:(CGRect)frame 45 | { 46 | if ((self = [super initWithFrame:frame])) { 47 | [self commonInit]; 48 | } 49 | return self; 50 | } 51 | 52 | - (id)initWithCoder:(NSCoder *)aDecoder 53 | { 54 | if ((self = [super initWithCoder:aDecoder])) { 55 | [self commonInit]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)commonInit 61 | { 62 | _handleWidth = 5.0f; 63 | _handleSelectedWidth = 15.0f; 64 | _handleHitWidth = 44.0f; 65 | _handleMinimumHeight = 70.0f; 66 | 67 | _handleCornerRadius = _handleWidth / 2; 68 | _handleSelectedCornerRadius = _handleSelectedWidth / 2; 69 | 70 | handleHitArea = CGRectZero; 71 | 72 | normalColor = [[UIColor colorWithWhite:0.6f alpha:1.0f] retain]; 73 | selectedColor = [[UIColor colorWithWhite:0.4f alpha:1.0f] retain]; 74 | 75 | handle = [[CALayer alloc] init]; 76 | [handle setCornerRadius:_handleCornerRadius]; 77 | [handle setAnchorPoint:CGPointMake(1.0f, 0.0f)]; 78 | [handle setFrame:CGRectMake(0, 0, _handleWidth, 0)]; 79 | [handle setBackgroundColor:[normalColor CGColor]]; 80 | [[self layer] addSublayer:handle]; 81 | } 82 | 83 | - (void)dealloc 84 | { 85 | [handle release]; 86 | 87 | [_scrollView removeObserver:self forKeyPath:@"contentOffset"]; 88 | [_scrollView removeObserver:self forKeyPath:@"contentSize"]; 89 | [_scrollView release]; 90 | 91 | [_handleAccessoryView release]; 92 | 93 | [normalColor release]; 94 | [selectedColor release]; 95 | 96 | [super dealloc]; 97 | } 98 | 99 | - (UIScrollView *)scrollView 100 | { 101 | return _scrollView; 102 | } 103 | 104 | - (void)setScrollView:(UIScrollView *)scrollView; 105 | { 106 | [_scrollView removeObserver:self forKeyPath:@"contentOffset"]; 107 | [_scrollView removeObserver:self forKeyPath:@"contentSize"]; 108 | 109 | [_scrollView release]; 110 | _scrollView = [scrollView retain]; 111 | 112 | [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; 113 | [_scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil]; 114 | [_scrollView setShowsVerticalScrollIndicator:NO]; 115 | 116 | [self setNeedsLayout]; 117 | } 118 | 119 | - (UIView *)handleAccessoryView 120 | { 121 | return _handleAccessoryView; 122 | } 123 | 124 | - (void)setHandleAccessoryView:(UIView *)handleAccessoryView 125 | { 126 | [handleAccessoryView retain]; 127 | [_handleAccessoryView removeFromSuperview]; 128 | [_handleAccessoryView release]; 129 | _handleAccessoryView = handleAccessoryView; 130 | 131 | [_handleAccessoryView setAlpha:0.0f]; 132 | [self addSubview:_handleAccessoryView]; 133 | [self setNeedsLayout]; 134 | } 135 | 136 | - (void)setHandleColor:(UIColor *)color forState:(UIControlState)state 137 | { 138 | if (state == UIControlStateNormal) { 139 | [color retain]; 140 | [normalColor release]; 141 | normalColor = color; 142 | } else if (state == UIControlStateSelected) { 143 | [color retain]; 144 | [selectedColor release]; 145 | selectedColor = color; 146 | } 147 | } 148 | 149 | - (CGFloat)handleCornerRadius 150 | { 151 | return _handleCornerRadius; 152 | } 153 | 154 | - (void)setHandleCornerRadius:(CGFloat)handleCornerRadius 155 | { 156 | _handleCornerRadius = handleCornerRadius; 157 | 158 | if (!handleDragged) { 159 | [handle setCornerRadius:_handleCornerRadius]; 160 | } 161 | } 162 | 163 | - (CGFloat)handleSelectedCornerRadius 164 | { 165 | return _handleSelectedCornerRadius; 166 | } 167 | 168 | - (void)setHandleSelectedCornerRadius:(CGFloat)handleSelectedCornerRadius 169 | { 170 | _handleSelectedCornerRadius = handleSelectedCornerRadius; 171 | 172 | if (handleDragged) { 173 | [handle setCornerRadius:_handleSelectedCornerRadius]; 174 | } 175 | } 176 | 177 | - (void)layoutSubviews 178 | { 179 | [super layoutSubviews]; 180 | 181 | CGFloat contentHeight = [_scrollView contentSize].height; 182 | CGFloat frameHeight = [_scrollView frame].size.height; 183 | 184 | if (contentHeight <= frameHeight) { 185 | // Prevent divide by 0 below when we arrive here before _scrollView has geometry. 186 | // Also explicity hide handle when not needed, occasionally it's left visible due to 187 | // layoutSubviews being called with transient & invalid geometery 188 | [handle setOpacity:0.0f]; 189 | return; 190 | } 191 | 192 | [CATransaction begin]; 193 | [CATransaction setDisableActions:YES]; 194 | 195 | CGRect bounds = [self bounds]; 196 | 197 | // Calculate the current scroll value (0, 1) inclusive. 198 | // Note that contentOffset.y only goes from (0, contentHeight - frameHeight) 199 | // prevent divide by 0 below when we arrive here before _scrollView has geometry 200 | CGFloat scrollValue = (contentHeight - frameHeight == 0) ? 0 201 | : [_scrollView contentOffset].y / (contentHeight - frameHeight); 202 | 203 | // Set handleHeight proportionally to how much content is being currently viewed 204 | CGFloat handleHeight = CLAMP((frameHeight / contentHeight) * bounds.size.height, 205 | _handleMinimumHeight, bounds.size.height); 206 | 207 | [handle setOpacity:(handleHeight == bounds.size.height) ? 0.0f : 1.0f]; 208 | 209 | // Not only move the handle, but also shift where the position maps on to the handle, 210 | // so that the handle doesn't go off screen when the scrollValue approaches 1. 211 | CGFloat handleY = CLAMP((scrollValue * bounds.size.height) - (scrollValue * handleHeight), 212 | 0, bounds.size.height - handleHeight); 213 | 214 | CGFloat previousWidth = [handle bounds].size.width ?: _handleWidth; 215 | [handle setPosition:CGPointMake(bounds.size.width, handleY)]; 216 | [handle setBounds:CGRectMake(0, 0, previousWidth, handleHeight)]; 217 | 218 | // Center the accessory view to the left of the handle 219 | CGRect accessoryFrame = [_handleAccessoryView frame]; 220 | [_handleAccessoryView setCenter:CGPointMake(bounds.size.width - _handleHitWidth - (accessoryFrame.size.width / 2), 221 | handleY + (handleHeight / 2))]; 222 | 223 | handleHitArea = CGRectMake(bounds.size.width - _handleHitWidth, handleY, 224 | _handleHitWidth, handleHeight); 225 | 226 | [CATransaction commit]; 227 | } 228 | 229 | - (BOOL)handleVisible 230 | { 231 | return [handle opacity] == 1.0f; 232 | } 233 | 234 | - (void)growHandle 235 | { 236 | if (![self handleVisible]) { 237 | return; 238 | } 239 | 240 | [CATransaction begin]; 241 | [CATransaction setAnimationDuration:0.3f]; 242 | 243 | [handle setCornerRadius:_handleSelectedCornerRadius]; 244 | [handle setBounds:CGRectMake(0, 0, _handleSelectedWidth, [handle bounds].size.height)]; 245 | [handle setBackgroundColor:[selectedColor CGColor]]; 246 | 247 | [CATransaction commit]; 248 | 249 | [UIView animateWithDuration:0.3f animations:^{ 250 | [_handleAccessoryView setAlpha:1.0f]; 251 | }]; 252 | } 253 | 254 | - (void)shrinkHandle 255 | { 256 | if (![self handleVisible]) { 257 | return; 258 | } 259 | 260 | [CATransaction begin]; 261 | [CATransaction setAnimationDuration:0.3f]; 262 | 263 | [handle setCornerRadius:_handleCornerRadius]; 264 | [handle setBounds:CGRectMake(0, 0, _handleWidth, [handle bounds].size.height)]; 265 | [handle setBackgroundColor:[normalColor CGColor]]; 266 | 267 | [CATransaction commit]; 268 | 269 | [UIView animateWithDuration:0.3f animations:^{ 270 | [_handleAccessoryView setAlpha:0.0f]; 271 | }]; 272 | } 273 | 274 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 275 | { 276 | return CGRectContainsPoint(handleHitArea, point); 277 | } 278 | 279 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 280 | { 281 | if (![self handleVisible]) { 282 | return NO; 283 | } 284 | 285 | lastTouchPoint = [touch locationInView:self]; 286 | 287 | // When the user initiates a drag, make the handle grow so it's easier to see 288 | handleDragged = YES; 289 | [self growHandle]; 290 | 291 | [self setNeedsLayout]; 292 | 293 | return YES; 294 | } 295 | 296 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 297 | { 298 | CGPoint point = [touch locationInView:self]; 299 | 300 | CGSize contentSize = [_scrollView contentSize]; 301 | CGPoint contentOffset = [_scrollView contentOffset]; 302 | CGFloat frameHeight = [_scrollView frame].size.height; 303 | CGFloat deltaY = ((point.y - lastTouchPoint.y) / [self bounds].size.height) 304 | * [_scrollView contentSize].height; 305 | 306 | [_scrollView setContentOffset:CGPointMake(contentOffset.x, CLAMP(contentOffset.y + deltaY, 307 | 0, contentSize.height - frameHeight)) 308 | animated:NO]; 309 | lastTouchPoint = point; 310 | 311 | return YES; 312 | } 313 | 314 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 315 | { 316 | lastTouchPoint = CGPointZero; 317 | 318 | // When user drag is finished, return handle to previous size 319 | handleDragged = NO; 320 | [self shrinkHandle]; 321 | } 322 | 323 | - (void)observeValueForKeyPath:(NSString *)keyPath 324 | ofObject:(id)object 325 | change:(NSDictionary *)change 326 | context:(void *)context 327 | { 328 | if (object != _scrollView) { 329 | return; 330 | } 331 | 332 | [self setNeedsLayout]; 333 | } 334 | 335 | @end 336 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/fillerati.txt: -------------------------------------------------------------------------------- 1 | And thus the first man of the Pequod that mounted the mast to look out for the White Whale, on the White Whale's own peculiar ground; that man was swallowed up in the deep. But few, perhaps, thought of that at the time. Indeed, in some sort, they were not grieved at this event, at least as a portent; for they regarded it, not as a foreshadowing of evil in the future, but as the fulfilment of an evil already presaged. They declared that now they knew the reason of those wild shrieks they had heard the night before. But again the old Manxman said nay. 2 | 3 | The lost life-buoy was now to be replaced; Starbuck was directed to see to it; but as no cask of sufficient lightness could be found, and as in the feverish eagerness of what seemed the approaching crisis of the voyage, all hands were impatient of any toil but what was directly connected with its final end, whatever that might prove to be; therefore, they were going to leave the ship's stern unprovided with a buoy, when by certain strange signs and inuendoes Queequeg hinted a hint concerning his coffin. 4 | 5 | "A life-buoy of a coffin!" cried Starbuck, starting. 6 | 7 | "Rather queer, that, I should say," said Stubb. 8 | 9 | "It will make a good enough one," said Flask, "the carpenter here can arrange it easily." 10 | 11 | "Bring it up; there's nothing else for it," said Starbuck, after a melancholy pause. "Rig it, carpenter; do not look at me so-;the coffin, I mean. Dost thou hear me? Rig it." 12 | 13 | "And shall I nail down the lid, sir?" moving his hand as with a hammer. 14 | 15 | "Aye." 16 | 17 | "And shall I caulk the seams, sir?" moving his hand as with a caulking-iron. 18 | 19 | "Aye." 20 | 21 | The house whirled around two or three times and rose slowly through the air. Dorothy felt as if she were going up in a balloon. 22 | 23 | The north and south winds met where the house stood, and made it the exact center of the cyclone. In the middle of a cyclone the air is generally still, but the great pressure of the wind on every side of the house raised it up higher and higher, until it was at the very top of the cyclone; and there it remained and was carried miles and miles away as easily as you could carry a feather. 24 | 25 | It was very dark, and the wind howled horribly around her, but Dorothy found she was riding quite easily. After the first few whirls around, and one other time when the house tipped badly, she felt as if she were being rocked gently, like a baby in a cradle. 26 | 27 | Toto did not like it. He ran about the room, now here, now there, barking loudly; but Dorothy sat quite still on the floor and waited to see what would happen. 28 | 29 | Once Toto got too near the open trap door, and fell in; and at first the little girl thought she had lost him. But soon she saw one of his ears sticking up through the hole, for the strong pressure of the air was keeping him up so that he could not fall. She crept to the hole, caught Toto by the ear, and dragged him into the room again, afterward closing the trap door so that no more accidents could happen. 30 | 31 | Hour after hour passed away, and slowly Dorothy got over her fright; but she felt quite lonely, and the wind shrieked so loudly all about her that she nearly became deaf. At first she had wondered if she would be dashed to pieces when the house fell again; but as the hours passed and nothing terrible happened, she stopped worrying and resolved to wait calmly and see what the future would bring. At last she crawled over the swaying floor to her bed, and lay down upon it; and Toto followed and lay down beside her. 32 | 33 | In spite of the swaying of the house and the wailing of the wind, Dorothy soon closed her eyes and fell fast asleep. 34 | 35 | She was awakened by a shock, so sudden and severe that if Dorothy had not been lying on the soft bed she might have been hurt. As it was, the jar made her catch her breath and wonder what had happened; and Toto put his cold little nose into her face and whined dismally. Dorothy sat up and noticed that the house was not moving; nor was it dark, for the bright sunshine came in at the window, flooding the little room. She sprang from her bed and with Toto at her heels ran and opened the door. 36 | 37 | The little girl gave a cry of amazement and looked about her, her eyes growing bigger and bigger at the wonderful sights she saw. 38 | 39 | The cyclone had set the house down very gently — for a cyclone — in the midst of a country of marvelous beauty. There were lovely patches of greensward all about, with stately trees bearing rich and luscious fruits. Banks of gorgeous flowers were on every hand, and birds with rare and brilliant plumage sang and fluttered in the trees and bushes. A little way off was a small brook, rushing and sparkling along between green banks, and murmuring in a voice very grateful to a little girl who had lived so long on the dry, gray prairies. 40 | 41 | "The rascal is off, after all!" he exclaimed. "Two thousand pounds sacrificed! He's as prodigal as a thief! I'll follow him to the end of the world if necessary; but, at the rate he is going on, the stolen money will soon be exhausted." 42 | 43 | The detective was not far wrong in making this conjecture. Since leaving London, what with travelling expenses, bribes, the purchase of the elephant, bails, and fines, Mr. Fogg had already spent more than five thousand pounds on the way, and the percentage of the sum recovered from the bank robber promised to the detectives, was rapidly diminishing. 44 | 45 | The Rangoon-;one of the Peninsular and Oriental Company's boats plying in the Chinese and Japanese seas-;was a screw steamer, built of iron, weighing about seventeen hundred and seventy tons, and with engines of four hundred horse-power. She was as fast, but not as well fitted up, as the Mongolia, and Aouda was not as comfortably provided for on board of her as Phileas Fogg could have wished. However, the trip from Calcutta to Hong Kong only comprised some three thousand five hundred miles, occupying from ten to twelve days, and the young woman was not difficult to please. 46 | 47 | During the first days of the journey Aouda became better acquainted with her protector, and constantly gave evidence of her deep gratitude for what he had done. The phlegmatic gentleman listened to her, apparently at least, with coldness, neither his voice nor his manner betraying the slightest emotion; but he seemed to be always on the watch that nothing should be wanting to Aouda's comfort. He visited her regularly each day at certain hours, not so much to talk himself, as to sit and hear her talk. He treated her with the strictest politeness, but with the precision of an automaton, the movements of which had been arranged for this purpose. Aouda did not quite know what to make of him, though Passepartout had given her some hints of his master's eccentricity, and made her smile by telling her of the wager which was sending him round the world. After all, she owed Phileas Fogg her life, and she always regarded him through the exalting medium of her gratitude. 48 | 49 | Aouda confirmed the Parsee guide's narrative of her touching history. She did, indeed, belong to the highest of the native races of India. Many of the Parsee merchants have made great fortunes there by dealing in cotton; and one of them, Sir Jametsee Jeejeebhoy, was made a baronet by the English government. Aouda was a relative of this great man, and it was his cousin, Jeejeeh, whom she hoped to join at Hong Kong. Whether she would find a protector in him she could not tell; but Mr. Fogg essayed to calm her anxieties, and to assure her that everything would be mathematically-;he used the very word-;arranged. Aouda fastened her great eyes, "clear as the sacred lakes of the Himalaya," upon him; but the intractable Fogg, as reserved as ever, did not seem at all inclined to throw himself into this lake. 50 | 51 | The first few days of the voyage passed prosperously, amid favourable weather and propitious winds, and they soon came in sight of the great Andaman, the principal of the islands in the Bay of Bengal, with its picturesque Saddle Peak, two thousand four hundred feet high, looming above the waters. The steamer passed along near the shores, but the savage Papuans, who are in the lowest scale of humanity, but are not, as has been asserted, cannibals, did not make their appearance. 52 | 53 | The panorama of the islands, as they steamed by them, was superb. Vast forests of palms, arecs, bamboo, teakwood, of the gigantic mimosa, and tree-like ferns covered the foreground, while behind, the graceful outlines of the mountains were traced against the sky; and along the coasts swarmed by thousands the precious swallows whose nests furnish a luxurious dish to the tables of the Celestial Empire. The varied landscape afforded by the Andaman Islands was soon passed, however, and the Rangoon rapidly approached the Straits of Malacca, which gave access to the China seas. 54 | 55 | What was detective Fix, so unluckily drawn on from country to country, doing all this while? He had managed to embark on the Rangoon at Calcutta without being seen by Passepartout, after leaving orders that, if the warrant should arrive, it should be forwarded to him at Hong Kong; and he hoped to conceal his presence to the end of the voyage. It would have been difficult to explain why he was on board without awakening Passepartout's suspicions, who thought him still at Bombay. But necessity impelled him, nevertheless, to renew his acquaintance with the worthy servant, as will be seen. 56 | 57 | All the detective's hopes and wishes were now centred on Hong Kong; for the steamer's stay at Singapore would be too brief to enable him to take any steps there. The arrest must be made at Hong Kong, or the robber would probably escape him for ever. Hong Kong was the last English ground on which he would set foot; beyond, China, Japan, America offered to Fogg an almost certain refuge. If the warrant should at last make its appearance at Hong Kong, Fix could arrest him and give him into the hands of the local police, and there would be no further trouble. But beyond Hong Kong, a simple warrant would be of no avail; an extradition warrant would be necessary, and that would result in delays and obstacles, of which the rascal would take advantage to elude justice. 58 | 59 | "Look here, Hare-Lip, you believe in lots of things you can't see." 60 | 61 | Hare-Lip shook his head. 62 | 63 | "You believe in dead men walking about. You never seen one dead man walk about." 64 | 65 | "I tell you I seen 'em, last winter, when I was wolf-hunting with dad." 66 | 67 | "Well, you always spit when you cross running water," Edwin challenged. 68 | 69 | "That's to keep off bad luck," was Hare-Lip's defence. 70 | 71 | "You believe in bad luck?" 72 | 73 | "Sure." 74 | 75 | "An' you ain't never seen bad luck," Edwin concluded triumphantly. "You're just as bad as Granser and his germs. You believe in what you don't see. Go on, Granser." 76 | 77 | Hare-Lip, crushed by this metaphysical defeat, remained silent, and the old man went on. Often and often, though this narrative must not be clogged by the details, was Granser's tale interrupted while the boys squabbled among themselves. Also, among themselves they kept up a constant, low-voiced exchange of explanation and conjecture, as they strove to follow the old man into his unknown and vanished world. 78 | 79 | "But I have you now Dian," I cried; "nor shall Jubal, nor any other have you, for you are mine," and I seized her hand, nor did I lift it above her head and let it fall in token of release. 80 | 81 | She had risen to her feet, and was looking straight into my eyes with level gaze. 82 | 83 | "I do not believe you," she said, "for if you meant it you would have done this when the others were present to witness it-;then I should truly have been your mate; now there is no one to see you do it, for you know that without witnesses your act does not bind you to me," and she withdrew her hand from mine and turned away. 84 | 85 | I tried to convince her that I was sincere, but she simply couldn't forget the humiliation that I had put upon her on that other occasion. 86 | 87 | "If you mean all that you say you will have ample chance to prove it," she said, "if Jubal does not catch and kill you. I am in your power, and the treatment you accord me will be the best proof of your intentions toward me. I am not your mate, and again I tell you that I hate you, and that I should be glad if I never saw you again." 88 | 89 | Dian certainly was candid. There was no gainsaying that. In fact I found candor and directness to be quite a marked characteristic of the cave men of Pellucidar. Finally I suggested that we make some attempt to gain my cave, where we might escape the searching Jubal, for I am free to admit that I had no considerable desire to meet the formidable and ferocious creature, of whose mighty prowess Dian had told me when I first met her. He it was who, armed with a puny knife, had met and killed a cave bear in a hand-to-hand struggle. It was Jubal who could cast his spear entirely through the armored carcass of the sadok at fifty paces. It was he who had crushed the skull of a charging dyryth with a single blow of his war club. No, I was not pining to meet the Ugly One-and it was quite certain that I should not go out and hunt for him; but the matter was taken out of my hands very quickly, as is often the way, and I did meet Jubal the Ugly One face to face. 90 | 91 | This is how it happened. I had led Dian back along the ledge the way she had come, searching for a path that would lead us to the top of the cliff, for I knew that we could then cross over to the edge of my own little valley, where I felt certain we should find a means of ingress from the cliff top. As we proceeded along the ledge I gave Dian minute directions for finding my cave against the chance of something happening to me. I knew that she would be quite safely hidden away from pursuit once she gained the shelter of my lair, and the valley would afford her ample means of sustenance. 92 | 93 | Also, I was very much piqued by her treatment of me. My heart was sad and heavy, and I wanted to make her feel badly by suggesting that something terrible might happen to me-;that I might, in fact, be killed. But it didn't work worth a cent, at least as far as I could perceive. Dian simply shrugged those magnificent shoulders of hers, and murmured something to the effect that one was not rid of trouble so easily as that. 94 | 95 | For a while I kept still. I was utterly squelched. And to think that I had twice protected her from attack-;the last time risking my life to save hers. It was incredible that even a daughter of the Stone Age could be so ungrateful-;so heartless; but maybe her heart partook of the qualities of her epoch. 96 | 97 | Presently we found a rift in the cliff which had been widened and extended by the action of the water draining through it from the plateau above. It gave us a rather rough climb to the summit, but finally we stood upon the level mesa which stretched back for several miles to the mountain range. Behind us lay the broad inland sea, curving upward in the horizonless distance to merge into the blue of the sky, so that for all the world it looked as though the sea lapped back to arch completely over us and disappear beyond the distant mountains at our backs-;the weird and uncanny aspect of the seascapes of Pellucidar balk description. 98 | 99 | "I know you cannot count beyond ten, so I will tell you. Hold up your two hands. On both of them you have altogether ten fingers and thumbs. Very well. I now take this grain of sand-;you hold it, Hoo-Hoo." He dropped the grain of sand into the lad's palm and went on. "Now that grain of sand stands for the ten fingers of Edwin. I add another grain. That's ten more fingers. And I add another, and another, and another, until I have added as many grains as Edwin has fingers and thumbs. That makes what I call one hundred. Remember that word-;one hundred. Now I put this pebble in Hare-Lip's hand. It stands for ten grains of sand, or ten tens of fingers, or one hundred fingers. I put in ten pebbles. They stand for a thousand fingers. I take a mussel-shell, and it stands for ten pebbles, or one hundred grains of sand, or one thousand fingers...." And so on, laboriously, and with much reiteration, he strove to build up in their minds a crude conception of numbers. As the quantities increased, he had the boys holding different magnitudes in each of their hands. For still higher sums, he laid the symbols on the log of driftwood; and for symbols he was hard put, being compelled to use the teeth from the skulls for millions, and the crab-shells for billions. It was here that he stopped, for the boys were showing signs of becoming tired. 100 | 101 | "There were four million people in San Francisco-;four teeth." 102 | 103 | The boys' eyes ranged along from the teeth and from hand to hand, down through the pebbles and sand-grains to Edwin's fingers. And back again they ranged along the ascending series in the effort to grasp such inconceivable numbers. 104 | 105 | "That was a lot of folks, Granser," Edwin at last hazarded. 106 | 107 | "Like sand on the beach here, like sand on the beach, each grain of sand a man, or woman, or child. Yes, my boy, all those people lived right here in San Francisco. And at one time or another all those people came out on this very beach-;more people than there are grains of sand. More-;more-;more. And San Francisco was a noble city. And across the bay-;where we camped last year, even more people lived, clear from Point Richmond, on the level ground and on the hills, all the way around to San Leandro-;one great city of seven million people.-;Seven teeth... there, that's it, seven millions." 108 | 109 | Again the boys' eyes ranged up and down from Edwin's fingers to the teeth on the log. 110 | 111 | "The world was full of people. The census of 2010 gave eight billions for the whole world-;eight crab-shells, yes, eight billions. It was not like to-day. Mankind knew a great deal more about getting food. And the more food there was, the more people there were. In the year 1800, there were one hundred and seventy millions in Europe alone. One hundred years later-;a grain of sand, Hoo-Hoo-;one hundred years later, at 1900, there were five hundred millions in Europe-;five grains of sand, Hoo-Hoo, and this one tooth. This shows how easy was the getting of food, and how men increased. And in the year 2000 there were fifteen hundred millions in Europe. And it was the same all over the rest of the world. Eight crab-shells there, yes, eight billion people were alive on the earth when the Scarlet Death began. 112 | 113 | "I was a young man when the Plague came-;twenty-seven years old; and I lived on the other side of San Francisco Bay, in Berkeley. You remember those great stone houses, Edwin, when we came down the hills from Contra Costa? That was where I lived, in those stone houses. I was a professor of English literature." 114 | 115 | Much of this was over the heads of the boys, but they strove to comprehend dimly this tale of the past. 116 | 117 | "What was them stone houses for?" Hare-Lip queried. 118 | -------------------------------------------------------------------------------- /WKVerticalScrollBar/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKVerticalScrollBar 3 | // http://github.com/litl/WKVerticalScrollBar 4 | // 5 | // Copyright (C) 2012 litl, LLC 6 | // Copyright (C) 2012 WKVerticalScrollBar authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import "WKAppDelegate.h" 29 | 30 | int main(int argc, char *argv[]) 31 | { 32 | @autoreleasepool { 33 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([WKAppDelegate class])); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /images/demo-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litl/WKVerticalScrollBar/27935231632da293f5a70ba35cd2bf7a98655e1b/images/demo-01.png -------------------------------------------------------------------------------- /images/demo-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litl/WKVerticalScrollBar/27935231632da293f5a70ba35cd2bf7a98655e1b/images/demo-02.png --------------------------------------------------------------------------------