├── .gitignore ├── Assets └── 1.gif ├── DRPageScrollView.podspec ├── DRPageScrollView.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── DRPageScrollView ├── DRPageScrollView.h └── DRPageScrollView.m ├── DRPageScrollViewDemo ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-40~iPad.png │ │ ├── Icon-40~iPad@2x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon-Small~iPad.png │ │ └── Icon-Small~iPad@2x.png │ ├── device-rotation.imageset │ │ ├── Contents.json │ │ └── device-rotation.pdf │ ├── feedback.imageset │ │ ├── Contents.json │ │ └── feedback.pdf │ ├── page-reuse.imageset │ │ ├── Contents.json │ │ └── page-reuse.pdf │ └── vulcan-salute.imageset │ │ ├── Contents.json │ │ └── vulcan-salute.pdf ├── Info.plist ├── MainViewController.h ├── MainViewController.m ├── PageViews.xib └── main.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | 22 | #Pods/ 23 | -------------------------------------------------------------------------------- /Assets/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/Assets/1.gif -------------------------------------------------------------------------------- /DRPageScrollView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "DRPageScrollView" 3 | s.version = "1.0.0" 4 | s.summary = "Block-driven paginated scroll views" 5 | s.homepage = "http://github.com/Dromaguirre/DRPageScrollView" 6 | s.author = { "David Roman" => "dromaguirre@gmail.com" } 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | 9 | s.platform = :ios, '8.0' 10 | s.ios.deployment_target = '8.0' 11 | 12 | s.source = { :git => "https://github.com/Dromaguirre/DRPageScrollView.git", :tag => s.version.to_s } 13 | s.source_files = 'DRPageScrollView/*.{h,m}' 14 | s.frameworks = 'Foundation', 'UIKit' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /DRPageScrollView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 75DF8A671ACE02C800A899B8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DF8A661ACE02C800A899B8 /* main.m */; }; 11 | 75DF8A6A1ACE02C800A899B8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DF8A691ACE02C800A899B8 /* AppDelegate.m */; }; 12 | 75DF8A6F1ACE02C800A899B8 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 75DF8A6D1ACE02C800A899B8 /* LaunchScreen.xib */; }; 13 | 75DF8A871ACE038000A899B8 /* DRPageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DF8A861ACE038000A899B8 /* DRPageScrollView.m */; }; 14 | 75DF8A901ACF305100A899B8 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DF8A8F1ACF305100A899B8 /* MainViewController.m */; }; 15 | 75E1D9611AF66BB800402514 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 75E1D9601AF66BB800402514 /* Images.xcassets */; }; 16 | 75E1D9631AF69BC200402514 /* PageViews.xib in Resources */ = {isa = PBXBuildFile; fileRef = 75E1D9621AF69BC200402514 /* PageViews.xib */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 75DF8A611ACE02C800A899B8 /* DRPageScrollViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DRPageScrollViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 75DF8A651ACE02C800A899B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 22 | 75DF8A661ACE02C800A899B8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 23 | 75DF8A681ACE02C800A899B8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 75DF8A691ACE02C800A899B8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 75DF8A6E1ACE02C800A899B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 26 | 75DF8A851ACE038000A899B8 /* DRPageScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DRPageScrollView.h; path = DRPageScrollView/DRPageScrollView.h; sourceTree = SOURCE_ROOT; }; 27 | 75DF8A861ACE038000A899B8 /* DRPageScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DRPageScrollView.m; path = DRPageScrollView/DRPageScrollView.m; sourceTree = SOURCE_ROOT; }; 28 | 75DF8A8E1ACF305100A899B8 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; 29 | 75DF8A8F1ACF305100A899B8 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; 30 | 75E1D9601AF66BB800402514 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 31 | 75E1D9621AF69BC200402514 /* PageViews.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageViews.xib; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 75DF8A5E1ACE02C800A899B8 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 75DF8A561ACE02A200A899B8 = { 46 | isa = PBXGroup; 47 | children = ( 48 | 75DF8A631ACE02C800A899B8 /* DRPageScrollViewDemo */, 49 | 75DF8A621ACE02C800A899B8 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 75DF8A621ACE02C800A899B8 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 75DF8A611ACE02C800A899B8 /* DRPageScrollViewDemo.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 75DF8A631ACE02C800A899B8 /* DRPageScrollViewDemo */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 75DF8A681ACE02C800A899B8 /* AppDelegate.h */, 65 | 75DF8A691ACE02C800A899B8 /* AppDelegate.m */, 66 | 75DF8A8E1ACF305100A899B8 /* MainViewController.h */, 67 | 75DF8A8F1ACF305100A899B8 /* MainViewController.m */, 68 | 75E1D9621AF69BC200402514 /* PageViews.xib */, 69 | 75DF8A881ACE038700A899B8 /* DRPageScrollView */, 70 | 75DF8A641ACE02C800A899B8 /* Supporting Files */, 71 | ); 72 | path = DRPageScrollViewDemo; 73 | sourceTree = ""; 74 | }; 75 | 75DF8A641ACE02C800A899B8 /* Supporting Files */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 75E1D9601AF66BB800402514 /* Images.xcassets */, 79 | 75DF8A6D1ACE02C800A899B8 /* LaunchScreen.xib */, 80 | 75DF8A651ACE02C800A899B8 /* Info.plist */, 81 | 75DF8A661ACE02C800A899B8 /* main.m */, 82 | ); 83 | name = "Supporting Files"; 84 | sourceTree = ""; 85 | }; 86 | 75DF8A881ACE038700A899B8 /* DRPageScrollView */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 75DF8A851ACE038000A899B8 /* DRPageScrollView.h */, 90 | 75DF8A861ACE038000A899B8 /* DRPageScrollView.m */, 91 | ); 92 | name = DRPageScrollView; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 75DF8A601ACE02C800A899B8 /* DRPageScrollViewDemo */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 75DF8A7C1ACE02C800A899B8 /* Build configuration list for PBXNativeTarget "DRPageScrollViewDemo" */; 101 | buildPhases = ( 102 | 75DF8A5D1ACE02C800A899B8 /* Sources */, 103 | 75DF8A5E1ACE02C800A899B8 /* Frameworks */, 104 | 75DF8A5F1ACE02C800A899B8 /* Resources */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = DRPageScrollViewDemo; 111 | productName = DRPageScrollViewDemo; 112 | productReference = 75DF8A611ACE02C800A899B8 /* DRPageScrollViewDemo.app */; 113 | productType = "com.apple.product-type.application"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | 75DF8A571ACE02A200A899B8 /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | LastUpgradeCheck = 0630; 122 | TargetAttributes = { 123 | 75DF8A601ACE02C800A899B8 = { 124 | CreatedOnToolsVersion = 6.2; 125 | }; 126 | }; 127 | }; 128 | buildConfigurationList = 75DF8A5A1ACE02A200A899B8 /* Build configuration list for PBXProject "DRPageScrollView" */; 129 | compatibilityVersion = "Xcode 3.2"; 130 | developmentRegion = English; 131 | hasScannedForEncodings = 0; 132 | knownRegions = ( 133 | en, 134 | Base, 135 | ); 136 | mainGroup = 75DF8A561ACE02A200A899B8; 137 | productRefGroup = 75DF8A621ACE02C800A899B8 /* Products */; 138 | projectDirPath = ""; 139 | projectRoot = ""; 140 | targets = ( 141 | 75DF8A601ACE02C800A899B8 /* DRPageScrollViewDemo */, 142 | ); 143 | }; 144 | /* End PBXProject section */ 145 | 146 | /* Begin PBXResourcesBuildPhase section */ 147 | 75DF8A5F1ACE02C800A899B8 /* Resources */ = { 148 | isa = PBXResourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 75DF8A6F1ACE02C800A899B8 /* LaunchScreen.xib in Resources */, 152 | 75E1D9631AF69BC200402514 /* PageViews.xib in Resources */, 153 | 75E1D9611AF66BB800402514 /* Images.xcassets in Resources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXResourcesBuildPhase section */ 158 | 159 | /* Begin PBXSourcesBuildPhase section */ 160 | 75DF8A5D1ACE02C800A899B8 /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 75DF8A6A1ACE02C800A899B8 /* AppDelegate.m in Sources */, 165 | 75DF8A671ACE02C800A899B8 /* main.m in Sources */, 166 | 75DF8A871ACE038000A899B8 /* DRPageScrollView.m in Sources */, 167 | 75DF8A901ACF305100A899B8 /* MainViewController.m in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | 75DF8A6D1ACE02C800A899B8 /* LaunchScreen.xib */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | 75DF8A6E1ACE02C800A899B8 /* Base */, 178 | ); 179 | name = LaunchScreen.xib; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXVariantGroup section */ 183 | 184 | /* Begin XCBuildConfiguration section */ 185 | 75DF8A5B1ACE02A200A899B8 /* Debug */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 189 | ONLY_ACTIVE_ARCH = YES; 190 | }; 191 | name = Debug; 192 | }; 193 | 75DF8A5C1ACE02A200A899B8 /* Release */ = { 194 | isa = XCBuildConfiguration; 195 | buildSettings = { 196 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 197 | }; 198 | name = Release; 199 | }; 200 | 75DF8A7D1ACE02C800A899B8 /* Debug */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | ALWAYS_SEARCH_USER_PATHS = NO; 204 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 212 | CLANG_WARN_EMPTY_BODY = YES; 213 | CLANG_WARN_ENUM_CONVERSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_UNREACHABLE_CODE = YES; 217 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 218 | CODE_SIGN_IDENTITY = "iPhone Developer"; 219 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 220 | COPY_PHASE_STRIP = NO; 221 | ENABLE_STRICT_OBJC_MSGSEND = YES; 222 | GCC_C_LANGUAGE_STANDARD = gnu99; 223 | GCC_DYNAMIC_NO_PIC = NO; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | INFOPLIST_FILE = DRPageScrollViewDemo/Info.plist; 237 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 238 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 239 | MTL_ENABLE_DEBUG_INFO = YES; 240 | ONLY_ACTIVE_ARCH = YES; 241 | PRODUCT_NAME = "$(TARGET_NAME)"; 242 | SDKROOT = iphoneos; 243 | TARGETED_DEVICE_FAMILY = "1,2"; 244 | }; 245 | name = Debug; 246 | }; 247 | 75DF8A7E1ACE02C800A899B8 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BOOL_CONVERSION = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | CODE_SIGN_IDENTITY = "iPhone Developer"; 266 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 267 | COPY_PHASE_STRIP = NO; 268 | ENABLE_NS_ASSERTIONS = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | INFOPLIST_FILE = DRPageScrollViewDemo/Info.plist; 278 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 279 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 280 | MTL_ENABLE_DEBUG_INFO = NO; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | SDKROOT = iphoneos; 283 | TARGETED_DEVICE_FAMILY = "1,2"; 284 | VALIDATE_PRODUCT = YES; 285 | }; 286 | name = Release; 287 | }; 288 | /* End XCBuildConfiguration section */ 289 | 290 | /* Begin XCConfigurationList section */ 291 | 75DF8A5A1ACE02A200A899B8 /* Build configuration list for PBXProject "DRPageScrollView" */ = { 292 | isa = XCConfigurationList; 293 | buildConfigurations = ( 294 | 75DF8A5B1ACE02A200A899B8 /* Debug */, 295 | 75DF8A5C1ACE02A200A899B8 /* Release */, 296 | ); 297 | defaultConfigurationIsVisible = 0; 298 | defaultConfigurationName = Release; 299 | }; 300 | 75DF8A7C1ACE02C800A899B8 /* Build configuration list for PBXNativeTarget "DRPageScrollViewDemo" */ = { 301 | isa = XCConfigurationList; 302 | buildConfigurations = ( 303 | 75DF8A7D1ACE02C800A899B8 /* Debug */, 304 | 75DF8A7E1ACE02C800A899B8 /* Release */, 305 | ); 306 | defaultConfigurationIsVisible = 0; 307 | defaultConfigurationName = Release; 308 | }; 309 | /* End XCConfigurationList section */ 310 | }; 311 | rootObject = 75DF8A571ACE02A200A899B8 /* Project object */; 312 | } 313 | -------------------------------------------------------------------------------- /DRPageScrollView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DRPageScrollView/DRPageScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DRPageScrollView.h 3 | // DRPageScrollView 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// The type of block used to define what to display on each page view. 12 | typedef void(^DRPageHandlerBlock)(UIView *pageView); 13 | 14 | @interface DRPageScrollView : UIScrollView 15 | 16 | /// Determines whether page views are reused or not in order to reduce memory consumption. 17 | @property (nonatomic, getter=isPageReuseEnabled) BOOL pageReuseEnabled; 18 | 19 | /// The current page index (starting from 0). 20 | @property (nonatomic, assign) NSInteger currentPage; 21 | 22 | /// The total number of pages in the scroll view. 23 | @property (nonatomic, readonly) NSInteger numberOfPages; 24 | 25 | /** 26 | * Sets up a new page for the scroll view. 27 | * 28 | * @param handler A block that defines what to display on the page. 29 | **/ 30 | - (void)addPageWithHandler:(DRPageHandlerBlock)handler; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /DRPageScrollView/DRPageScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DRPageScrollView.m 3 | // DRPageScrollView 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import "DRPageScrollView.h" 10 | 11 | @interface DRPageScrollView () { 12 | NSInteger previousPage; 13 | NSInteger instantaneousPreviousPage; 14 | } 15 | 16 | @property (nonatomic, strong) NSArray *pageViews; 17 | @property (nonatomic, strong) NSArray *pageHandlers; 18 | 19 | @end 20 | 21 | @implementation DRPageScrollView 22 | 23 | - (instancetype)init { 24 | if (self = [super init]) { 25 | previousPage = -1; 26 | self.pagingEnabled = YES; 27 | self.showsHorizontalScrollIndicator = NO; 28 | self.showsVerticalScrollIndicator = NO; 29 | 30 | self.pageViews = [NSMutableArray new]; 31 | self.pageHandlers = [NSArray new]; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | - (void)setPageReuseEnabled:(BOOL)pageReuseEnabled { 38 | if (self.numberOfPages > 0) return; 39 | _pageReuseEnabled = pageReuseEnabled; 40 | } 41 | 42 | - (NSInteger)currentPage { 43 | CGPoint presentationLayerContentOffset = [self.layer.presentationLayer bounds].origin; 44 | return MAX(round(presentationLayerContentOffset.x/self.frame.size.width), 0); 45 | } 46 | 47 | - (void)setCurrentPage:(NSInteger)currentPage { 48 | self.contentOffset = CGPointMake(self.frame.size.width*currentPage, self.contentOffset.y); 49 | } 50 | 51 | - (NSInteger)numberOfPages { 52 | return [self.pageHandlers count]; 53 | } 54 | 55 | - (void)addPageWithHandler:(DRPageHandlerBlock)handler { 56 | self.pageHandlers = [self.pageHandlers arrayByAddingObject:handler]; 57 | } 58 | 59 | #pragma mark Pages 60 | 61 | - (BOOL)isAnimating { 62 | return [[self.layer animationKeys] count] != 0; 63 | } 64 | 65 | - (void)setContentOffset:(CGPoint)contentOffset { 66 | [super setContentOffset:contentOffset]; 67 | 68 | if ([self isAnimating]) { 69 | CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(didRefreshDisplay:)]; 70 | [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 71 | } else { 72 | [self didScrollToPosition:contentOffset]; 73 | } 74 | } 75 | 76 | - (void)didRefreshDisplay:(CADisplayLink *)displayLink { 77 | if ([self isAnimating]) { 78 | CGPoint presentationLayerContentOffset = [self.layer.presentationLayer bounds].origin; 79 | [self didScrollToPosition:presentationLayerContentOffset]; 80 | } else { 81 | [displayLink invalidate]; 82 | } 83 | } 84 | 85 | - (void)didScrollToPosition:(CGPoint)position { 86 | if (self.numberOfPages == 0 || self.currentPage == previousPage) return; 87 | 88 | if (self.pageReuseEnabled) { 89 | for (UIView *pageView in self.pageViews) { 90 | if (pageView.tag < self.currentPage - 1 || pageView.tag > self.currentPage + 1) { 91 | [self queuePageView:pageView]; 92 | } 93 | } 94 | } 95 | 96 | for (NSInteger i = self.currentPage-1; i <= self.currentPage+1; i++) { 97 | if (i >= 0 && i < self.numberOfPages && ![self pageViewWithTag:i]) { 98 | UIView *pageView = [self dequeuePageView]; 99 | pageView.tag = i; 100 | 101 | DRPageHandlerBlock handler = self.pageHandlers[i]; 102 | handler(pageView); 103 | [self addSubview:pageView]; 104 | 105 | NSInteger pageMultiplier = 2*i+1; // We need an odd multiplier. Any odd number can be expressed as 2n+1. 106 | 107 | NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:pageMultiplier constant:0]; 108 | NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]; 109 | NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0]; 110 | NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1 constant:0]; 111 | 112 | [self addConstraints:@[centerXConstraint, centerYConstraint, widthConstraint, heightConstraint]]; // This may lead to vertical orientation support in the future ;) 113 | } 114 | } 115 | 116 | previousPage = self.currentPage; 117 | } 118 | 119 | - (void)queuePageView:(UIView *)pageView { 120 | [pageView removeFromSuperview]; 121 | 122 | for (UIView *sb in pageView.subviews) { 123 | [sb removeFromSuperview]; 124 | } 125 | 126 | pageView.tag = -1; 127 | } 128 | 129 | - (UIView *)dequeuePageView { 130 | for (UIView *pv in self.pageViews) { 131 | if (!pv.superview) { 132 | return pv; 133 | } 134 | } 135 | 136 | UIView *pageView = [UIView new]; 137 | pageView.tag = -1; 138 | pageView.translatesAutoresizingMaskIntoConstraints = NO; 139 | self.pageViews = [self.pageViews arrayByAddingObject:pageView]; 140 | 141 | return pageView; 142 | } 143 | 144 | - (UIView *)pageViewWithTag:(NSInteger)tag { 145 | for (UIView *pageView in self.subviews) { 146 | if (pageView.tag == tag && pageView.class == [UIView class]) { 147 | return pageView; 148 | } 149 | } 150 | 151 | return nil; 152 | } 153 | 154 | #pragma mark Rotation/resize 155 | 156 | - (void)layoutSubviews { 157 | CGFloat neededContentSizeWidth = self.frame.size.width*self.numberOfPages; 158 | 159 | if (self.contentSize.width != neededContentSizeWidth) { 160 | [self refreshDimensions]; 161 | self.currentPage = instantaneousPreviousPage; 162 | } else { 163 | instantaneousPreviousPage = self.currentPage; 164 | } 165 | 166 | [super layoutSubviews]; 167 | } 168 | 169 | - (void)refreshDimensions { 170 | self.contentSize = CGSizeMake(self.frame.size.width*self.numberOfPages, self.frame.size.height); 171 | self.contentInset = UIEdgeInsetsZero; 172 | } 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DRPageScrollViewDemo 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DRPageScrollViewDemo 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | #import "MainViewController.h" 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | self.window.backgroundColor = [UIColor whiteColor]; 18 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MainViewController new]]; 19 | 20 | [self.window makeKeyAndVisible]; 21 | 22 | return YES; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 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 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small@3x.png", 7 | "scale" : "3x", 8 | }, 9 | { 10 | "size" : "40x40", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-40@3x.png", 13 | "scale" : "3x", 14 | }, 15 | { 16 | "size" : "60x60", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-60@3x.png", 19 | "scale" : "3x", 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-40@2x.png", 25 | "scale" : "2x", 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-60@2x.png", 31 | "scale" : "2x", 32 | }, 33 | { 34 | "size" : "29x29", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-Small@2x.png", 37 | "scale" : "2x", 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "ipad", 42 | "filename" : "Icon-40~iPad.png", 43 | "scale" : "1x", 44 | }, 45 | { 46 | "size" : "40x40", 47 | "idiom" : "ipad", 48 | "filename" : "Icon-40~iPad@2x.png", 49 | "scale" : "2x", 50 | }, 51 | { 52 | "size" : "76x76", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-76.png", 55 | "scale" : "1x", 56 | }, 57 | { 58 | "size" : "76x76", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-76@2x.png", 61 | "scale" : "2x", 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-Small~iPad.png", 67 | "scale" : "1x", 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-Small~iPad@2x.png", 73 | "scale" : "2x", 74 | }, 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | }, 80 | "properties" : { 81 | "pre-rendered" : false 82 | } 83 | } -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40~iPad.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40~iPad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-40~iPad@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small~iPad.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small~iPad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/AppIcon.appiconset/Icon-Small~iPad@2x.png -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/device-rotation.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "device-rotation.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/device-rotation.imageset/device-rotation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/device-rotation.imageset/device-rotation.pdf -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/feedback.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "feedback.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/feedback.imageset/feedback.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/feedback.imageset/feedback.pdf -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/page-reuse.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "page-reuse.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/page-reuse.imageset/page-reuse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/page-reuse.imageset/page-reuse.pdf -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/vulcan-salute.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "vulcan-salute.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Images.xcassets/vulcan-salute.imageset/vulcan-salute.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davdroman/DRPageScrollView/c2b561ec89023b32fe66ecae3d4e95bf1d77f41e/DRPageScrollViewDemo/Images.xcassets/vulcan-salute.imageset/vulcan-salute.pdf -------------------------------------------------------------------------------- /DRPageScrollViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | me.davidroman.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | DRPSV 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // DRPageScrollView 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.m 3 | // DRPageScrollView 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // Copyright (c) 2015 David Román Aguirre. All rights reserved. 7 | // 8 | 9 | #import "MainViewController.h" 10 | 11 | #import "DRPageScrollView.h" 12 | 13 | @interface MainViewController () 14 | 15 | @property (nonatomic, strong) DRPageScrollView *pageScrollView; 16 | 17 | @end 18 | 19 | @implementation MainViewController 20 | 21 | - (instancetype)init { 22 | if (self = [super init]) { 23 | self.pageScrollView = [DRPageScrollView new]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | self.title = @"DRPageScrollView"; 33 | self.view.backgroundColor = [UIColor whiteColor]; 34 | 35 | self.pageScrollView.pageReuseEnabled = YES; 36 | [self.view addSubview:self.pageScrollView]; 37 | applyConstraints(self.pageScrollView); 38 | 39 | // Note: you can either take this nib approach or directly instantiate your UI elements programatically and add them to pageView. 40 | 41 | NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:@"PageViews" owner:self options:nil]; 42 | 43 | for (UIView *pv in nibViews) { 44 | [self.pageScrollView addPageWithHandler:^(UIView *pageView) { 45 | [pageView addSubview:pv]; 46 | applyConstraints(pv); 47 | }]; 48 | } 49 | } 50 | 51 | void applyConstraints(UIView *view) { 52 | view.translatesAutoresizingMaskIntoConstraints = NO; 53 | 54 | NSArray *attributeArray = @[@(NSLayoutAttributeTop), @(NSLayoutAttributeLeft), @(NSLayoutAttributeBottom), @(NSLayoutAttributeRight)]; 55 | 56 | for (NSNumber *attributeNumber in attributeArray) { 57 | NSLayoutAttribute attribute = (NSLayoutAttribute)[attributeNumber integerValue]; 58 | 59 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view attribute:attribute relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:attribute multiplier:1 constant:0]; 60 | 61 | [view.superview addConstraint:constraint]; 62 | } 63 | } 64 | 65 | - (void)viewDidAppear:(BOOL)animated { 66 | [super viewDidAppear:animated]; 67 | 68 | // Uncomment for a nice page to page jumping demo 69 | // [self performSelector:@selector(jumpToRandomPagesIndefinitely) withObject:nil afterDelay:1]; 70 | } 71 | 72 | - (void)jumpToRandomPagesIndefinitely { 73 | NSInteger randomPage = arc4random() % self.pageScrollView.numberOfPages; 74 | 75 | [UIView animateWithDuration:1 animations:^{ 76 | self.pageScrollView.currentPage = randomPage; 77 | } completion:^(BOOL finished) { 78 | [self performSelector:@selector(jumpToRandomPagesIndefinitely) withObject:nil afterDelay:1]; 79 | }]; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/PageViews.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 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 | 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 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /DRPageScrollViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DRPageScrollViewDemo 4 | // 5 | // Created by David Román Aguirre on 3/4/15. 6 | // 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Roman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **THIS PROJECT IS NO LONGER MAINTAINED. HERE ARE SOME SUITABLE ALTERNATIVES:** 2 | > 3 | > - https://github.com/WenchaoD/FSPagerView 4 | > - https://github.com/rechsteiner/Parchment 5 | > - https://github.com/uias/Pageboy 6 | 7 | --- 8 | 9 | DRPageScrollView 10 | ================ 11 | 12 |

13 | GIF 1 14 |

15 | 16 | ## Features 17 | 18 | - __Block-driven__ page setup. 19 | - __Auto Layout__ friendly. 20 | - __Page reusability__ à la UITableView reusable cells. 21 | 22 | ## Installation 23 | 24 | #### CocoaPods 25 | 26 | ```ruby 27 | pod 'DRPageScrollView' 28 | ``` 29 | 30 | #### Manual 31 | 32 | Drag and copy all files in the [__DRPageScrollView__](DRPageScrollView) folder into your project. 33 | 34 | ## At a glance 35 | 36 | #### Enabling page reusability 37 | 38 | Enabling page reusability is ridiculously simple: 39 | 40 | ```objective-c 41 | pageScrollView.pageReuseEnabled = YES; 42 | ``` 43 | 44 | _Note: `pageReuseEnabled` is meant to be set to the desired value before adding any pages to the page scroll view._ 45 | 46 | #### Adding a page 47 | 48 | Using Auto Layout on the subviews is not compulsory, but it's highly recommended. For the sake of simplicity, the example below doesn't contain Auto Layout code. 49 | 50 | ```objective-c 51 | [pageScrollView addPageWithHandler:^(UIView *pageView) { 52 | UIView *squareView = [UIView new]; 53 | squareView.backgroundColor = [UIColor redColor]; 54 | squareView.frame = CGRectMake(50, 50, 100, 100); 55 | 56 | [pageView addSubview:squareView]; 57 | }]; 58 | ``` 59 | 60 | ## License 61 | 62 | DRPageScrollView is available under the MIT license. 63 | --------------------------------------------------------------------------------