├── .gitignore ├── Demos ├── SCGridViewDemo │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── SCAppDelegate.h │ ├── SCAppDelegate.m │ ├── SCExampleGridViewController.h │ ├── SCExampleGridViewController.m │ ├── SCGridViewDemo-Info.plist │ ├── SCGridViewDemo-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── SCPagingDemo │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── SCAppDelegate.h │ ├── SCAppDelegate.m │ ├── SCDemoPageViewController.h │ ├── SCDemoPageViewController.m │ ├── SCPageOneViewController.h │ ├── SCPageOneViewController.m │ ├── SCPageTwoViewController.h │ ├── SCPageTwoViewController.m │ ├── SCPagingDemo-Info.plist │ ├── SCPagingDemo-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── SCPagingGridDemo │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── SCAppDelegate.h │ ├── SCAppDelegate.m │ ├── SCExamplePagingGridViewController.h │ ├── SCExamplePagingGridViewController.m │ ├── SCPagingGridDemo-Info.plist │ ├── SCPagingGridDemo-Prefix.pch │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── README.markdown ├── SCPagingGridView.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── SCPagingDemo.xcscheme │ └── SCPagingGridDemo.xcscheme └── Source ├── Base ├── SCViewController.h └── SCViewController.m ├── SCGridView.h ├── SCGridView.m ├── SCGridViewController.h ├── SCGridViewController.m ├── SCGridViewDelegate.h ├── SCPageIndicatorProtocol.h ├── SCPageIndicatorView.h ├── SCPageIndicatorView.m ├── SCPageView.h ├── SCPageView.m ├── SCPageViewController.h ├── SCPageViewController.m ├── SCPageViewDelegate.h ├── SCPagingGridViewController.h ├── SCPagingGridViewController.m ├── SCSwizzle.h ├── SCSwizzle.m ├── SCViewRecycler.h └── SCViewRecycler.m /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Xcode 3 | .DS_Store 4 | build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | *.xcworkspace 14 | !default.xcworkspace 15 | xcuserdata 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | .idea/ 20 | 21 | # OSX 22 | .AppleDouble 23 | .LSOverride 24 | Icon 25 | 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | # Files that might appear on external disk 31 | .Spotlight-V100 32 | .Trashes 33 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCGridViewDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCGridViewDemo/Default.png -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCGridViewDemo/Default@2x.png -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAppDelegate.h 3 | // SCGridViewDemo 4 | // 5 | // Created by Jesse Andersen on 11/1/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAppDelegate.m 3 | // SCGridViewDemo 4 | // 5 | // Created by Jesse Andersen on 11/1/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import "SCAppDelegate.h" 10 | #import "SCExampleGridViewController.h" 11 | 12 | @implementation SCAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | self.window.backgroundColor = [UIColor whiteColor]; 18 | self.window.rootViewController = [[SCExampleGridViewController alloc] init]; 19 | [self.window makeKeyAndVisible]; 20 | return YES; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCExampleGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCExampleGridViewController.h 3 | // SCPagingGridView 4 | // 5 | // Created by Jesse Andersen on 11/1/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import "SCViewController.h" 10 | 11 | @interface SCExampleGridViewController : SCViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCExampleGridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCExampleGridViewController.m 3 | // SCPagingGridView 4 | // 5 | // Created by Jesse Andersen on 11/1/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import "SCExampleGridViewController.h" 10 | #import "SCGridView.h" 11 | #import 12 | 13 | @interface SCExampleGridViewController () 14 | 15 | @end 16 | 17 | @implementation SCExampleGridViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | self.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; 22 | 23 | CGFloat height = floorf(self.view.bounds.size.height * .8f); 24 | CGFloat width = floorf(self.view.bounds.size.width * .8f); 25 | 26 | SCGridView *grid = [[SCGridView alloc] initWithFrame:CGRectMake(floorf((self.view.bounds.size.width - width)/2.0f), floorf((self.view.bounds.size.height - height)/2.0f), width, height)]; 27 | grid.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 28 | grid.layer.cornerRadius = 6.0f; 29 | grid.clipsToBounds = YES; 30 | grid.backgroundColor = [UIColor clearColor]; 31 | grid.schema = @[ @(1), @(1), @(2) ]; 32 | grid.rowSpacing = 5.0f; 33 | grid.colSpacing = 5.0f; 34 | [self.view addSubview:grid]; 35 | 36 | NSMutableArray *cells = [[NSMutableArray alloc] initWithCapacity:grid.size]; 37 | UILabel *label = [[UILabel alloc] init]; 38 | label.textAlignment = UITextAlignmentCenter; 39 | label.text = @"1st cell"; 40 | [cells addObject:label]; 41 | 42 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 43 | [button setTitle:@"2nd cell" forState:UIControlStateNormal]; 44 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 45 | [button setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted]; 46 | button.backgroundColor = [UIColor whiteColor]; 47 | [cells addObject:button]; 48 | 49 | label = [[UILabel alloc] init]; 50 | label.numberOfLines = 0; 51 | label.textAlignment = UITextAlignmentCenter; 52 | label.text = @"3rd cell"; 53 | [cells addObject:label]; 54 | 55 | label = [[UILabel alloc] init]; 56 | label.numberOfLines = 0; 57 | label.textAlignment = UITextAlignmentCenter; 58 | label.text = @"4th cell"; 59 | [cells addObject:label]; 60 | 61 | grid.cells = cells; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCGridViewDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.scribd.${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 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/SCGridViewDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SCGridViewDemo' target in the 'SCGridViewDemo' 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 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demos/SCGridViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCGridViewDemo 4 | // 5 | // Created by Jesse Andersen on 11/1/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "SCAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SCAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /Demos/SCPagingDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingDemo/Default.png -------------------------------------------------------------------------------- /Demos/SCPagingDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingDemo/Default@2x.png -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCAppDelegate : UIResponder 27 | 28 | @property (strong, nonatomic) UIWindow *window; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCAppDelegate.h" 25 | #import "SCDemoPageViewController.h" 26 | 27 | @implementation SCAppDelegate 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 30 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 31 | // Override point for customization after application launch. 32 | self.window.backgroundColor = [UIColor whiteColor]; 33 | self.window.rootViewController = [[SCDemoPageViewController alloc] init]; 34 | [self.window makeKeyAndVisible]; 35 | return YES; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCDemoPageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageViewController.h" 25 | 26 | @interface SCDemoPageViewController : SCPageViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCDemoPageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCDemoPageViewController.h" 25 | #import "SCPageOneViewController.h" 26 | #import "SCPageTwoViewController.h" 27 | 28 | @interface SCDemoPageViewController () 29 | 30 | @end 31 | 32 | @implementation SCDemoPageViewController 33 | 34 | - (id)init { 35 | if (self = [super init]) { 36 | UIViewController *pageOne = [[SCPageOneViewController alloc] init]; 37 | UIViewController *pageTwo = [[SCPageTwoViewController alloc] init]; 38 | self.pages = @[ [[UINavigationController alloc] initWithRootViewController:pageOne], [[UINavigationController alloc] initWithRootViewController:pageTwo] ]; 39 | } 40 | return self; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPageOneViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCPageOneViewController : UIViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPageOneViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageOneViewController.h" 25 | #import "SCPageViewController.h" 26 | #import "SCPageView.h" 27 | 28 | @interface SCPageOneViewController () 29 | 30 | @property (nonatomic, weak) UIButton *directionButton; 31 | 32 | @end 33 | 34 | @implementation SCPageOneViewController 35 | 36 | - (id)init { 37 | if (self = [super init]) { 38 | self.title = @"Page One"; 39 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Page Two" style:UIBarButtonItemStylePlain target:self action:@selector(_pageTwoTapped:)]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | self.view.backgroundColor = [UIColor redColor]; 47 | 48 | UIButton *directionButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 49 | [directionButton addTarget:self action:@selector(_directionTapped:) forControlEvents:UIControlEventTouchUpInside]; 50 | directionButton.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin; 51 | [self.view addSubview:directionButton]; 52 | self.directionButton = directionButton; 53 | [self _updateDirectionButton]; 54 | } 55 | 56 | - (void)viewWillAppear:(BOOL)animated { 57 | [super viewWillAppear:animated]; 58 | self.directionButton.center = self.view.center; 59 | } 60 | 61 | #pragma mark - Button Actions 62 | 63 | - (void)_pageTwoTapped:(id)sender { 64 | [self.pageViewController.pageView setCurrentPageNumber:1 animated:YES fast:NO]; 65 | } 66 | 67 | - (void)_directionTapped:(id)sender { 68 | self.pageViewController.pageView.direction = self.pageViewController.pageView.direction == SCPagingDirectionHorizontal ? SCPagingDirectionVertical : SCPagingDirectionHorizontal; 69 | [self _updateDirectionButton]; 70 | } 71 | 72 | - (void)_updateDirectionButton { 73 | if (self.pageViewController.pageView.direction == SCPagingDirectionHorizontal) { 74 | [self.directionButton setTitle:@"Switch to Vertical" forState:UIControlStateNormal]; 75 | } else { 76 | [self.directionButton setTitle:@"Switch to Horizontal" forState:UIControlStateNormal]; 77 | } 78 | [self.directionButton sizeToFit]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPageTwoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCPageTwoViewController : UIViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPageTwoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageTwoViewController.h" 25 | 26 | @interface SCPageTwoViewController () 27 | 28 | @end 29 | 30 | @implementation SCPageTwoViewController 31 | 32 | - (id)init { 33 | if (self = [super init]) { 34 | self.title = @"Page Two"; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | self.view.backgroundColor = [UIColor blueColor]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPagingDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.scribd.${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 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/SCPagingDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SCPagingExample' target in the 'SCPagingExample' 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 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demos/SCPagingDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCPagingExample 4 | // 5 | // Created by Jesse Andersen on 10/30/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "SCAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SCAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingGridDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingGridDemo/Default.png -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scribd/SCPagingGridView/c6aea6ca836e566835acb97bbf39b88a4ec1de78/Demos/SCPagingGridDemo/Default@2x.png -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCAppDelegate : UIResponder 27 | 28 | @property (strong, nonatomic) UIWindow *window; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCAppDelegate.h" 25 | #import "SCExamplePagingGridViewController.h" 26 | 27 | @interface SCAppDelegate () 28 | 29 | @property (nonatomic, strong) SCPagingGridViewController *pagingGridView; 30 | 31 | @end 32 | 33 | @implementation SCAppDelegate 34 | 35 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 36 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 37 | self.window.backgroundColor = [UIColor blackColor]; 38 | [self.window makeKeyAndVisible]; 39 | 40 | self.pagingGridView = [[SCExamplePagingGridViewController alloc] init]; 41 | self.window.rootViewController = self.pagingGridView; 42 | 43 | return YES; 44 | } 45 | 46 | @end -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCExamplePagingGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPagingGridViewController.h" 25 | 26 | @interface SCExamplePagingGridViewController : SCPagingGridViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCExamplePagingGridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCExamplePagingGridViewController.h" 25 | #import "SCGridView.h" 26 | #import "SCPageView.h" 27 | #import "SCPageIndicatorView.h" 28 | 29 | @interface SCExamplePagingGridViewController () 30 | 31 | @end 32 | 33 | @implementation SCExamplePagingGridViewController 34 | 35 | - (void)viewDidLoad { 36 | [super viewDidLoad]; 37 | 38 | [self showPageIndicator]; 39 | 40 | self.pageView.gapBetweenPages = 2; 41 | self.pageView.nextGapView.backgroundColor = [UIColor blackColor]; 42 | self.pageView.previousGapView.backgroundColor = self.pageView.nextGapView.backgroundColor; 43 | 44 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 45 | self.schema = self.schema = @[ @[@(3), @(2), @(3)], @[@(2), @(3), @(2)], @[@(3), @(3), @(3)] ]; 46 | } else { 47 | self.schema = self.schema = @[ @[@(2), @(1), @(2)], @[@(1), @(2), @(1)], @[@(2), @(2)] ]; 48 | } 49 | } 50 | 51 | #pragma mark - SCPagingGridView 52 | 53 | - (NSInteger)numberOfCellsInPageView:(SCPageView *)pageView { 54 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 55 | return 200; 56 | } else { 57 | return 60; 58 | } 59 | } 60 | 61 | - (void)configureGridView:(SCGridView *)gridView forPageNumber:(NSUInteger)pageNumber { 62 | gridView.backgroundColor = [UIColor blackColor]; 63 | gridView.rowSpacing = 2; 64 | gridView.colSpacing = 2; 65 | } 66 | 67 | - (Class)cellClass { 68 | return [UILabel class]; 69 | } 70 | 71 | - (void)configureCell:(UIView *)cell atPosition:(NSUInteger)position { 72 | if ([cell isKindOfClass:[UILabel class]]) { 73 | UILabel *label = (UILabel *)cell; 74 | label.autoresizingMask = UIViewAutoresizingNone; 75 | label.textAlignment = UITextAlignmentCenter; 76 | label.font = [UIFont boldSystemFontOfSize:20]; 77 | 78 | CGFloat red = (CGFloat)arc4random() / 0x100000000; 79 | CGFloat green = (CGFloat)arc4random() / 0x100000000; 80 | CGFloat blue = (CGFloat)arc4random() / 0x100000000; 81 | label.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f]; 82 | label.text = [@(position) stringValue]; 83 | } 84 | } 85 | 86 | - (void)didSelectCell:(UIView *)cell atPosition:(NSUInteger)position { 87 | [[[UIAlertView alloc] initWithTitle:@"BAM!" message:[NSString stringWithFormat:@"Tapped cell at position: %d", position] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil] show]; 88 | } 89 | 90 | - (void)configurePageIndicator:(SCPageIndicatorView *)pageIndicatorView { 91 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 92 | pageIndicatorView.dotSize = CGSizeMake(12.0f, 12.0f); 93 | pageIndicatorView.dotLineWidth = 2.0f; 94 | pageIndicatorView.dotPadding = 6.0f; 95 | pageIndicatorView.dotColor = [UIColor whiteColor]; 96 | } 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCPagingGridDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.scribd.${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 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/SCPagingGridDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SCPagingGrid' target in the 'SCPagingGrid' 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 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demos/SCPagingGridDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCPagingGrid 4 | // 5 | // Created by Jesse Andersen on 10/29/12. 6 | // Copyright (c) 2012 Scribd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "SCAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SCAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | SCPagingGridView 2 | === 3 | 4 | SCPagingGridView is a collection of iOS UIViewController containers, views, & helpers. The project is comprised of the following components: 5 | 6 | SCPageViewController 7 | --- 8 | A custom page view controller that supports both horizontal & vertical paging. 9 | 10 | SCGridView 11 | --- 12 | A UIView sublcass that lays out its children according to a given schema. 13 | 14 | Screen shot: 15 | 16 | iPhone example 17 | 18 | Sample Code: 19 | 20 | ``` 21 | @implementation SCExampleGridViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | self.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; 26 | 27 | CGFloat height = floorf(self.view.bounds.size.height * .8f); 28 | CGFloat width = floorf(self.view.bounds.size.width * .8f); 29 | 30 | SCGridView *grid = [[SCGridView alloc] initWithFrame:CGRectMake(floorf((self.view.bounds.size.width - width)/2.0f), floorf((self.view.bounds.size.height - height)/2.0f), width, height)]; 31 | grid.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 32 | grid.layer.cornerRadius = 6.0f; 33 | grid.clipsToBounds = YES; 34 | grid.backgroundColor = [UIColor clearColor]; 35 | grid.schema = @[ @(1), @(1), @(2) ]; 36 | grid.rowSpacing = 5.0f; 37 | grid.colSpacing = 5.0f; 38 | [self.view addSubview:grid]; 39 | 40 | NSMutableArray *cells = [[NSMutableArray alloc] initWithCapacity:grid.size]; 41 | 42 | UILabel *label = [[UILabel alloc] init]; 43 | label.textAlignment = UITextAlignmentCenter; 44 | label.text = @"1st cell"; 45 | [cells addObject:label]; 46 | 47 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 48 | [button setTitle:@"2nd cell" forState:UIControlStateNormal]; 49 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 50 | [button setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted]; 51 | button.backgroundColor = [UIColor whiteColor]; 52 | [cells addObject:button]; 53 | 54 | label = [[UILabel alloc] init]; 55 | label.numberOfLines = 0; 56 | label.textAlignment = UITextAlignmentCenter; 57 | label.text = @"3rd cell"; 58 | [cells addObject:label]; 59 | 60 | label = [[UILabel alloc] init]; 61 | label.numberOfLines = 0; 62 | label.textAlignment = UITextAlignmentCenter; 63 | label.text = @"4th cell"; 64 | [cells addObject:label]; 65 | 66 | grid.cells = cells; 67 | } 68 | 69 | @end 70 | ``` 71 | 72 | SCPagingGridViewController 73 | --- 74 | A view controller container that supports laying out a series of cells in pageable grid views. 75 | 76 | Screen shot: 77 | 78 | iPhone example 79 | 80 | SCViewRecycler 81 | --- 82 | A class that recycles views. It is similar to UITableView's reusable cells system, but can be used for any view type. 83 | 84 | Usage 85 | --- 86 | 87 | To use SCPagingGridView, add the ``` source/ ``` files into your XCode Project. The preferred method is to setup a git submodule and reference the files in your Xcode project. ` git submodule add https://github.com/scribd/SCPagingGridView.git SCPagingGridView ` 88 | 89 | Requirements 90 | --- 91 | 92 | SCPagingGridView requires iOS 5.0+ and Xcode 4.3+ The projects uses ARC, but it may be used with non-ARC projects by setting the: ` -fobjc-arc ` compiler flag on the ` *.m ` files. You can set this flag under Target -> Build Phases -> Compile Sources 93 | 94 | Apps 95 | --- 96 | SCPagingGridView is used in the following apps: 97 | 98 | * Scribd - [http://itunes.apple.com/us/app/scribd-worlds-largest-online/id542557212?ls=1&mt=8](http://itunes.apple.com/us/app/scribd-worlds-largest-online/id542557212?ls=1&mt=8) 99 | 100 | License 101 | --- 102 | 103 | ``` 104 | 105 | Permission is hereby granted, free of charge, to any person obtaining a copy of 106 | this software and associated documentation files (the "Software"), to deal in 107 | the Software without restriction, including without limitation the rights to 108 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 109 | of the Software, and to permit persons to whom the Software is furnished to do 110 | so, subject to the following conditions: 111 | 112 | The above copyright notice and this permission notice shall be included in all 113 | copies or substantial portions of the Software. 114 | 115 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 116 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 117 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 118 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 119 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 120 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 121 | SOFTWARE. 122 | 123 | ``` -------------------------------------------------------------------------------- /SCPagingGridView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 950436F7163F25E800BF33D4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F6163F25E800BF33D4 /* UIKit.framework */; }; 11 | 950436F9163F25E800BF33D4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F8163F25E800BF33D4 /* Foundation.framework */; }; 12 | 950436FB163F25E800BF33D4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436FA163F25E800BF33D4 /* CoreGraphics.framework */; }; 13 | 950438321640968200BF33D4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F6163F25E800BF33D4 /* UIKit.framework */; }; 14 | 950438331640968200BF33D4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F8163F25E800BF33D4 /* Foundation.framework */; }; 15 | 950438341640968200BF33D4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436FA163F25E800BF33D4 /* CoreGraphics.framework */; }; 16 | 950438B816409A4F00BF33D4 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438AC16409A4F00BF33D4 /* Default-568h@2x.png */; }; 17 | 950438B916409A4F00BF33D4 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438AD16409A4F00BF33D4 /* Default.png */; }; 18 | 950438BA16409A4F00BF33D4 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438AE16409A4F00BF33D4 /* Default@2x.png */; }; 19 | 950438BB16409A4F00BF33D4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 950438AF16409A4F00BF33D4 /* InfoPlist.strings */; }; 20 | 950438BC16409A4F00BF33D4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 950438B116409A4F00BF33D4 /* main.m */; }; 21 | 950438BD16409A4F00BF33D4 /* SCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 950438B316409A4F00BF33D4 /* SCAppDelegate.m */; }; 22 | 950438BE16409A4F00BF33D4 /* SCExamplePagingGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 950438B516409A4F00BF33D4 /* SCExamplePagingGridViewController.m */; }; 23 | 950438CB16409A6000BF33D4 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438C116409A6000BF33D4 /* Default-568h@2x.png */; }; 24 | 950438CC16409A6000BF33D4 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438C216409A6000BF33D4 /* Default.png */; }; 25 | 950438CD16409A6000BF33D4 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 950438C316409A6000BF33D4 /* Default@2x.png */; }; 26 | 950438CE16409A6000BF33D4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 950438C416409A6000BF33D4 /* InfoPlist.strings */; }; 27 | 950438CF16409A6000BF33D4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 950438C616409A6000BF33D4 /* main.m */; }; 28 | 950438D016409A6000BF33D4 /* SCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 950438C816409A6000BF33D4 /* SCAppDelegate.m */; }; 29 | 9531DBDD1643068C0063629F /* SCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DBDC1643068C0063629F /* SCViewController.m */; }; 30 | 9531DBDE1643068C0063629F /* SCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DBDC1643068C0063629F /* SCViewController.m */; }; 31 | 9531DC40164335540063629F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F6163F25E800BF33D4 /* UIKit.framework */; }; 32 | 9531DC41164335540063629F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436F8163F25E800BF33D4 /* Foundation.framework */; }; 33 | 9531DC42164335540063629F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950436FA163F25E800BF33D4 /* CoreGraphics.framework */; }; 34 | 9531DC58164335910063629F /* SCViewController.h in Sources */ = {isa = PBXBuildFile; fileRef = 9531DBDB1643068C0063629F /* SCViewController.h */; }; 35 | 9531DC59164335910063629F /* SCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DBDC1643068C0063629F /* SCViewController.m */; }; 36 | 9531DC5A164335910063629F /* SCSwizzle.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7516409B6900BBB572 /* SCSwizzle.h */; }; 37 | 9531DC5B164335910063629F /* SCSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7616409B6900BBB572 /* SCSwizzle.m */; }; 38 | 9531DC5C164335910063629F /* SCViewRecycler.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7716409B6900BBB572 /* SCViewRecycler.h */; }; 39 | 9531DC5D164335910063629F /* SCViewRecycler.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7816409B6900BBB572 /* SCViewRecycler.m */; }; 40 | 9531DC5E164335910063629F /* SCGridView.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7D16409B8100BBB572 /* SCGridView.h */; }; 41 | 9531DC5F164335910063629F /* SCGridView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7E16409B8100BBB572 /* SCGridView.m */; }; 42 | 9531DC60164335910063629F /* SCGridViewController.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7F16409B8100BBB572 /* SCGridViewController.h */; }; 43 | 9531DC61164335910063629F /* SCGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8016409B8100BBB572 /* SCGridViewController.m */; }; 44 | 9531DC62164335910063629F /* SCGridViewDelegate.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8116409B8100BBB572 /* SCGridViewDelegate.h */; }; 45 | 9531DC63164335910063629F /* SCPageIndicatorProtocol.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8616409B9400BBB572 /* SCPageIndicatorProtocol.h */; }; 46 | 9531DC64164335910063629F /* SCPageIndicatorView.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8716409B9400BBB572 /* SCPageIndicatorView.h */; }; 47 | 9531DC65164335910063629F /* SCPageIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8816409B9400BBB572 /* SCPageIndicatorView.m */; }; 48 | 9531DC66164335910063629F /* SCPageView.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8916409B9400BBB572 /* SCPageView.h */; }; 49 | 9531DC67164335910063629F /* SCPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8A16409B9400BBB572 /* SCPageView.m */; }; 50 | 9531DC68164335910063629F /* SCPageViewController.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8B16409B9400BBB572 /* SCPageViewController.h */; }; 51 | 9531DC69164335910063629F /* SCPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8C16409B9400BBB572 /* SCPageViewController.m */; }; 52 | 9531DC6A164335910063629F /* SCPageViewDelegate.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8D16409B9400BBB572 /* SCPageViewDelegate.h */; }; 53 | 9531DC6B164335910063629F /* SCPagingGridViewController.h in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA9416409BA400BBB572 /* SCPagingGridViewController.h */; }; 54 | 9531DC6C164335910063629F /* SCPagingGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA9516409BA400BBB572 /* SCPagingGridViewController.m */; }; 55 | 9531DC78164335E90063629F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9531DC6E164335E90063629F /* Default-568h@2x.png */; }; 56 | 9531DC79164335E90063629F /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 9531DC6F164335E90063629F /* Default.png */; }; 57 | 9531DC7A164335E90063629F /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9531DC70164335E90063629F /* Default@2x.png */; }; 58 | 9531DC7B164335E90063629F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9531DC71164335E90063629F /* InfoPlist.strings */; }; 59 | 9531DC7C164335E90063629F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DC73164335E90063629F /* main.m */; }; 60 | 9531DC7D164335E90063629F /* SCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DC75164335E90063629F /* SCAppDelegate.m */; }; 61 | 9531DC821643364A0063629F /* SCExampleGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9531DC811643364A0063629F /* SCExampleGridViewController.m */; }; 62 | 95FFCA7916409B6900BBB572 /* SCSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7616409B6900BBB572 /* SCSwizzle.m */; }; 63 | 95FFCA7A16409B6900BBB572 /* SCSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7616409B6900BBB572 /* SCSwizzle.m */; }; 64 | 95FFCA7B16409B6900BBB572 /* SCViewRecycler.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7816409B6900BBB572 /* SCViewRecycler.m */; }; 65 | 95FFCA7C16409B6900BBB572 /* SCViewRecycler.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7816409B6900BBB572 /* SCViewRecycler.m */; }; 66 | 95FFCA8216409B8100BBB572 /* SCGridView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7E16409B8100BBB572 /* SCGridView.m */; }; 67 | 95FFCA8316409B8100BBB572 /* SCGridView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA7E16409B8100BBB572 /* SCGridView.m */; }; 68 | 95FFCA8416409B8100BBB572 /* SCGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8016409B8100BBB572 /* SCGridViewController.m */; }; 69 | 95FFCA8516409B8100BBB572 /* SCGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8016409B8100BBB572 /* SCGridViewController.m */; }; 70 | 95FFCA8E16409B9400BBB572 /* SCPageIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8816409B9400BBB572 /* SCPageIndicatorView.m */; }; 71 | 95FFCA8F16409B9400BBB572 /* SCPageIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8816409B9400BBB572 /* SCPageIndicatorView.m */; }; 72 | 95FFCA9016409B9400BBB572 /* SCPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8A16409B9400BBB572 /* SCPageView.m */; }; 73 | 95FFCA9116409B9400BBB572 /* SCPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8A16409B9400BBB572 /* SCPageView.m */; }; 74 | 95FFCA9216409B9400BBB572 /* SCPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8C16409B9400BBB572 /* SCPageViewController.m */; }; 75 | 95FFCA9316409B9400BBB572 /* SCPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA8C16409B9400BBB572 /* SCPageViewController.m */; }; 76 | 95FFCA9616409BA400BBB572 /* SCPagingGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA9516409BA400BBB572 /* SCPagingGridViewController.m */; }; 77 | 95FFCA9716409BA400BBB572 /* SCPagingGridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA9516409BA400BBB572 /* SCPagingGridViewController.m */; }; 78 | 95FFCA9A16409F4000BBB572 /* SCDemoPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCA9916409F4000BBB572 /* SCDemoPageViewController.m */; }; 79 | 95FFCAB11640A16100BBB572 /* SCPageOneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCAB01640A16100BBB572 /* SCPageOneViewController.m */; }; 80 | 95FFCAB41640A1C400BBB572 /* SCPageTwoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95FFCAB31640A1C400BBB572 /* SCPageTwoViewController.m */; }; 81 | /* End PBXBuildFile section */ 82 | 83 | /* Begin PBXFileReference section */ 84 | 950436F2163F25E800BF33D4 /* SCPagingGridView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCPagingGridView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | 950436F6163F25E800BF33D4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 86 | 950436F8163F25E800BF33D4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 87 | 950436FA163F25E800BF33D4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 88 | 950438301640968200BF33D4 /* SCPagingDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCPagingDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 950438AC16409A4F00BF33D4 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 90 | 950438AD16409A4F00BF33D4 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 91 | 950438AE16409A4F00BF33D4 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 92 | 950438B016409A4F00BF33D4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 93 | 950438B116409A4F00BF33D4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 94 | 950438B216409A4F00BF33D4 /* SCAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAppDelegate.h; sourceTree = ""; }; 95 | 950438B316409A4F00BF33D4 /* SCAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAppDelegate.m; sourceTree = ""; }; 96 | 950438B416409A4F00BF33D4 /* SCExamplePagingGridViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCExamplePagingGridViewController.h; sourceTree = ""; }; 97 | 950438B516409A4F00BF33D4 /* SCExamplePagingGridViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCExamplePagingGridViewController.m; sourceTree = ""; }; 98 | 950438B616409A4F00BF33D4 /* SCPagingGridDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SCPagingGridDemo-Info.plist"; sourceTree = ""; }; 99 | 950438B716409A4F00BF33D4 /* SCPagingGridDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SCPagingGridDemo-Prefix.pch"; sourceTree = ""; }; 100 | 950438C116409A6000BF33D4 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 101 | 950438C216409A6000BF33D4 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 102 | 950438C316409A6000BF33D4 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 103 | 950438C516409A6000BF33D4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 104 | 950438C616409A6000BF33D4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 105 | 950438C716409A6000BF33D4 /* SCAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAppDelegate.h; sourceTree = ""; }; 106 | 950438C816409A6000BF33D4 /* SCAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAppDelegate.m; sourceTree = ""; }; 107 | 950438C916409A6000BF33D4 /* SCPagingDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SCPagingDemo-Info.plist"; sourceTree = ""; }; 108 | 950438CA16409A6000BF33D4 /* SCPagingDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SCPagingDemo-Prefix.pch"; sourceTree = ""; }; 109 | 9531DBDB1643068C0063629F /* SCViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCViewController.h; path = Source/Base/SCViewController.h; sourceTree = SOURCE_ROOT; }; 110 | 9531DBDC1643068C0063629F /* SCViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCViewController.m; path = Source/Base/SCViewController.m; sourceTree = SOURCE_ROOT; }; 111 | 9531DC3E164335540063629F /* SCGridViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCGridViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | 9531DC6E164335E90063629F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 113 | 9531DC6F164335E90063629F /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 114 | 9531DC70164335E90063629F /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 115 | 9531DC72164335E90063629F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 116 | 9531DC73164335E90063629F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 117 | 9531DC74164335E90063629F /* SCAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAppDelegate.h; sourceTree = ""; }; 118 | 9531DC75164335E90063629F /* SCAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAppDelegate.m; sourceTree = ""; }; 119 | 9531DC76164335E90063629F /* SCGridViewDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SCGridViewDemo-Info.plist"; sourceTree = ""; }; 120 | 9531DC77164335E90063629F /* SCGridViewDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SCGridViewDemo-Prefix.pch"; sourceTree = ""; }; 121 | 9531DC801643364A0063629F /* SCExampleGridViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCExampleGridViewController.h; sourceTree = ""; }; 122 | 9531DC811643364A0063629F /* SCExampleGridViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCExampleGridViewController.m; sourceTree = ""; }; 123 | 95FFCA7516409B6900BBB572 /* SCSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCSwizzle.h; path = Source/SCSwizzle.h; sourceTree = SOURCE_ROOT; }; 124 | 95FFCA7616409B6900BBB572 /* SCSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCSwizzle.m; path = Source/SCSwizzle.m; sourceTree = SOURCE_ROOT; }; 125 | 95FFCA7716409B6900BBB572 /* SCViewRecycler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCViewRecycler.h; path = Source/SCViewRecycler.h; sourceTree = SOURCE_ROOT; }; 126 | 95FFCA7816409B6900BBB572 /* SCViewRecycler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCViewRecycler.m; path = Source/SCViewRecycler.m; sourceTree = SOURCE_ROOT; }; 127 | 95FFCA7D16409B8100BBB572 /* SCGridView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCGridView.h; path = Source/SCGridView.h; sourceTree = SOURCE_ROOT; }; 128 | 95FFCA7E16409B8100BBB572 /* SCGridView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCGridView.m; path = Source/SCGridView.m; sourceTree = SOURCE_ROOT; }; 129 | 95FFCA7F16409B8100BBB572 /* SCGridViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCGridViewController.h; path = Source/SCGridViewController.h; sourceTree = SOURCE_ROOT; }; 130 | 95FFCA8016409B8100BBB572 /* SCGridViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCGridViewController.m; path = Source/SCGridViewController.m; sourceTree = SOURCE_ROOT; }; 131 | 95FFCA8116409B8100BBB572 /* SCGridViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCGridViewDelegate.h; path = Source/SCGridViewDelegate.h; sourceTree = SOURCE_ROOT; }; 132 | 95FFCA8616409B9400BBB572 /* SCPageIndicatorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPageIndicatorProtocol.h; path = Source/SCPageIndicatorProtocol.h; sourceTree = SOURCE_ROOT; }; 133 | 95FFCA8716409B9400BBB572 /* SCPageIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPageIndicatorView.h; path = Source/SCPageIndicatorView.h; sourceTree = SOURCE_ROOT; }; 134 | 95FFCA8816409B9400BBB572 /* SCPageIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCPageIndicatorView.m; path = Source/SCPageIndicatorView.m; sourceTree = SOURCE_ROOT; }; 135 | 95FFCA8916409B9400BBB572 /* SCPageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPageView.h; path = Source/SCPageView.h; sourceTree = SOURCE_ROOT; }; 136 | 95FFCA8A16409B9400BBB572 /* SCPageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCPageView.m; path = Source/SCPageView.m; sourceTree = SOURCE_ROOT; }; 137 | 95FFCA8B16409B9400BBB572 /* SCPageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPageViewController.h; path = Source/SCPageViewController.h; sourceTree = SOURCE_ROOT; }; 138 | 95FFCA8C16409B9400BBB572 /* SCPageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCPageViewController.m; path = Source/SCPageViewController.m; sourceTree = SOURCE_ROOT; }; 139 | 95FFCA8D16409B9400BBB572 /* SCPageViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPageViewDelegate.h; path = Source/SCPageViewDelegate.h; sourceTree = SOURCE_ROOT; }; 140 | 95FFCA9416409BA400BBB572 /* SCPagingGridViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPagingGridViewController.h; path = Source/SCPagingGridViewController.h; sourceTree = SOURCE_ROOT; }; 141 | 95FFCA9516409BA400BBB572 /* SCPagingGridViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCPagingGridViewController.m; path = Source/SCPagingGridViewController.m; sourceTree = SOURCE_ROOT; }; 142 | 95FFCA9816409F4000BBB572 /* SCDemoPageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCDemoPageViewController.h; sourceTree = ""; }; 143 | 95FFCA9916409F4000BBB572 /* SCDemoPageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCDemoPageViewController.m; sourceTree = ""; }; 144 | 95FFCAAF1640A16100BBB572 /* SCPageOneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPageOneViewController.h; sourceTree = ""; }; 145 | 95FFCAB01640A16100BBB572 /* SCPageOneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPageOneViewController.m; sourceTree = ""; }; 146 | 95FFCAB21640A1C400BBB572 /* SCPageTwoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPageTwoViewController.h; sourceTree = ""; }; 147 | 95FFCAB31640A1C400BBB572 /* SCPageTwoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPageTwoViewController.m; sourceTree = ""; }; 148 | /* End PBXFileReference section */ 149 | 150 | /* Begin PBXFrameworksBuildPhase section */ 151 | 950436EF163F25E800BF33D4 /* Frameworks */ = { 152 | isa = PBXFrameworksBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 950436F7163F25E800BF33D4 /* UIKit.framework in Frameworks */, 156 | 950436F9163F25E800BF33D4 /* Foundation.framework in Frameworks */, 157 | 950436FB163F25E800BF33D4 /* CoreGraphics.framework in Frameworks */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | 9504382D1640968200BF33D4 /* Frameworks */ = { 162 | isa = PBXFrameworksBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 950438321640968200BF33D4 /* UIKit.framework in Frameworks */, 166 | 950438331640968200BF33D4 /* Foundation.framework in Frameworks */, 167 | 950438341640968200BF33D4 /* CoreGraphics.framework in Frameworks */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | 9531DC3B164335540063629F /* Frameworks */ = { 172 | isa = PBXFrameworksBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 9531DC40164335540063629F /* UIKit.framework in Frameworks */, 176 | 9531DC41164335540063629F /* Foundation.framework in Frameworks */, 177 | 9531DC42164335540063629F /* CoreGraphics.framework in Frameworks */, 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | }; 181 | /* End PBXFrameworksBuildPhase section */ 182 | 183 | /* Begin PBXGroup section */ 184 | 950436E7163F25E800BF33D4 = { 185 | isa = PBXGroup; 186 | children = ( 187 | 9504371D163F272F00BF33D4 /* Source */, 188 | 9504384C164096BA00BF33D4 /* Demos */, 189 | 950436F5163F25E800BF33D4 /* Frameworks */, 190 | 950436F3163F25E800BF33D4 /* Products */, 191 | ); 192 | sourceTree = ""; 193 | }; 194 | 950436F3163F25E800BF33D4 /* Products */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 950436F2163F25E800BF33D4 /* SCPagingGridView.app */, 198 | 950438301640968200BF33D4 /* SCPagingDemo.app */, 199 | 9531DC3E164335540063629F /* SCGridViewDemo.app */, 200 | ); 201 | name = Products; 202 | sourceTree = ""; 203 | }; 204 | 950436F5163F25E800BF33D4 /* Frameworks */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 950436F6163F25E800BF33D4 /* UIKit.framework */, 208 | 950436F8163F25E800BF33D4 /* Foundation.framework */, 209 | 950436FA163F25E800BF33D4 /* CoreGraphics.framework */, 210 | ); 211 | name = Frameworks; 212 | sourceTree = ""; 213 | }; 214 | 9504371D163F272F00BF33D4 /* Source */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 9531DBD9164306770063629F /* Base */, 218 | 9504373A163F29EA00BF33D4 /* Helpers */, 219 | 9504373C163F2A8800BF33D4 /* Grid View */, 220 | 9504373B163F2A7B00BF33D4 /* Page View */, 221 | 9504373D163F2A9E00BF33D4 /* Paging Grid View */, 222 | ); 223 | name = Source; 224 | path = SCPagingGrid/Source; 225 | sourceTree = ""; 226 | }; 227 | 9504373A163F29EA00BF33D4 /* Helpers */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 95FFCA7516409B6900BBB572 /* SCSwizzle.h */, 231 | 95FFCA7616409B6900BBB572 /* SCSwizzle.m */, 232 | 95FFCA7716409B6900BBB572 /* SCViewRecycler.h */, 233 | 95FFCA7816409B6900BBB572 /* SCViewRecycler.m */, 234 | ); 235 | name = Helpers; 236 | sourceTree = ""; 237 | }; 238 | 9504373B163F2A7B00BF33D4 /* Page View */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 95FFCA8616409B9400BBB572 /* SCPageIndicatorProtocol.h */, 242 | 95FFCA8716409B9400BBB572 /* SCPageIndicatorView.h */, 243 | 95FFCA8816409B9400BBB572 /* SCPageIndicatorView.m */, 244 | 95FFCA8916409B9400BBB572 /* SCPageView.h */, 245 | 95FFCA8A16409B9400BBB572 /* SCPageView.m */, 246 | 95FFCA8B16409B9400BBB572 /* SCPageViewController.h */, 247 | 95FFCA8C16409B9400BBB572 /* SCPageViewController.m */, 248 | 95FFCA8D16409B9400BBB572 /* SCPageViewDelegate.h */, 249 | ); 250 | name = "Page View"; 251 | sourceTree = ""; 252 | }; 253 | 9504373C163F2A8800BF33D4 /* Grid View */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | 95FFCA7D16409B8100BBB572 /* SCGridView.h */, 257 | 95FFCA7E16409B8100BBB572 /* SCGridView.m */, 258 | 95FFCA7F16409B8100BBB572 /* SCGridViewController.h */, 259 | 95FFCA8016409B8100BBB572 /* SCGridViewController.m */, 260 | 95FFCA8116409B8100BBB572 /* SCGridViewDelegate.h */, 261 | ); 262 | name = "Grid View"; 263 | sourceTree = ""; 264 | }; 265 | 9504373D163F2A9E00BF33D4 /* Paging Grid View */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | 95FFCA9416409BA400BBB572 /* SCPagingGridViewController.h */, 269 | 95FFCA9516409BA400BBB572 /* SCPagingGridViewController.m */, 270 | ); 271 | name = "Paging Grid View"; 272 | sourceTree = ""; 273 | }; 274 | 9504384C164096BA00BF33D4 /* Demos */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | 9531DC6D164335E90063629F /* SCGridViewDemo */, 278 | 950438C016409A6000BF33D4 /* SCPagingDemo */, 279 | 950438AB16409A4F00BF33D4 /* SCPagingGridDemo */, 280 | ); 281 | name = Demos; 282 | sourceTree = ""; 283 | }; 284 | 950438AB16409A4F00BF33D4 /* SCPagingGridDemo */ = { 285 | isa = PBXGroup; 286 | children = ( 287 | 950438D216409A7000BF33D4 /* Supporting Files */, 288 | 950438B216409A4F00BF33D4 /* SCAppDelegate.h */, 289 | 950438B316409A4F00BF33D4 /* SCAppDelegate.m */, 290 | 950438B416409A4F00BF33D4 /* SCExamplePagingGridViewController.h */, 291 | 950438B516409A4F00BF33D4 /* SCExamplePagingGridViewController.m */, 292 | ); 293 | name = SCPagingGridDemo; 294 | path = Demos/SCPagingGridDemo; 295 | sourceTree = ""; 296 | }; 297 | 950438C016409A6000BF33D4 /* SCPagingDemo */ = { 298 | isa = PBXGroup; 299 | children = ( 300 | 950438D316409ACF00BF33D4 /* Supporting Files */, 301 | 950438C716409A6000BF33D4 /* SCAppDelegate.h */, 302 | 950438C816409A6000BF33D4 /* SCAppDelegate.m */, 303 | 95FFCA9816409F4000BBB572 /* SCDemoPageViewController.h */, 304 | 95FFCA9916409F4000BBB572 /* SCDemoPageViewController.m */, 305 | 95FFCAAF1640A16100BBB572 /* SCPageOneViewController.h */, 306 | 95FFCAB01640A16100BBB572 /* SCPageOneViewController.m */, 307 | 95FFCAB21640A1C400BBB572 /* SCPageTwoViewController.h */, 308 | 95FFCAB31640A1C400BBB572 /* SCPageTwoViewController.m */, 309 | ); 310 | name = SCPagingDemo; 311 | path = Demos/SCPagingDemo; 312 | sourceTree = ""; 313 | }; 314 | 950438D216409A7000BF33D4 /* Supporting Files */ = { 315 | isa = PBXGroup; 316 | children = ( 317 | 950438AC16409A4F00BF33D4 /* Default-568h@2x.png */, 318 | 950438AD16409A4F00BF33D4 /* Default.png */, 319 | 950438AE16409A4F00BF33D4 /* Default@2x.png */, 320 | 950438AF16409A4F00BF33D4 /* InfoPlist.strings */, 321 | 950438B116409A4F00BF33D4 /* main.m */, 322 | 950438B616409A4F00BF33D4 /* SCPagingGridDemo-Info.plist */, 323 | 950438B716409A4F00BF33D4 /* SCPagingGridDemo-Prefix.pch */, 324 | ); 325 | name = "Supporting Files"; 326 | sourceTree = ""; 327 | }; 328 | 950438D316409ACF00BF33D4 /* Supporting Files */ = { 329 | isa = PBXGroup; 330 | children = ( 331 | 950438C916409A6000BF33D4 /* SCPagingDemo-Info.plist */, 332 | 950438CA16409A6000BF33D4 /* SCPagingDemo-Prefix.pch */, 333 | 950438C116409A6000BF33D4 /* Default-568h@2x.png */, 334 | 950438C216409A6000BF33D4 /* Default.png */, 335 | 950438C316409A6000BF33D4 /* Default@2x.png */, 336 | 950438C416409A6000BF33D4 /* InfoPlist.strings */, 337 | 950438C616409A6000BF33D4 /* main.m */, 338 | ); 339 | name = "Supporting Files"; 340 | sourceTree = ""; 341 | }; 342 | 9531DBD9164306770063629F /* Base */ = { 343 | isa = PBXGroup; 344 | children = ( 345 | 9531DBDB1643068C0063629F /* SCViewController.h */, 346 | 9531DBDC1643068C0063629F /* SCViewController.m */, 347 | ); 348 | name = Base; 349 | sourceTree = ""; 350 | }; 351 | 9531DC6D164335E90063629F /* SCGridViewDemo */ = { 352 | isa = PBXGroup; 353 | children = ( 354 | 9531DC7F164335F40063629F /* Supporting Files */, 355 | 9531DC74164335E90063629F /* SCAppDelegate.h */, 356 | 9531DC75164335E90063629F /* SCAppDelegate.m */, 357 | 9531DC801643364A0063629F /* SCExampleGridViewController.h */, 358 | 9531DC811643364A0063629F /* SCExampleGridViewController.m */, 359 | ); 360 | name = SCGridViewDemo; 361 | path = Demos/SCGridViewDemo; 362 | sourceTree = ""; 363 | }; 364 | 9531DC7F164335F40063629F /* Supporting Files */ = { 365 | isa = PBXGroup; 366 | children = ( 367 | 9531DC76164335E90063629F /* SCGridViewDemo-Info.plist */, 368 | 9531DC77164335E90063629F /* SCGridViewDemo-Prefix.pch */, 369 | 9531DC6E164335E90063629F /* Default-568h@2x.png */, 370 | 9531DC6F164335E90063629F /* Default.png */, 371 | 9531DC70164335E90063629F /* Default@2x.png */, 372 | 9531DC71164335E90063629F /* InfoPlist.strings */, 373 | 9531DC73164335E90063629F /* main.m */, 374 | ); 375 | name = "Supporting Files"; 376 | sourceTree = ""; 377 | }; 378 | /* End PBXGroup section */ 379 | 380 | /* Begin PBXNativeTarget section */ 381 | 950436F1163F25E800BF33D4 /* SCPagingGridDemo */ = { 382 | isa = PBXNativeTarget; 383 | buildConfigurationList = 95043710163F25E800BF33D4 /* Build configuration list for PBXNativeTarget "SCPagingGridDemo" */; 384 | buildPhases = ( 385 | 950436EE163F25E800BF33D4 /* Sources */, 386 | 950436EF163F25E800BF33D4 /* Frameworks */, 387 | 950436F0163F25E800BF33D4 /* Resources */, 388 | ); 389 | buildRules = ( 390 | ); 391 | dependencies = ( 392 | ); 393 | name = SCPagingGridDemo; 394 | productName = SCPagingGrid; 395 | productReference = 950436F2163F25E800BF33D4 /* SCPagingGridView.app */; 396 | productType = "com.apple.product-type.application"; 397 | }; 398 | 9504382F1640968200BF33D4 /* SCPagingDemo */ = { 399 | isa = PBXNativeTarget; 400 | buildConfigurationList = 950438491640968200BF33D4 /* Build configuration list for PBXNativeTarget "SCPagingDemo" */; 401 | buildPhases = ( 402 | 9504382C1640968200BF33D4 /* Sources */, 403 | 9504382D1640968200BF33D4 /* Frameworks */, 404 | 9504382E1640968200BF33D4 /* Resources */, 405 | ); 406 | buildRules = ( 407 | ); 408 | dependencies = ( 409 | ); 410 | name = SCPagingDemo; 411 | productName = SCPagingExample; 412 | productReference = 950438301640968200BF33D4 /* SCPagingDemo.app */; 413 | productType = "com.apple.product-type.application"; 414 | }; 415 | 9531DC3D164335540063629F /* SCGridViewDemo */ = { 416 | isa = PBXNativeTarget; 417 | buildConfigurationList = 9531DC55164335540063629F /* Build configuration list for PBXNativeTarget "SCGridViewDemo" */; 418 | buildPhases = ( 419 | 9531DC3A164335540063629F /* Sources */, 420 | 9531DC3B164335540063629F /* Frameworks */, 421 | 9531DC3C164335540063629F /* Resources */, 422 | ); 423 | buildRules = ( 424 | ); 425 | dependencies = ( 426 | ); 427 | name = SCGridViewDemo; 428 | productName = SCGridViewDemo; 429 | productReference = 9531DC3E164335540063629F /* SCGridViewDemo.app */; 430 | productType = "com.apple.product-type.application"; 431 | }; 432 | /* End PBXNativeTarget section */ 433 | 434 | /* Begin PBXProject section */ 435 | 950436E9163F25E800BF33D4 /* Project object */ = { 436 | isa = PBXProject; 437 | attributes = { 438 | CLASSPREFIX = SC; 439 | LastUpgradeCheck = 0450; 440 | ORGANIZATIONNAME = Scribd; 441 | }; 442 | buildConfigurationList = 950436EC163F25E800BF33D4 /* Build configuration list for PBXProject "SCPagingGridView" */; 443 | compatibilityVersion = "Xcode 3.2"; 444 | developmentRegion = English; 445 | hasScannedForEncodings = 0; 446 | knownRegions = ( 447 | en, 448 | ); 449 | mainGroup = 950436E7163F25E800BF33D4; 450 | productRefGroup = 950436F3163F25E800BF33D4 /* Products */; 451 | projectDirPath = ""; 452 | projectRoot = ""; 453 | targets = ( 454 | 950436F1163F25E800BF33D4 /* SCPagingGridDemo */, 455 | 9504382F1640968200BF33D4 /* SCPagingDemo */, 456 | 9531DC3D164335540063629F /* SCGridViewDemo */, 457 | ); 458 | }; 459 | /* End PBXProject section */ 460 | 461 | /* Begin PBXResourcesBuildPhase section */ 462 | 950436F0163F25E800BF33D4 /* Resources */ = { 463 | isa = PBXResourcesBuildPhase; 464 | buildActionMask = 2147483647; 465 | files = ( 466 | 950438B816409A4F00BF33D4 /* Default-568h@2x.png in Resources */, 467 | 950438B916409A4F00BF33D4 /* Default.png in Resources */, 468 | 950438BA16409A4F00BF33D4 /* Default@2x.png in Resources */, 469 | 950438BB16409A4F00BF33D4 /* InfoPlist.strings in Resources */, 470 | ); 471 | runOnlyForDeploymentPostprocessing = 0; 472 | }; 473 | 9504382E1640968200BF33D4 /* Resources */ = { 474 | isa = PBXResourcesBuildPhase; 475 | buildActionMask = 2147483647; 476 | files = ( 477 | 950438CB16409A6000BF33D4 /* Default-568h@2x.png in Resources */, 478 | 950438CC16409A6000BF33D4 /* Default.png in Resources */, 479 | 950438CD16409A6000BF33D4 /* Default@2x.png in Resources */, 480 | 950438CE16409A6000BF33D4 /* InfoPlist.strings in Resources */, 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | }; 484 | 9531DC3C164335540063629F /* Resources */ = { 485 | isa = PBXResourcesBuildPhase; 486 | buildActionMask = 2147483647; 487 | files = ( 488 | 9531DC78164335E90063629F /* Default-568h@2x.png in Resources */, 489 | 9531DC79164335E90063629F /* Default.png in Resources */, 490 | 9531DC7A164335E90063629F /* Default@2x.png in Resources */, 491 | 9531DC7B164335E90063629F /* InfoPlist.strings in Resources */, 492 | ); 493 | runOnlyForDeploymentPostprocessing = 0; 494 | }; 495 | /* End PBXResourcesBuildPhase section */ 496 | 497 | /* Begin PBXSourcesBuildPhase section */ 498 | 950436EE163F25E800BF33D4 /* Sources */ = { 499 | isa = PBXSourcesBuildPhase; 500 | buildActionMask = 2147483647; 501 | files = ( 502 | 950438BC16409A4F00BF33D4 /* main.m in Sources */, 503 | 950438BD16409A4F00BF33D4 /* SCAppDelegate.m in Sources */, 504 | 950438BE16409A4F00BF33D4 /* SCExamplePagingGridViewController.m in Sources */, 505 | 95FFCA7916409B6900BBB572 /* SCSwizzle.m in Sources */, 506 | 95FFCA7B16409B6900BBB572 /* SCViewRecycler.m in Sources */, 507 | 95FFCA8216409B8100BBB572 /* SCGridView.m in Sources */, 508 | 95FFCA8416409B8100BBB572 /* SCGridViewController.m in Sources */, 509 | 95FFCA8E16409B9400BBB572 /* SCPageIndicatorView.m in Sources */, 510 | 95FFCA9016409B9400BBB572 /* SCPageView.m in Sources */, 511 | 95FFCA9216409B9400BBB572 /* SCPageViewController.m in Sources */, 512 | 95FFCA9616409BA400BBB572 /* SCPagingGridViewController.m in Sources */, 513 | 9531DBDD1643068C0063629F /* SCViewController.m in Sources */, 514 | ); 515 | runOnlyForDeploymentPostprocessing = 0; 516 | }; 517 | 9504382C1640968200BF33D4 /* Sources */ = { 518 | isa = PBXSourcesBuildPhase; 519 | buildActionMask = 2147483647; 520 | files = ( 521 | 950438CF16409A6000BF33D4 /* main.m in Sources */, 522 | 950438D016409A6000BF33D4 /* SCAppDelegate.m in Sources */, 523 | 95FFCA7A16409B6900BBB572 /* SCSwizzle.m in Sources */, 524 | 95FFCA7C16409B6900BBB572 /* SCViewRecycler.m in Sources */, 525 | 95FFCA8316409B8100BBB572 /* SCGridView.m in Sources */, 526 | 95FFCA8516409B8100BBB572 /* SCGridViewController.m in Sources */, 527 | 95FFCA8F16409B9400BBB572 /* SCPageIndicatorView.m in Sources */, 528 | 95FFCA9116409B9400BBB572 /* SCPageView.m in Sources */, 529 | 95FFCA9316409B9400BBB572 /* SCPageViewController.m in Sources */, 530 | 95FFCA9716409BA400BBB572 /* SCPagingGridViewController.m in Sources */, 531 | 95FFCA9A16409F4000BBB572 /* SCDemoPageViewController.m in Sources */, 532 | 95FFCAB11640A16100BBB572 /* SCPageOneViewController.m in Sources */, 533 | 95FFCAB41640A1C400BBB572 /* SCPageTwoViewController.m in Sources */, 534 | 9531DBDE1643068C0063629F /* SCViewController.m in Sources */, 535 | ); 536 | runOnlyForDeploymentPostprocessing = 0; 537 | }; 538 | 9531DC3A164335540063629F /* Sources */ = { 539 | isa = PBXSourcesBuildPhase; 540 | buildActionMask = 2147483647; 541 | files = ( 542 | 9531DC58164335910063629F /* SCViewController.h in Sources */, 543 | 9531DC59164335910063629F /* SCViewController.m in Sources */, 544 | 9531DC5A164335910063629F /* SCSwizzle.h in Sources */, 545 | 9531DC5B164335910063629F /* SCSwizzle.m in Sources */, 546 | 9531DC5C164335910063629F /* SCViewRecycler.h in Sources */, 547 | 9531DC5D164335910063629F /* SCViewRecycler.m in Sources */, 548 | 9531DC5E164335910063629F /* SCGridView.h in Sources */, 549 | 9531DC5F164335910063629F /* SCGridView.m in Sources */, 550 | 9531DC60164335910063629F /* SCGridViewController.h in Sources */, 551 | 9531DC61164335910063629F /* SCGridViewController.m in Sources */, 552 | 9531DC62164335910063629F /* SCGridViewDelegate.h in Sources */, 553 | 9531DC63164335910063629F /* SCPageIndicatorProtocol.h in Sources */, 554 | 9531DC64164335910063629F /* SCPageIndicatorView.h in Sources */, 555 | 9531DC65164335910063629F /* SCPageIndicatorView.m in Sources */, 556 | 9531DC66164335910063629F /* SCPageView.h in Sources */, 557 | 9531DC67164335910063629F /* SCPageView.m in Sources */, 558 | 9531DC68164335910063629F /* SCPageViewController.h in Sources */, 559 | 9531DC69164335910063629F /* SCPageViewController.m in Sources */, 560 | 9531DC6A164335910063629F /* SCPageViewDelegate.h in Sources */, 561 | 9531DC6B164335910063629F /* SCPagingGridViewController.h in Sources */, 562 | 9531DC6C164335910063629F /* SCPagingGridViewController.m in Sources */, 563 | 9531DC7C164335E90063629F /* main.m in Sources */, 564 | 9531DC7D164335E90063629F /* SCAppDelegate.m in Sources */, 565 | 9531DC821643364A0063629F /* SCExampleGridViewController.m in Sources */, 566 | ); 567 | runOnlyForDeploymentPostprocessing = 0; 568 | }; 569 | /* End PBXSourcesBuildPhase section */ 570 | 571 | /* Begin PBXVariantGroup section */ 572 | 950438AF16409A4F00BF33D4 /* InfoPlist.strings */ = { 573 | isa = PBXVariantGroup; 574 | children = ( 575 | 950438B016409A4F00BF33D4 /* en */, 576 | ); 577 | name = InfoPlist.strings; 578 | sourceTree = ""; 579 | }; 580 | 950438C416409A6000BF33D4 /* InfoPlist.strings */ = { 581 | isa = PBXVariantGroup; 582 | children = ( 583 | 950438C516409A6000BF33D4 /* en */, 584 | ); 585 | name = InfoPlist.strings; 586 | sourceTree = ""; 587 | }; 588 | 9531DC71164335E90063629F /* InfoPlist.strings */ = { 589 | isa = PBXVariantGroup; 590 | children = ( 591 | 9531DC72164335E90063629F /* en */, 592 | ); 593 | name = InfoPlist.strings; 594 | sourceTree = ""; 595 | }; 596 | /* End PBXVariantGroup section */ 597 | 598 | /* Begin XCBuildConfiguration section */ 599 | 9504370E163F25E800BF33D4 /* Debug */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | ALWAYS_SEARCH_USER_PATHS = NO; 603 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 604 | CLANG_CXX_LIBRARY = "libc++"; 605 | CLANG_ENABLE_OBJC_ARC = YES; 606 | CLANG_WARN_EMPTY_BODY = YES; 607 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 608 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 609 | COPY_PHASE_STRIP = NO; 610 | GCC_C_LANGUAGE_STANDARD = gnu99; 611 | GCC_DYNAMIC_NO_PIC = NO; 612 | GCC_OPTIMIZATION_LEVEL = 0; 613 | GCC_PREPROCESSOR_DEFINITIONS = ( 614 | "DEBUG=1", 615 | "$(inherited)", 616 | ); 617 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 618 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 619 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 620 | GCC_WARN_UNUSED_VARIABLE = YES; 621 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 622 | ONLY_ACTIVE_ARCH = YES; 623 | SDKROOT = iphoneos; 624 | TARGETED_DEVICE_FAMILY = "1,2"; 625 | }; 626 | name = Debug; 627 | }; 628 | 9504370F163F25E800BF33D4 /* Release */ = { 629 | isa = XCBuildConfiguration; 630 | buildSettings = { 631 | ALWAYS_SEARCH_USER_PATHS = NO; 632 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 633 | CLANG_CXX_LIBRARY = "libc++"; 634 | CLANG_ENABLE_OBJC_ARC = YES; 635 | CLANG_WARN_EMPTY_BODY = YES; 636 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 637 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 638 | COPY_PHASE_STRIP = YES; 639 | GCC_C_LANGUAGE_STANDARD = gnu99; 640 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 641 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 642 | GCC_WARN_UNUSED_VARIABLE = YES; 643 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 644 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 645 | SDKROOT = iphoneos; 646 | TARGETED_DEVICE_FAMILY = "1,2"; 647 | VALIDATE_PRODUCT = YES; 648 | }; 649 | name = Release; 650 | }; 651 | 95043711163F25E800BF33D4 /* Debug */ = { 652 | isa = XCBuildConfiguration; 653 | buildSettings = { 654 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 655 | GCC_PREFIX_HEADER = "Demos/SCPagingGridDemo/SCPagingGridDemo-Prefix.pch"; 656 | INFOPLIST_FILE = "Demos/SCPagingGridDemo/SCPagingGridDemo-Info.plist"; 657 | PRODUCT_NAME = SCPagingGridView; 658 | WRAPPER_EXTENSION = app; 659 | }; 660 | name = Debug; 661 | }; 662 | 95043712163F25E800BF33D4 /* Release */ = { 663 | isa = XCBuildConfiguration; 664 | buildSettings = { 665 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 666 | GCC_PREFIX_HEADER = "Demos/SCPagingGridDemo/SCPagingGridDemo-Prefix.pch"; 667 | INFOPLIST_FILE = "Demos/SCPagingGridDemo/SCPagingGridDemo-Info.plist"; 668 | PRODUCT_NAME = SCPagingGridView; 669 | WRAPPER_EXTENSION = app; 670 | }; 671 | name = Release; 672 | }; 673 | 950438471640968200BF33D4 /* Debug */ = { 674 | isa = XCBuildConfiguration; 675 | buildSettings = { 676 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 677 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 678 | GCC_PREFIX_HEADER = "Demos/SCPagingDemo/SCPagingDemo-Prefix.pch"; 679 | INFOPLIST_FILE = "Demos/SCPagingDemo/SCPagingDemo-Info.plist"; 680 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 681 | PRODUCT_NAME = "$(TARGET_NAME)"; 682 | WRAPPER_EXTENSION = app; 683 | }; 684 | name = Debug; 685 | }; 686 | 950438481640968200BF33D4 /* Release */ = { 687 | isa = XCBuildConfiguration; 688 | buildSettings = { 689 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 690 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 691 | GCC_PREFIX_HEADER = "Demos/SCPagingDemo/SCPagingDemo-Prefix.pch"; 692 | INFOPLIST_FILE = "Demos/SCPagingDemo/SCPagingDemo-Info.plist"; 693 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 694 | PRODUCT_NAME = "$(TARGET_NAME)"; 695 | WRAPPER_EXTENSION = app; 696 | }; 697 | name = Release; 698 | }; 699 | 9531DC56164335540063629F /* Debug */ = { 700 | isa = XCBuildConfiguration; 701 | buildSettings = { 702 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 703 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 704 | GCC_PREFIX_HEADER = "Demos/SCGridViewDemo/SCGridViewDemo-Prefix.pch"; 705 | INFOPLIST_FILE = "Demos/SCGridViewDemo/SCGridViewDemo-Info.plist"; 706 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 707 | PRODUCT_NAME = "$(TARGET_NAME)"; 708 | WRAPPER_EXTENSION = app; 709 | }; 710 | name = Debug; 711 | }; 712 | 9531DC57164335540063629F /* Release */ = { 713 | isa = XCBuildConfiguration; 714 | buildSettings = { 715 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 716 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 717 | GCC_PREFIX_HEADER = "Demos/SCGridViewDemo/SCGridViewDemo-Prefix.pch"; 718 | INFOPLIST_FILE = "Demos/SCGridViewDemo/SCGridViewDemo-Info.plist"; 719 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 720 | PRODUCT_NAME = "$(TARGET_NAME)"; 721 | WRAPPER_EXTENSION = app; 722 | }; 723 | name = Release; 724 | }; 725 | /* End XCBuildConfiguration section */ 726 | 727 | /* Begin XCConfigurationList section */ 728 | 950436EC163F25E800BF33D4 /* Build configuration list for PBXProject "SCPagingGridView" */ = { 729 | isa = XCConfigurationList; 730 | buildConfigurations = ( 731 | 9504370E163F25E800BF33D4 /* Debug */, 732 | 9504370F163F25E800BF33D4 /* Release */, 733 | ); 734 | defaultConfigurationIsVisible = 0; 735 | defaultConfigurationName = Release; 736 | }; 737 | 95043710163F25E800BF33D4 /* Build configuration list for PBXNativeTarget "SCPagingGridDemo" */ = { 738 | isa = XCConfigurationList; 739 | buildConfigurations = ( 740 | 95043711163F25E800BF33D4 /* Debug */, 741 | 95043712163F25E800BF33D4 /* Release */, 742 | ); 743 | defaultConfigurationIsVisible = 0; 744 | defaultConfigurationName = Release; 745 | }; 746 | 950438491640968200BF33D4 /* Build configuration list for PBXNativeTarget "SCPagingDemo" */ = { 747 | isa = XCConfigurationList; 748 | buildConfigurations = ( 749 | 950438471640968200BF33D4 /* Debug */, 750 | 950438481640968200BF33D4 /* Release */, 751 | ); 752 | defaultConfigurationIsVisible = 0; 753 | defaultConfigurationName = Release; 754 | }; 755 | 9531DC55164335540063629F /* Build configuration list for PBXNativeTarget "SCGridViewDemo" */ = { 756 | isa = XCConfigurationList; 757 | buildConfigurations = ( 758 | 9531DC56164335540063629F /* Debug */, 759 | 9531DC57164335540063629F /* Release */, 760 | ); 761 | defaultConfigurationIsVisible = 0; 762 | }; 763 | /* End XCConfigurationList section */ 764 | }; 765 | rootObject = 950436E9163F25E800BF33D4 /* Project object */; 766 | } 767 | -------------------------------------------------------------------------------- /SCPagingGridView.xcodeproj/xcshareddata/xcschemes/SCPagingDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /SCPagingGridView.xcodeproj/xcshareddata/xcschemes/SCPagingGridDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Source/Base/SCViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCViewController : UIViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Source/Base/SCViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCViewController.h" 25 | 26 | @interface SCViewController () 27 | 28 | @end 29 | 30 | @implementation SCViewController 31 | 32 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { 33 | BOOL result = NO; 34 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 35 | result = YES; 36 | } else { 37 | result = toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown; 38 | } 39 | return result; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Source/SCGridView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | #import "SCGridViewDelegate.h" 27 | 28 | @interface SCGridView : UIView 29 | 30 | /* The schema determines how the GridView will be laid out. 31 | * 32 | * This property must be set to an array of integers. Each integer in the array 33 | * constitutes a new row in the grid and the value of the integer decides the number 34 | * of columns in that row. 35 | * 36 | * Example 1: 37 | * self.schema = @[@(2), @(2)]; 38 | * This creates a gridView with 2 rows. Each row has 2 columns. 39 | * 40 | * Example 2: 41 | * self.schema = @[@(3), @(2), @(5)]; 42 | * This creates a gridView with 3 rows. The first row has 3 columns. The second row 43 | * has 2 columns. The third row has 5 columns. 44 | * 45 | */ 46 | @property (nonatomic, strong) NSArray *schema; 47 | 48 | @property (nonatomic, weak) iddelegate; 49 | 50 | // The amount of space between each row 51 | @property (nonatomic, assign) CGFloat rowSpacing; 52 | 53 | // The amount of space between each column 54 | @property (nonatomic, assign) CGFloat colSpacing; 55 | 56 | // An array of UIViews. The views will be laid out starting at the top left of the grid 57 | // and then right & down. Alternatively, instead of setting cells directly, 58 | // the gridView delegate can provide cells. 59 | @property (nonatomic, strong) NSArray *cells; 60 | 61 | // Force the gridview to ask its delegate for new cells, if it has a delegate. 62 | - (void)reloadData; 63 | 64 | // The total number of cells in the gridview 65 | - (NSUInteger)size; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Source/SCGridView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCGridView.h" 25 | 26 | @interface SCGridView () 27 | 28 | @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; 29 | 30 | @end 31 | 32 | @implementation SCGridView 33 | 34 | #pragma mark - UIView 35 | 36 | - (id)initWithFrame:(CGRect)frame { 37 | if (self = [super initWithFrame:frame]) { 38 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_tapPerformed:)]; 39 | tap.delegate = self; 40 | [self addGestureRecognizer:tap]; 41 | self.tapGesture = tap; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)layoutSubviews { 47 | NSUInteger numberOfRows = [self.schema count]; 48 | if (numberOfRows > 0) { 49 | CGFloat totalRowSpacing = (numberOfRows - 1) * self.rowSpacing; 50 | NSUInteger rowHeight = floorf((self.bounds.size.height - totalRowSpacing) / numberOfRows); 51 | NSUInteger cellNumber = 0; 52 | CGFloat y = 0.0f; 53 | for (int row = 0; row < numberOfRows; ++row) { 54 | if (cellNumber >= [self.cells count]) { 55 | break; 56 | } 57 | BOOL isLastRow = (row+1 == numberOfRows); 58 | if (isLastRow) { 59 | // to correct for rounding 60 | rowHeight = self.bounds.size.height - y; 61 | } 62 | NSInteger numberOfCols = [[self.schema objectAtIndex:row] integerValue]; 63 | if (numberOfCols > 0) { 64 | CGFloat totalColSpacing = (numberOfCols - 1) * self.colSpacing; 65 | NSUInteger colWidth = floorf((self.bounds.size.width - totalColSpacing) / numberOfCols); 66 | CGFloat x = 0.0f; 67 | for (int col = 0; col < numberOfCols; ++col) { 68 | if (cellNumber >= [self.cells count]) { 69 | break; 70 | } 71 | BOOL isLastCol = (col+1 == numberOfCols); 72 | if (isLastCol) { 73 | // to correct for rounding 74 | colWidth = self.bounds.size.width - x; 75 | } 76 | UIView *cell = [self.cells objectAtIndex:cellNumber]; 77 | cell.frame = CGRectMake(x, y, colWidth, rowHeight); 78 | x += (colWidth + self.colSpacing); 79 | cellNumber++; 80 | } 81 | } 82 | y += (rowHeight + self.rowSpacing); 83 | } 84 | } 85 | } 86 | 87 | #pragma mark - Grid View 88 | 89 | - (NSUInteger)size { 90 | NSUInteger result = 0; 91 | for (NSNumber *row in self.schema) { 92 | result += [row integerValue]; 93 | } 94 | return result; 95 | } 96 | 97 | // [1,2,1]... [2,1,2]; 98 | - (void)setSchema:(NSArray *)schema { 99 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[schema count]]; 100 | for (id obj in schema) { 101 | if ([obj isKindOfClass:[NSNumber class]]) { 102 | [result addObject:obj]; 103 | } 104 | } 105 | _schema = [result copy]; 106 | } 107 | 108 | - (void)reloadData { 109 | NSMutableArray *cells = [NSMutableArray array]; 110 | if ([self.delegate respondsToSelector:@selector(viewAtPosition:inGridView:coordinates:size:)]) { 111 | NSInteger position = 0; 112 | NSInteger numberOfRows = [self.schema count]; 113 | if (numberOfRows > 0) { 114 | for (int row = 0; row < numberOfRows; ++row) { 115 | NSInteger numberOfCols = [[self.schema objectAtIndex:row] integerValue]; 116 | if (numberOfCols > 0) { 117 | CGSize size = CGSizeMake(numberOfRows, numberOfCols); 118 | for (int col = 0; col < numberOfCols; ++col) { 119 | UIView *cell = [self.delegate viewAtPosition:position inGridView:self coordinates:CGPointMake(row, col) size:size]; 120 | if (!cell) { 121 | // blank view 122 | cell = [[UIView alloc] init]; 123 | } 124 | [cells addObject:cell]; 125 | ++position; 126 | } 127 | } 128 | } 129 | } 130 | } 131 | self.cells = cells; 132 | } 133 | 134 | - (void)setCells:(NSArray *)cells { 135 | for (UIView *view in _cells) { 136 | [view removeFromSuperview]; 137 | } 138 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[cells count]]; 139 | for (id obj in cells) { 140 | if ([obj isKindOfClass:[UIView class]]) { 141 | UIView *view = obj; 142 | view.autoresizingMask = UIViewAutoresizingNone; 143 | [result addObject:view]; 144 | [self addSubview:view]; 145 | } 146 | } 147 | _cells = result; 148 | [self setNeedsLayout]; 149 | } 150 | 151 | #pragma mark - Tap Gesture 152 | 153 | - (void)_tapPerformed:(UIGestureRecognizer *)gestureRecognizer { 154 | if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) { 155 | UITapGestureRecognizer *tap = (UITapGestureRecognizer *)gestureRecognizer; 156 | if (tap.state == UIGestureRecognizerStateEnded) { 157 | if ([self.delegate respondsToSelector:@selector(gridView:didSelectCell:atIndex:)]) { 158 | CGPoint location = [tap locationInView:self]; 159 | for (int i = 0; i < [self.cells count]; ++i) { 160 | UIView * view = [self.cells objectAtIndex:i]; 161 | CGPoint local = [view convertPoint:location fromView:self]; 162 | if ([view pointInside:local withEvent:nil]) { 163 | [self.delegate gridView:self didSelectCell:view atIndex:i]; 164 | break; 165 | } 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | #pragma mark - UIGestureRecognizerDelegate 173 | 174 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { 175 | return ![[self hitTest:[touch locationInView:self] withEvent:nil] isKindOfClass:[UIControl class]]; 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /Source/SCGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCGridViewDelegate.h" 25 | #import "SCViewController.h" 26 | 27 | @class SCGridView; 28 | 29 | @interface SCGridViewController : SCViewController 30 | 31 | @property (nonatomic, weak, readonly) SCGridView *gridView; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Source/SCGridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCGridViewController.h" 25 | #import "SCGridView.h" 26 | 27 | @interface SCGridViewController () { 28 | BOOL _dataLoaded; 29 | } 30 | 31 | @property (nonatomic, weak) SCGridView *gridView; 32 | 33 | @end 34 | 35 | @implementation SCGridViewController 36 | 37 | #pragma mark - UIViewController 38 | 39 | - (void)loadView { 40 | SCGridView *view = [[SCGridView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 41 | view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 42 | self.gridView = view; 43 | self.view = view; 44 | } 45 | 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | _dataLoaded = NO; 49 | } 50 | 51 | - (void)viewWillAppear:(BOOL)animated { 52 | [super viewWillAppear:animated]; 53 | if (!_dataLoaded) { 54 | _dataLoaded = YES; 55 | [self.gridView reloadData]; 56 | } 57 | } 58 | 59 | #pragma mark - Grid View 60 | 61 | - (void)setGridView:(SCGridView *)gridView { 62 | if (gridView != _gridView) { 63 | _gridView.delegate = nil; 64 | _gridView = gridView; 65 | _gridView.delegate = self; 66 | } 67 | } 68 | 69 | #pragma mark - SCGridViewDelegate 70 | 71 | - (UIView *)viewAtPosition:(NSUInteger)position inGridView:(SCGridView *)gridView coordinates:(CGPoint)coordinates size:(CGSize)size { 72 | return nil; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Source/SCGridViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @class SCGridView; 27 | 28 | @protocol SCGridViewDelegate 29 | 30 | @optional 31 | 32 | - (UIView *)viewAtPosition:(NSUInteger)position inGridView:(SCGridView *)gridView coordinates:(CGPoint)coordinates size:(CGSize)size; 33 | - (void)gridView:(SCGridView *)gridView didSelectCell:(UIView *)cell atIndex:(NSUInteger)index; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Source/SCPageIndicatorProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @protocol SCPageIndicatorDelegate 27 | 28 | - (void)requestPageChangeTo:(NSUInteger)pageNumber panning:(BOOL)isPanning; 29 | 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /Source/SCPageIndicatorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | #import "SCPageIndicatorProtocol.h" 27 | 28 | @interface SCPageIndicatorView : UIView 29 | 30 | @property (nonatomic, assign) NSUInteger numberOfPages; 31 | @property (nonatomic, assign) NSUInteger currentPage; 32 | @property (nonatomic, weak) id pageIndicatorDelegate; 33 | 34 | @property (nonatomic, strong, readonly) UIPanGestureRecognizer *panGesture; 35 | @property (nonatomic, strong, readonly) UITapGestureRecognizer *tapGesture; 36 | @property (nonatomic, strong, readonly) UIView *emptyView; 37 | @property (nonatomic, strong, readonly) UIView *filledView; 38 | 39 | @property (nonatomic, strong) UIColor *dotColor; 40 | @property (nonatomic, assign) CGSize dotSize; 41 | @property (nonatomic, assign) CGFloat dotPadding; 42 | @property (nonatomic, assign) CGFloat dotLineWidth; 43 | 44 | - (UIImage *)createEmptyImage; 45 | - (UIImage *)createFilledImage; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Source/SCPageIndicatorView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageIndicatorView.h" 25 | 26 | @interface SCPageIndicatorView () 27 | 28 | @property (nonatomic, strong) UIView *emptyView; 29 | @property (nonatomic, strong) UIView *filledView; 30 | @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; 31 | @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; 32 | @property (nonatomic, assign) CGSize dotSizeWithPadding; 33 | 34 | @end 35 | 36 | @implementation SCPageIndicatorView { 37 | NSInteger _lastDispatchedPage; 38 | BOOL _panningActivated; 39 | } 40 | 41 | @synthesize numberOfPages = _numberOfPages; 42 | @synthesize currentPage = _currentPage; 43 | @synthesize pageIndicatorDelegate = _pageIndicatorDelegate; 44 | 45 | + (UIImage *)emptyImageWithColor:(UIColor *)color size:(CGSize)size padding:(CGFloat)padding lineWidth:(CGFloat)lineWidth { 46 | CGFloat x = ceilf(lineWidth/2.0f); 47 | CGFloat y = ceilf(lineWidth/2.0f); 48 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width + padding, size.height), NO, 0.0f); 49 | CGContextRef context = UIGraphicsGetCurrentContext(); 50 | CGContextSetLineWidth(context, lineWidth); 51 | [color setStroke]; 52 | CGContextAddPath(context, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, size.width - (x*2.0f), size.height - (y*2.0f))].CGPath); 53 | CGContextStrokePath(context); 54 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); 55 | UIGraphicsEndImageContext(); 56 | return result; 57 | } 58 | 59 | + (UIImage *)filledImageWithColor:(UIColor *)color size:(CGSize)size padding:(CGFloat)padding { 60 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width + padding, size.height), NO, 0.0f); 61 | CGContextRef context = UIGraphicsGetCurrentContext(); 62 | [color setFill]; 63 | CGContextAddPath(context, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)].CGPath); 64 | CGContextFillPath(context); 65 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); 66 | UIGraphicsEndImageContext(); 67 | return result; 68 | } 69 | 70 | - (id)initWithFrame:(CGRect)frame { 71 | self = [super initWithFrame:frame]; 72 | if (self) { 73 | _dotSize = CGSizeMake(10.0f, 10.0f); 74 | _dotColor = [UIColor whiteColor]; 75 | _dotPadding = 3.0f; 76 | _dotLineWidth = 1.0f; 77 | self.backgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f]; 78 | 79 | _emptyView = [[UIView alloc] initWithFrame:self.bounds]; 80 | [self addSubview:_emptyView]; 81 | 82 | _filledView = [[UIView alloc] init]; 83 | [_emptyView addSubview:_filledView]; 84 | 85 | // gesture recognizers 86 | _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_handleTap:)]; 87 | _tapGesture.delegate = self; 88 | [self addGestureRecognizer:_tapGesture]; 89 | 90 | _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(_handlePan:)]; 91 | _panGesture.delegate = self; 92 | [self addGestureRecognizer:_panGesture]; 93 | 94 | [self _updateDotSizes]; 95 | } 96 | return self; 97 | } 98 | 99 | #pragma mark - UIView 100 | 101 | - (void)layoutSubviews { 102 | CGSize size = CGSizeMake(self.dotSizeWithPadding.width * self.numberOfPages, _dotSize.height); 103 | CGPoint origin = CGPointMake(10.0f, floorf((self.bounds.size.height - size.height)/2.0f)); 104 | self.emptyView.frame = CGRectMake(origin.x, origin.y, size.width, size.height); 105 | 106 | if (self.panGesture.state != UIGestureRecognizerStateBegan && self.panGesture.state != UIGestureRecognizerStateChanged) { 107 | [self _placeCurrentPageIndicator:NO]; 108 | } 109 | } 110 | 111 | - (void)setNumberOfPages:(NSUInteger)numberOfPages { 112 | if (numberOfPages != _numberOfPages) { 113 | _numberOfPages = numberOfPages; 114 | self.emptyView.hidden = _numberOfPages <= 1; 115 | [self setNeedsLayout]; 116 | } 117 | } 118 | 119 | - (void)setCurrentPage:(NSUInteger)currentPage { 120 | if (currentPage != _currentPage) { 121 | _currentPage = currentPage; 122 | if (self.panGesture.state != UIGestureRecognizerStateBegan && self.panGesture.state != UIGestureRecognizerStateChanged) { 123 | [self _placeCurrentPageIndicator:YES]; 124 | } 125 | } 126 | } 127 | 128 | - (void)_placeCurrentPageIndicator:(BOOL)animate { 129 | [UIView animateWithDuration:animate ? 0.3f : 0.0f animations:^{ 130 | self.filledView.frame = CGRectMake(self.currentPage * self.dotSizeWithPadding.width, 0.0f, self.dotSizeWithPadding.width, _dotSize.height); 131 | }]; 132 | } 133 | 134 | #pragma mark - Dot Config 135 | 136 | - (void)setDotColor:(UIColor *)dotColor { 137 | if (dotColor != _dotColor) { 138 | _dotColor = dotColor; 139 | [self _updateDotImages]; 140 | } 141 | } 142 | 143 | - (void)setDotSize:(CGSize)dotSize { 144 | if (!CGSizeEqualToSize(dotSize, _dotSize)) { 145 | _dotSize = dotSize; 146 | [self _updateDotSizes]; 147 | } 148 | } 149 | 150 | - (void)setDotPadding:(CGFloat)dotPadding { 151 | if (dotPadding != _dotPadding) { 152 | _dotPadding = dotPadding; 153 | [self _updateDotSizes]; 154 | } 155 | } 156 | 157 | - (void)setDotLineWidth:(CGFloat)dotLineWidth { 158 | if (dotLineWidth != _dotLineWidth) { 159 | _dotLineWidth = dotLineWidth; 160 | [self _updateDotImages]; 161 | } 162 | } 163 | 164 | #pragma mark - Dot Images 165 | 166 | - (UIImage *)createEmptyImage { 167 | return [[self class] emptyImageWithColor:self.dotColor size:self.dotSize padding:self.dotPadding lineWidth:self.dotLineWidth]; 168 | } 169 | 170 | - (UIImage *)createFilledImage { 171 | return [[self class] filledImageWithColor:self.dotColor size:self.dotSize padding:self.dotPadding]; 172 | } 173 | 174 | - (void)_updateDotSizes { 175 | self.dotSizeWithPadding = CGSizeMake(self.dotSize.width + self.dotPadding, self.dotSize.height); 176 | CGRect rect = self.filledView.frame; 177 | rect.size = self.dotSizeWithPadding; 178 | self.filledView.frame = rect; 179 | 180 | [self _updateDotImages]; 181 | [self setNeedsLayout]; 182 | } 183 | 184 | - (void)_updateDotImages { 185 | _emptyView.backgroundColor = [UIColor colorWithPatternImage:[self createEmptyImage]]; 186 | _filledView.backgroundColor = [UIColor colorWithPatternImage:[self createFilledImage]]; 187 | } 188 | 189 | #pragma mark - Gesture Wrecka Nizers 190 | 191 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 192 | if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) { 193 | return [self _validateGestureLocation:gestureRecognizer]; 194 | } 195 | return YES; 196 | } 197 | 198 | 199 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 200 | if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { 201 | return YES; 202 | } 203 | return NO; 204 | } 205 | 206 | - (void)_handleTap:(UIGestureRecognizer *)gestureRecognizer { 207 | if (gestureRecognizer.state == UIGestureRecognizerStateRecognized && [self.pageIndicatorDelegate respondsToSelector:@selector(requestPageChangeTo:panning:)]) { 208 | CGPoint loc = [gestureRecognizer locationInView:self.emptyView]; 209 | NSUInteger page = floorf(loc.x / self.dotSizeWithPadding.width); 210 | self.currentPage = page; 211 | [self.pageIndicatorDelegate requestPageChangeTo:page panning:NO]; 212 | } 213 | } 214 | 215 | - (void)_handlePan:(UIGestureRecognizer *)gestureRecognizer { 216 | if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { 217 | _lastDispatchedPage = -1; 218 | _panningActivated = [self _validateGestureLocation:gestureRecognizer]; 219 | } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged && [self.pageIndicatorDelegate respondsToSelector:@selector(requestPageChangeTo:panning:)]) { 220 | if (!_panningActivated && [self _validateGestureLocation:gestureRecognizer]) { 221 | _panningActivated = YES; 222 | } 223 | if (_panningActivated) { 224 | CGPoint loc = [gestureRecognizer locationInView:self.emptyView]; 225 | loc.x -= floorf(self.dotSizeWithPadding.width/2.0f); 226 | if (loc.x < 0.0f) { 227 | loc.x = 0.0f; 228 | } else if (loc.x > self.emptyView.bounds.size.width - self.dotSizeWithPadding.width) { 229 | loc.x = self.emptyView.bounds.size.width - self.dotSizeWithPadding.width; 230 | } 231 | NSInteger page = floorf(loc.x / self.dotSizeWithPadding.width); 232 | if (page >= 0 && page < self.numberOfPages) { 233 | CGRect frame = self.filledView.frame; 234 | frame.origin.x = loc.x; 235 | self.filledView.frame = frame; 236 | if (page != _lastDispatchedPage) { 237 | _lastDispatchedPage = page; 238 | _currentPage = page; 239 | [self.pageIndicatorDelegate requestPageChangeTo:page panning:YES]; 240 | } 241 | } 242 | } 243 | } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { 244 | [self _placeCurrentPageIndicator:YES]; 245 | } 246 | } 247 | 248 | #pragma mark - Validation 249 | 250 | - (BOOL)_validateGestureLocation:(UIGestureRecognizer *)gestureRecognizer { 251 | CGPoint loc = [gestureRecognizer locationInView:self.emptyView]; 252 | loc.y = 0.0f; 253 | return [self.emptyView pointInside:loc withEvent:nil]; 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /Source/SCPageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | #import "SCPageViewDelegate.h" 28 | 29 | @class SCPageContainerView; 30 | 31 | typedef enum _SCPageViewState { 32 | SCPageViewStateResting = 0, 33 | SCPageViewStateTransitionPrevious, 34 | SCPageViewStateTransitionPreviousImmediately, 35 | SCPageViewStateTransitionNext, 36 | SCPageViewStateTransitionNextImmediately, 37 | SCPageViewStateTransitionInterrupted 38 | } SCPageViewState; 39 | 40 | @interface SCPageView : UIScrollView 41 | 42 | // configuration 43 | @property (nonatomic, weak) id pageDelegate; 44 | @property (nonatomic, assign) CGFloat pagingThresholdPercent; 45 | @property (nonatomic, assign) CGFloat pagingThresholdMinimum; 46 | @property (nonatomic, assign) SCPagingDirection direction; // defaults to vertical 47 | @property (nonatomic, assign) CGFloat gapBetweenPages; // defaults to 0.0f 48 | @property (nonatomic, strong, readonly) UIView *nextGapView; 49 | @property (nonatomic, strong, readonly) UIView *previousGapView; 50 | @property (nonatomic, assign, readonly) NSUInteger numberOfPages; 51 | 52 | // inner workings 53 | @property (nonatomic, assign, readonly) NSUInteger currentPageNumber; 54 | @property (nonatomic, assign, readonly) SCPageViewState pageViewState; 55 | 56 | - (void)reloadData; 57 | - (void)setCurrentPageNumber:(NSUInteger)currentPageNumber animated:(BOOL)animated fast:(BOOL)fastAnimation; 58 | 59 | @end -------------------------------------------------------------------------------- /Source/SCPageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageView.h" 25 | 26 | #import 27 | 28 | @interface SCPageContainerView : UIView { 29 | SCPagingDirection _direction; 30 | } 31 | 32 | @property (nonatomic, strong) UIView *pageView; 33 | @property (nonatomic, strong) UIView *headerView; 34 | 35 | - (id)initWithFrame:(CGRect)frame header:(UIView *)header page:(UIView *)page direction:(SCPagingDirection)direction; 36 | 37 | @end 38 | 39 | @interface SCPageView () { 40 | CGFloat _totalMovement; 41 | SCPageContainerView *_animatingPage; 42 | } 43 | 44 | @property (nonatomic, assign) NSUInteger numberOfPages; 45 | @property (nonatomic, weak) SCPageContainerView *activePage; 46 | @property (nonatomic, weak) SCPageContainerView *previousPage; 47 | @property (nonatomic, weak) SCPageContainerView *nextPage; 48 | @property (nonatomic, assign) NSUInteger currentPageNumber; 49 | @property (nonatomic, strong) NSNumber *scrollFinalPosition; 50 | @property (nonatomic, assign) SCPageViewState pageViewState; 51 | @property (nonatomic, strong) UIView *transitionView; 52 | @property (nonatomic, strong) UIView *nextGapView; 53 | @property (nonatomic, strong) UIView *previousGapView; 54 | 55 | @end 56 | 57 | @implementation SCPageView 58 | 59 | static CGFloat const kCancellationTheshold = 20.0f; 60 | static CGFloat const kVelocityThreshold = 1000.0f; 61 | 62 | - (void)dealloc { 63 | [self removeObserver:self forKeyPath:@"contentOffset"]; 64 | } 65 | 66 | - (void)_setup { 67 | self.showsHorizontalScrollIndicator = NO; 68 | self.showsVerticalScrollIndicator = NO; 69 | self.alwaysBounceHorizontal = YES; 70 | self.alwaysBounceVertical = YES; 71 | [self addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:0]; 72 | [self.panGestureRecognizer addTarget:self action:@selector(_scHandlePan:)]; 73 | _transitionView = [[UIView alloc] init]; 74 | _transitionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 75 | _pagingThresholdPercent = 0.1f; 76 | _direction = SCPagingDirectionVertical; 77 | 78 | _nextGapView = [[UIView alloc] init]; 79 | _previousGapView = [[UIView alloc] init]; 80 | 81 | [self _configureGapViews]; 82 | } 83 | 84 | - (id)init { 85 | if (self = [super init]) { 86 | [self _setup]; 87 | } 88 | return self; 89 | } 90 | 91 | - (id)initWithFrame:(CGRect)frame { 92 | if (self = [super initWithFrame:frame]) { 93 | [self _setup]; 94 | } 95 | return self; 96 | } 97 | 98 | #pragma mark - UIView 99 | 100 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 101 | [super touchesBegan:touches withEvent:event]; 102 | if (self.pageViewState == SCPageViewStateTransitionNext || self.pageViewState == SCPageViewStateTransitionPrevious) { 103 | self.pageViewState = SCPageViewStateTransitionInterrupted; 104 | } 105 | } 106 | 107 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 108 | [super touchesEnded:touches withEvent:event]; 109 | if (self.pageViewState == SCPageViewStateTransitionInterrupted) { 110 | [self _alignToBestPage]; 111 | } 112 | } 113 | 114 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 115 | [super touchesCancelled:touches withEvent:event]; 116 | if (self.pageViewState == SCPageViewStateTransitionInterrupted && self.panGestureRecognizer.state != UIGestureRecognizerStateBegan && self.panGestureRecognizer.state != UIGestureRecognizerStateChanged) { 117 | [self _alignToBestPage]; 118 | } 119 | } 120 | 121 | - (void)layoutSubviews { 122 | [super layoutSubviews]; 123 | 124 | // previous page 125 | if (self.previousPage) { 126 | CGSize size = [self _sizeForPage:self.previousPage]; 127 | CGPoint origin; 128 | CGRect gapFrame = self.previousGapView.frame; 129 | if (self.direction == SCPagingDirectionVertical) { 130 | origin = CGPointMake(0.0f, -size.height - self.gapBetweenPages); 131 | gapFrame.origin = CGPointMake(0.0f, -self.gapBetweenPages); 132 | } else { 133 | origin = CGPointMake(-size.width - self.gapBetweenPages, 0.0f); 134 | gapFrame.origin = CGPointMake(-self.gapBetweenPages, 0.0f); 135 | } 136 | self.previousPage.frame = [self _rectWithOrigin:origin size:size]; 137 | self.previousGapView.frame = gapFrame; 138 | } 139 | 140 | // active page 141 | if (self.activePage) { 142 | self.activePage.frame = [self _rectWithOrigin:CGPointZero size:[self _sizeForPage:self.activePage]]; 143 | } 144 | 145 | // next page 146 | if (self.nextPage) { 147 | CGPoint origin; 148 | CGRect gapFrame = self.nextGapView.frame; 149 | if (self.direction == SCPagingDirectionVertical) { 150 | origin = CGPointMake(0.0f, self.activePage.frame.origin.y + self.activePage.frame.size.height + self.gapBetweenPages); 151 | gapFrame.origin = CGPointMake(0.0f, self.activePage.frame.origin.y + self.activePage.frame.size.height); 152 | } else { 153 | origin = CGPointMake(self.activePage.frame.origin.x + self.activePage.frame.size.width + self.gapBetweenPages, 0.0f); 154 | gapFrame.origin = CGPointMake(self.activePage.frame.origin.x + self.activePage.frame.size.width, 0.0f); 155 | } 156 | self.nextPage.frame = [self _rectWithOrigin:origin size:[self _sizeForPage:self.nextPage]]; 157 | self.nextGapView.frame = gapFrame; 158 | } 159 | 160 | CGFloat contentHeight = self.activePage.frame.size.height; 161 | if (contentHeight < self.bounds.size.height) { 162 | contentHeight = self.bounds.size.height; 163 | } 164 | 165 | self.contentSize = CGSizeMake(self.bounds.size.width, contentHeight); 166 | } 167 | 168 | - (CGSize)_sizeForPage:(UIView *)page { 169 | CGSize size = self.bounds.size; 170 | size = [page sizeThatFits:size]; 171 | if (size.width < self.bounds.size.width) { 172 | size.width = self.bounds.size.width; 173 | } 174 | if (size.height < self.bounds.size.height) { 175 | size.height = self.bounds.size.height; 176 | } 177 | return size; 178 | } 179 | 180 | - (CGRect)_rectWithOrigin:(CGPoint)origin size:(CGSize)size { 181 | return CGRectMake(origin.x, origin.y, size.width, size.height); 182 | } 183 | 184 | #pragma mark - UIScrollView 185 | 186 | - (void)setContentOffset:(CGPoint)contentOffset { 187 | if (self.direction == SCPagingDirectionVertical) { 188 | [super setContentOffset:CGPointMake(0.0f, contentOffset.y)]; 189 | } else { 190 | [super setContentOffset:CGPointMake(contentOffset.x, 0.0f)]; 191 | } 192 | } 193 | 194 | #pragma mark - Pan Gesture 195 | 196 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 197 | if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { 198 | UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer; 199 | CGPoint translate = [pan translationInView:self]; 200 | return [self _validateMovement:translate]; 201 | } 202 | return YES; 203 | } 204 | 205 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 206 | if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [gestureRecognizer.view isKindOfClass:[SCPageView class]] && [otherGestureRecognizer.view isKindOfClass:[SCPageView class]]) { 207 | return YES; 208 | } 209 | return NO; 210 | } 211 | 212 | - (void)_scHandlePan:(UIGestureRecognizer *)gestureRecognizer { 213 | if (gestureRecognizer.state == UIGestureRecognizerStateBegan && [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { 214 | CGPoint translate = [((UIPanGestureRecognizer *)gestureRecognizer) translationInView:self]; 215 | _totalMovement = fabsf(translate.y) + fabsf(translate.x); 216 | } 217 | 218 | if (gestureRecognizer.state == UIGestureRecognizerStateBegan && (self.pageViewState == SCPageViewStateTransitionNext || self.pageViewState == SCPageViewStateTransitionPrevious)) { 219 | self.pageViewState = SCPageViewStateTransitionInterrupted; 220 | } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded && (self.pageViewState == SCPageViewStateResting || self.pageViewState == SCPageViewStateTransitionInterrupted)) { 221 | [self _alignToBestPage]; 222 | } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged && [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && _totalMovement < kCancellationTheshold) { 223 | // if the total movement of the pan is under a certain threshold, we will see if it should be canceled due to invalid movement. 224 | // The main purpose of this code is to ensure a vertical pan doesn't trigger when a horizontal should have & vice versa. 225 | UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer; 226 | CGPoint translate = [pan translationInView:self]; 227 | _totalMovement += fabsf(translate.y) + fabsf(translate.x); 228 | if (_totalMovement < kCancellationTheshold && ![self _validateMovement:translate]) { 229 | // if the movement becomes invalid after a short amount of time, cancel it. 230 | pan.enabled = NO; 231 | pan.enabled = YES; 232 | } 233 | } 234 | } 235 | 236 | - (BOOL)_validateMovement:(CGPoint)translate { 237 | if (self.direction == SCPagingDirectionVertical) { 238 | return (translate.y == 0 && translate.x == 0) || (translate.y != 0 && ((fabsf(translate.x) / fabsf(translate.y)) < 1.0f)); 239 | } else { 240 | return (translate.y == 0 && translate.x == 0) || (translate.x != 0 && ((fabsf(translate.y) / fabsf(translate.x)) < 1.0f)); 241 | } 242 | } 243 | 244 | - (void)_alignToBestPage { 245 | CGFloat velocity; 246 | CGFloat top = 0.0f; 247 | CGFloat bottom; 248 | CGFloat pagingThreshold = [self _pagingThreshold]; 249 | CGFloat contentOffset; 250 | CGPoint velocityPoint = [self.panGestureRecognizer velocityInView:self]; 251 | if (self.direction == SCPagingDirectionVertical) { 252 | velocity = velocityPoint.y; 253 | bottom = self.activePage.frame.size.height - self.bounds.size.height; 254 | contentOffset = self.contentOffset.y; 255 | } else { 256 | velocity = velocityPoint.x; 257 | bottom = self.activePage.frame.size.width - self.bounds.size.width; 258 | contentOffset = self.contentOffset.x; 259 | } 260 | 261 | if ((contentOffset < (top - pagingThreshold) || velocity > kVelocityThreshold) && self.previousPage && velocity > 0) { 262 | self.pageViewState = SCPageViewStateTransitionPrevious; 263 | } else if ((contentOffset > (bottom + pagingThreshold) || velocity < -kVelocityThreshold) && self.nextPage && velocity < 0) { 264 | self.pageViewState = SCPageViewStateTransitionNext; 265 | } else if (self.pageViewState == SCPageViewStateTransitionInterrupted && contentOffset < top) { 266 | [self setContentOffset:CGPointMake(0.0f, 0.0f) animated:YES]; 267 | } else if (self.pageViewState == SCPageViewStateTransitionInterrupted && contentOffset > bottom) { 268 | CGPoint origin = self.direction == SCPagingDirectionVertical ? CGPointMake(0.0f, bottom) : CGPointMake(bottom, 0.0f); 269 | [self setContentOffset:origin animated:YES]; 270 | } 271 | } 272 | 273 | - (CGFloat)_pagingThreshold { 274 | CGFloat result; 275 | if (self.direction == SCPagingDirectionVertical) { 276 | result = self.bounds.size.height * self.pagingThresholdPercent; 277 | } else { 278 | result = self.bounds.size.width * self.pagingThresholdPercent; 279 | } 280 | 281 | if (self.pagingThresholdMinimum > 0.0f && result < self.pagingThresholdMinimum) { 282 | result = self.pagingThresholdMinimum; 283 | } 284 | 285 | return result; 286 | } 287 | 288 | #pragma mark - Page Loading 289 | 290 | - (void)reloadData { 291 | if ([self.pageDelegate respondsToSelector:@selector(numberOfPagesInPageView:)]) { 292 | self.numberOfPages = [self.pageDelegate numberOfPagesInPageView:self]; 293 | } else { 294 | self.numberOfPages = 0; 295 | } 296 | self.activePage = nil; 297 | BOOL hadPrevious = self.previousPage != nil; 298 | BOOL hadNext = self.nextPage != nil; 299 | self.previousPage = nil; 300 | self.nextPage = nil; 301 | if (_currentPageNumber > _numberOfPages - 1) { 302 | _currentPageNumber = _numberOfPages - 1; 303 | } 304 | [self _loadCurrentPage]; 305 | if (hadNext) { 306 | [self _loadNextPage]; 307 | } 308 | if (hadPrevious) { 309 | [self _loadPreviousPage]; 310 | } 311 | } 312 | 313 | #pragma mark - Properties 314 | 315 | - (void)setNumberOfPages:(NSUInteger)numberOfPages { 316 | if (numberOfPages != _numberOfPages) { 317 | _numberOfPages = numberOfPages; 318 | if ([self.pageDelegate respondsToSelector:@selector(pageView:didChangeNumberOfPagesTo:)]) { 319 | [self.pageDelegate pageView:self didChangeNumberOfPagesTo:_numberOfPages]; 320 | } 321 | } 322 | } 323 | 324 | - (void)setCurrentPageNumber:(NSUInteger)currentPageNumber animated:(BOOL)animated fast:(BOOL)fastAnimation { 325 | if (currentPageNumber != _currentPageNumber) { 326 | [self _transitionToPageNumber:currentPageNumber animated:animated fast:fastAnimation]; 327 | } 328 | } 329 | 330 | - (void)setCurrentPageNumber:(NSUInteger)currentPageNumber { 331 | [self _setCurrentPageNumber:currentPageNumber force:NO]; 332 | } 333 | 334 | - (void)_setCurrentPageNumber:(NSUInteger)currentPageNumber force:(BOOL)force { 335 | if (currentPageNumber != _currentPageNumber || force) { 336 | _currentPageNumber = currentPageNumber; 337 | if ([self.pageDelegate respondsToSelector:@selector(pageView:didChangeCurrentPageNumberTo:)]) { 338 | [self.pageDelegate pageView:self didChangeCurrentPageNumberTo:_currentPageNumber]; 339 | } 340 | } 341 | } 342 | 343 | #pragma mark - Page Transitioning 344 | 345 | static CGFloat const fastAnimationSpeed = 0.15f; 346 | static CGFloat const slowAnimationSpeed = 0.3f; 347 | static CGFloat const baseAnimationDelay = 0.15f; 348 | static CGFloat const delayIncrementAmount = 0.025f; 349 | 350 | - (void)_transitionToPageNumber:(NSUInteger)pageNumber animated:(BOOL)animated fast:(BOOL)fastAnimation { 351 | self.previousPage = nil; 352 | self.nextPage = nil; 353 | 354 | if (animated) { 355 | BOOL isForward = pageNumber > self.currentPageNumber; 356 | if (fastAnimation) { 357 | [self _animatePage:pageNumber isFoward:isForward duration:fastAnimationSpeed finalPage:pageNumber delay:baseAnimationDelay]; 358 | } else { 359 | CGFloat pageJump = abs(self.currentPageNumber - pageNumber); 360 | CGFloat duration = (animated ? (pageJump > 1 ? fastAnimationSpeed : slowAnimationSpeed) : 0.0f); 361 | if (isForward) { 362 | [self _animatePage:pageNumber isFoward:isForward duration:duration finalPage:pageNumber delay:0.0f]; 363 | } else { 364 | [self _animatePage:pageNumber isFoward:isForward duration:duration finalPage:pageNumber delay:0.0f]; 365 | } 366 | } 367 | } else { 368 | self.activePage = [self _loadPageNumber:pageNumber]; 369 | [self _setCurrentPageNumber:pageNumber force:YES]; 370 | } 371 | } 372 | 373 | - (void)_animatePage:(NSUInteger)pageIter isFoward:(BOOL)isForward duration:(CGFloat)duration finalPage:(NSUInteger)finalPage delay:(CGFloat)delay { 374 | _currentPageNumber = pageIter; 375 | SCPageContainerView *next = [self _loadPageNumber:pageIter]; 376 | SCPageContainerView *previous = _animatingPage ?: _activePage; 377 | _animatingPage = next; 378 | _activePage = nil; 379 | if (next) { 380 | CGRect frame = self.bounds; 381 | if (self.direction == SCPagingDirectionHorizontal) { 382 | frame.origin.x = isForward ? (frame.size.width + self.gapBetweenPages) : (-frame.size.width - self.gapBetweenPages); 383 | } else { 384 | frame.origin.y = isForward ? (frame.size.height + self.gapBetweenPages) : (-frame.size.height - self.gapBetweenPages); 385 | } 386 | next.frame = frame; 387 | [self addSubview:next]; 388 | 389 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 390 | [UIView animateWithDuration:duration delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{ 391 | CGRect frame = previous.frame; 392 | if (self.direction == SCPagingDirectionHorizontal) { 393 | frame.origin.x = isForward ? (-frame.size.width - self.gapBetweenPages) : (frame.size.width + self.gapBetweenPages); 394 | } else { 395 | frame.origin.y = isForward ? (-frame.size.height - self.gapBetweenPages) : (frame.size.height + self.gapBetweenPages); 396 | } 397 | previous.frame = frame; 398 | next.frame = CGRectMake(0.0f, 0.0f, next.frame.size.width, next.frame.size.height); 399 | } completion:^(BOOL finished) { 400 | [previous removeFromSuperview]; 401 | if (_animatingPage == next) { 402 | self.activePage = next; 403 | [self _setCurrentPageNumber:finalPage force:YES]; 404 | _animatingPage = nil; 405 | } 406 | }]; 407 | }); 408 | } 409 | } 410 | 411 | #pragma mark - Pages 412 | 413 | - (void)setPreviousPage:(SCPageContainerView *)previousPage { 414 | if (previousPage != _previousPage) { 415 | [_previousPage removeFromSuperview]; 416 | _previousPage = previousPage; 417 | if (_previousPage) { 418 | self.previousGapView.hidden = NO; 419 | [self addSubview:_previousPage]; 420 | [self setNeedsLayout]; 421 | } else { 422 | self.previousGapView.hidden = YES; 423 | } 424 | } 425 | } 426 | 427 | - (void)setActivePage:(SCPageContainerView *)activePage { 428 | if (activePage != _activePage) { 429 | [_activePage removeFromSuperview]; 430 | _activePage = activePage; 431 | if (_activePage) { 432 | [self addSubview:_activePage]; 433 | [self setNeedsLayout]; 434 | if ([self.pageDelegate respondsToSelector:@selector(pageDidBecomeActive:page:pageView:)]) { 435 | [self.pageDelegate pageDidBecomeActive:self.currentPageNumber page:_activePage pageView:self]; 436 | } 437 | } 438 | } 439 | } 440 | 441 | - (void)setNextPage:(SCPageContainerView *)nextPage { 442 | if (nextPage != _nextPage) { 443 | [_nextPage removeFromSuperview]; 444 | _nextPage = nextPage; 445 | if (_nextPage) { 446 | self.nextGapView.hidden = NO; 447 | [self addSubview:_nextPage]; 448 | [self setNeedsLayout]; 449 | } else { 450 | self.nextGapView.hidden = YES; 451 | } 452 | } 453 | } 454 | 455 | #pragma mark - Page Loading 456 | 457 | - (void)_loadCurrentPage { 458 | if (self.numberOfPages > 0 && self.currentPageNumber < self.numberOfPages) { 459 | self.activePage = [self _loadPageNumber:self.currentPageNumber]; 460 | } 461 | } 462 | 463 | - (void)_loadPreviousPage { 464 | if (self.numberOfPages > 1 && self.currentPageNumber > 0) { 465 | self.previousPage = [self _loadPageNumber:self.currentPageNumber-1]; 466 | } 467 | } 468 | 469 | - (void)_loadNextPage { 470 | if (self.numberOfPages > 1 && self.currentPageNumber < self.numberOfPages - 1) { 471 | self.nextPage = [self _loadPageNumber:self.currentPageNumber+1]; 472 | } 473 | } 474 | 475 | - (SCPageContainerView *)_loadPageNumber:(NSUInteger)pageNumber { 476 | UIView *page; 477 | if ([self.pageDelegate respondsToSelector:@selector(pageForPageNumber:inPageView:)]) { 478 | page = [self.pageDelegate pageForPageNumber:pageNumber inPageView:self]; 479 | } 480 | 481 | UIView *header; 482 | if ([self.pageDelegate respondsToSelector:@selector(headerViewForPageNumber:inPageView:)]) { 483 | header = [self.pageDelegate headerViewForPageNumber:pageNumber inPageView:self]; 484 | } 485 | 486 | SCPageContainerView *result = [[SCPageContainerView alloc] initWithFrame:self.bounds header:header page:page direction:self.direction]; 487 | result.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 488 | return result; 489 | } 490 | 491 | #pragma mark - State 492 | 493 | - (void)setPageViewState:(SCPageViewState)pageViewState { 494 | if (pageViewState != _pageViewState) { 495 | SCPageViewState previous; 496 | if (pageViewState == SCPageViewStateTransitionNextImmediately || pageViewState == SCPageViewStateTransitionPreviousImmediately) { 497 | previous = pageViewState; 498 | _pageViewState = SCPageViewStateResting; 499 | } else { 500 | previous = _pageViewState; 501 | _pageViewState = pageViewState; 502 | } 503 | 504 | switch (_pageViewState) { 505 | case SCPageViewStateResting: { 506 | [self.transitionView removeFromSuperview]; 507 | SCPageContainerView *active = self.activePage; 508 | if (previous == SCPageViewStateTransitionPrevious || previous == SCPageViewStateTransitionPreviousImmediately) { 509 | active = self.previousPage; 510 | _previousPage = nil; 511 | self.nextPage = nil; 512 | self.currentPageNumber -= 1; 513 | } else if (previous == SCPageViewStateTransitionNext || previous == SCPageViewStateTransitionNextImmediately) { 514 | active = self.nextPage; 515 | _nextPage = nil; 516 | self.previousPage = nil; 517 | self.currentPageNumber += 1; 518 | } else { 519 | self.previousPage = nil; 520 | self.nextPage = nil; 521 | } 522 | self.activePage = active; 523 | self.scrollFinalPosition = nil; 524 | self.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f); 525 | break; 526 | } 527 | case SCPageViewStateTransitionNext: { 528 | self.transitionView.frame = self.bounds; 529 | [self addSubview:self.transitionView]; 530 | if (self.direction == SCPagingDirectionVertical) { 531 | self.scrollFinalPosition = @(self.activePage.frame.size.height); 532 | [self setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, self.nextPage.frame.size.height, 0.0f)]; 533 | if (self.contentOffset.y < [self.scrollFinalPosition floatValue]) { 534 | [self setContentOffset:CGPointMake(0.0f, [self.scrollFinalPosition floatValue]) animated:YES]; 535 | } 536 | } else { 537 | self.scrollFinalPosition = @(self.activePage.frame.size.width); 538 | [self setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, self.nextPage.frame.size.width)]; 539 | if (self.contentOffset.x < [self.scrollFinalPosition floatValue]) { 540 | [self setContentOffset:CGPointMake([self.scrollFinalPosition floatValue], 0.0f) animated:YES]; 541 | } 542 | } 543 | break; 544 | } 545 | case SCPageViewStateTransitionPrevious: { 546 | self.transitionView.frame = self.bounds; 547 | [self addSubview:self.transitionView]; 548 | if (self.direction == SCPagingDirectionVertical) { 549 | self.scrollFinalPosition = @(-self.previousPage.frame.size.height); 550 | [self setContentInset:UIEdgeInsetsMake(self.previousPage.frame.size.height, 0.0f, 0.0f, 0.0f)]; 551 | if (self.contentOffset.y > [self.scrollFinalPosition floatValue]) { 552 | [self setContentOffset:CGPointMake(0.0f, [self.scrollFinalPosition floatValue]) animated:YES]; 553 | } 554 | } else { 555 | self.scrollFinalPosition = @(-self.previousPage.frame.size.width); 556 | [self setContentInset:UIEdgeInsetsMake(0.0f, self.previousPage.frame.size.width, 0.0f, 0.0f)]; 557 | if (self.contentOffset.x > [self.scrollFinalPosition floatValue]) { 558 | [self setContentOffset:CGPointMake([self.scrollFinalPosition floatValue], 0.0f) animated:YES]; 559 | } 560 | } 561 | break; 562 | } case SCPageViewStateTransitionInterrupted: { 563 | break; 564 | } 565 | default: 566 | break; 567 | } 568 | } 569 | } 570 | 571 | #pragma mark - Configuration 572 | 573 | - (void)setDirection:(SCPagingDirection)direction { 574 | if (direction != _direction) { 575 | _direction = direction; 576 | [self _configureGapViews]; 577 | [self setNeedsLayout]; 578 | } 579 | } 580 | 581 | - (void)setGapBetweenPages:(CGFloat)gapBetweenPages { 582 | if (gapBetweenPages != _gapBetweenPages) { 583 | // sanitize the value 584 | if (gapBetweenPages < 0.0f) { 585 | _gapBetweenPages = 0.0f; 586 | } else { 587 | _gapBetweenPages = floorf(gapBetweenPages); 588 | } 589 | [self _configureGapViews]; 590 | [self setNeedsLayout]; 591 | } 592 | } 593 | 594 | #pragma mark - Gap Views 595 | 596 | - (void)_configureGapViews { 597 | if (self.gapBetweenPages > 0.0f) { 598 | [self addSubview:self.nextGapView]; 599 | [self addSubview:self.previousGapView]; 600 | if (self.direction == SCPagingDirectionHorizontal) { 601 | self.nextGapView.frame = CGRectMake(0.0f, 0.0f, self.gapBetweenPages, self.bounds.size.height); 602 | self.nextGapView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 603 | } else { 604 | self.nextGapView.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, self.gapBetweenPages); 605 | self.nextGapView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 606 | } 607 | self.previousGapView.frame = self.nextGapView.frame; 608 | self.previousGapView.autoresizingMask = self.nextGapView.autoresizingMask; 609 | } else { 610 | [self.nextGapView removeFromSuperview]; 611 | [self.previousGapView removeFromSuperview]; 612 | } 613 | } 614 | 615 | #pragma mark - KVO 616 | 617 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 618 | if ([keyPath isEqualToString:@"contentOffset"]) { 619 | CGPoint offsetPoint = [[change objectForKey:NSKeyValueChangeNewKey] CGPointValue]; 620 | 621 | // adjust the transition view 622 | if (self.transitionView.superview) { 623 | CGRect frame = self.transitionView.frame; 624 | frame.origin = offsetPoint; 625 | self.transitionView.frame = frame; 626 | } 627 | 628 | CGFloat offset; 629 | CGFloat selfBoundsSize; 630 | CGFloat activePageSize; 631 | if (self.direction == SCPagingDirectionVertical) { 632 | offset = offsetPoint.y; 633 | selfBoundsSize = self.bounds.size.height; 634 | activePageSize = self.activePage.frame.size.height; 635 | } else { 636 | offset = offsetPoint.x; 637 | selfBoundsSize = self.bounds.size.width; 638 | activePageSize = self.activePage.frame.size.width; 639 | } 640 | 641 | // make the header view of the active page stickyickyicky 642 | if (self.activePage.headerView) { 643 | CGFloat origin = (offset > 0) && (!self.nextPage || offset <= (activePageSize - selfBoundsSize)) ? fabsf(offset) : 0.0f; 644 | CGRect frame = self.activePage.headerView.frame; 645 | frame.origin = (self.direction == SCPagingDirectionVertical) ? CGPointMake(0.0f, origin) : CGPointMake(origin, 0.0f); 646 | self.activePage.headerView.frame = frame; 647 | } 648 | 649 | // previous page shiat 650 | if (self.pageViewState == SCPageViewStateTransitionPrevious && offset == [self.scrollFinalPosition floatValue]) { 651 | self.pageViewState = SCPageViewStateResting; 652 | } else if (self.pageViewState == SCPageViewStateResting && offset < 0 && self.previousPage == nil && self.currentPageNumber > 0) { 653 | [self _loadPreviousPage]; 654 | } else if (self.pageViewState == SCPageViewStateResting && offset > 0 && self.previousPage) { 655 | self.previousPage = nil; 656 | } 657 | 658 | // next page shiat 659 | if (self.pageViewState == SCPageViewStateTransitionNext && offset == [self.scrollFinalPosition floatValue]) { 660 | self.pageViewState = SCPageViewStateResting; 661 | } else if (self.pageViewState == SCPageViewStateResting && (offset + selfBoundsSize) > activePageSize && self.nextPage == nil) { 662 | [self _loadNextPage]; 663 | } else if (self.pageViewState == SCPageViewStateResting && (offset + selfBoundsSize) < activePageSize && self.nextPage) { 664 | self.nextPage = nil; 665 | } 666 | 667 | if (self.pageViewState == SCPageViewStateTransitionInterrupted) { 668 | if (offset >= 0 && offset <= (activePageSize - selfBoundsSize)) { 669 | self.pageViewState = SCPageViewStateResting; 670 | } else if (offset > activePageSize && self.currentPageNumber < (self.numberOfPages - 1)) { 671 | // If the user manages to pan completely past the active page, transition to the next page 672 | self.pageViewState = SCPageViewStateTransitionNextImmediately; 673 | } else if (offset < -activePageSize && self.currentPageNumber > 0) { 674 | // If the user manages to pane completely past the active page, tranisition to the prev page 675 | self.pageViewState = SCPageViewStateTransitionPreviousImmediately; 676 | } 677 | } 678 | } 679 | } 680 | 681 | @end 682 | 683 | @implementation SCPageContainerView 684 | 685 | - (id)initWithFrame:(CGRect)frame header:(UIView *)header page:(UIView *)page direction:(SCPagingDirection)direction { 686 | if (self = [super initWithFrame:frame]) { 687 | self.clipsToBounds = YES; 688 | _direction = direction; 689 | self.headerView = header; 690 | header.clipsToBounds = YES; 691 | frame = header.frame; 692 | frame.origin = CGPointZero; 693 | frame.size = _direction == SCPagingDirectionVertical ? CGSizeMake(self.bounds.size.width, header.frame.size.height) : CGSizeMake(header.frame.size.width, self.bounds.size.height); 694 | header.frame = frame; 695 | header.autoresizingMask = _direction == SCPagingDirectionVertical ? UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin : UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin; 696 | [self addSubview:header]; 697 | 698 | self.pageView = page; 699 | frame = page.frame; 700 | if (_direction == SCPagingDirectionVertical) { 701 | frame.origin = CGPointMake(0.0f, header.frame.size.height); 702 | frame.size = CGSizeMake(self.bounds.size.width, self.bounds.size.height - header.frame.size.height); 703 | } else { 704 | frame.origin = CGPointMake(header.frame.size.width, 0.0f); 705 | frame.size = CGSizeMake(self.bounds.size.width - header.frame.size.width, self.bounds.size.height); 706 | } 707 | page.frame = frame; 708 | page.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 709 | [self addSubview:page]; 710 | 711 | [self bringSubviewToFront:header]; 712 | } 713 | return self; 714 | } 715 | 716 | - (CGSize)sizeThatFits:(CGSize)size { 717 | CGSize pageSize = [self.pageView sizeThatFits:size]; 718 | if (_direction == SCPagingDirectionVertical) { 719 | return CGSizeMake(size.width, pageSize.height + self.headerView.frame.size.height); 720 | } else { 721 | return CGSizeMake(pageSize.width + self.headerView.frame.size.width, size.height); 722 | } 723 | } 724 | 725 | - (void)layoutSubviews { 726 | if (_direction == SCPagingDirectionVertical) { 727 | self.pageView.frame = CGRectMake(0.0f, self.headerView.frame.size.height, self.bounds.size.width, self.bounds.size.height - self.headerView.frame.size.height); 728 | } else { 729 | self.pageView.frame = CGRectMake(self.headerView.frame.size.width, 0.0f, self.bounds.size.width - self.headerView.frame.size.width, self.bounds.size.height); 730 | } 731 | } 732 | 733 | @end 734 | -------------------------------------------------------------------------------- /Source/SCPageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageViewDelegate.h" 25 | #import "SCViewController.h" 26 | 27 | @class SCPageIndicatorView; 28 | 29 | @interface SCPageViewController : SCViewController 30 | 31 | @property (nonatomic, weak, readonly) SCPageView *pageView; 32 | @property (nonatomic, weak, readonly) SCPageIndicatorView *pageIndicator; 33 | @property (nonatomic, strong) NSArray *pages; 34 | 35 | - (void)showPageIndicator; 36 | - (void)hidePageIndicator; 37 | - (SCPageIndicatorView *)createPageIndicatorWithFrame:(CGRect)frame; 38 | - (void)configurePageIndicator:(SCPageIndicatorView *)pageIndicatorView; 39 | 40 | - (UIViewController *)controllerAtPageNumber:(NSUInteger)pageNumber; 41 | 42 | @end 43 | 44 | @interface UIViewController (SCPageViewControllerAdditions) 45 | 46 | @property (nonatomic, weak, readonly) SCPageViewController *pageViewController; 47 | 48 | @end -------------------------------------------------------------------------------- /Source/SCPageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageViewController.h" 25 | #import "SCPageView.h" 26 | #import "SCPageIndicatorView.h" 27 | 28 | @interface SCPageViewController () { 29 | BOOL _dataLoaded; 30 | BOOL _showPageIndicator; 31 | } 32 | 33 | @property (nonatomic, weak) SCPageView *pageView; 34 | @property (nonatomic, weak) SCPageIndicatorView *pageIndicator; 35 | 36 | @end 37 | 38 | @implementation SCPageViewController 39 | 40 | #pragma mark - UIViewController 41 | 42 | - (void)loadView { 43 | UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 44 | 45 | SCPageView *pageView = [[SCPageView alloc] initWithFrame:view.bounds]; 46 | pageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 47 | self.pageView = pageView; 48 | [view addSubview:pageView]; 49 | 50 | self.view = view; 51 | } 52 | 53 | - (void)viewDidLoad { 54 | [super viewDidLoad]; 55 | self.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; 56 | _dataLoaded = NO; 57 | 58 | if (_showPageIndicator) { 59 | [self showPageIndicator]; 60 | } 61 | } 62 | 63 | - (void)viewWillAppear:(BOOL)animated { 64 | [super viewWillAppear:animated]; 65 | if (!_dataLoaded) { 66 | _dataLoaded = YES; 67 | [self.pageView reloadData]; 68 | } 69 | } 70 | 71 | #pragma mark - Page Indicator 72 | 73 | - (void)showPageIndicator { 74 | _showPageIndicator = YES; 75 | if (self.isViewLoaded) { 76 | SCPageIndicatorView *pageIndicator = self.pageIndicator; 77 | CGRect frame = CGRectMake(self.pageView.frame.origin.x, self.view.bounds.size.height - 44.0f, self.pageView.frame.size.width, 44.0f); 78 | if (!pageIndicator) { 79 | pageIndicator = [self createPageIndicatorWithFrame:frame]; 80 | pageIndicator.pageIndicatorDelegate = self; 81 | pageIndicator.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; 82 | [self configurePageIndicator:pageIndicator]; 83 | self.pageIndicator = pageIndicator; 84 | } else { 85 | pageIndicator.frame = frame; 86 | } 87 | pageIndicator.numberOfPages = [self numberOfPagesInPageView:self.pageView]; 88 | [self.view addSubview:pageIndicator]; 89 | 90 | frame = self.pageView.frame; 91 | frame.size.height = self.view.bounds.size.height - pageIndicator.frame.size.height; 92 | self.pageView.frame = frame; 93 | } 94 | } 95 | 96 | - (void)hidePageIndicator { 97 | _showPageIndicator = NO; 98 | if (self.pageIndicator) { 99 | [self.pageIndicator removeFromSuperview]; 100 | self.pageIndicator = nil; 101 | } 102 | if (self.isViewLoaded) { 103 | CGRect frame = self.pageView.frame; 104 | frame.size.height = self.view.bounds.size.height; 105 | self.pageView.frame = frame; 106 | } 107 | } 108 | 109 | - (SCPageIndicatorView *)createPageIndicatorWithFrame:(CGRect)frame { 110 | return [[SCPageIndicatorView alloc] initWithFrame:frame]; 111 | } 112 | 113 | - (void)configurePageIndicator:(SCPageIndicatorView *)pageIndicatorView { 114 | // subclasses can override to customize look of page indicator 115 | } 116 | 117 | #pragma mark - Properties 118 | 119 | - (void)setPageView:(SCPageView *)pageView { 120 | if (pageView != _pageView) { 121 | _pageView.pageDelegate = nil; 122 | _pageView.delegate = nil; 123 | 124 | _pageView = pageView; 125 | 126 | _pageView.delegate = self; 127 | _pageView.pageDelegate = self; 128 | } 129 | } 130 | 131 | - (void)setPages:(NSArray *)pages { 132 | if (pages != _pages) { 133 | for (id obj in _pages) { 134 | if ([obj isKindOfClass:[UIViewController class]]) { 135 | UIViewController *child = obj; 136 | [child willMoveToParentViewController:nil]; 137 | if (child.isViewLoaded) { 138 | [child.view removeFromSuperview]; 139 | } 140 | [child removeFromParentViewController]; 141 | } 142 | } 143 | _pages = pages; 144 | for (id obj in _pages) { 145 | if ([obj isKindOfClass:[UIViewController class]]) { 146 | UIViewController *child = obj; 147 | [self addChildViewController:child]; 148 | [child didMoveToParentViewController:self]; 149 | } 150 | } 151 | if (self.isViewLoaded) { 152 | [self.pageView reloadData]; 153 | } 154 | } 155 | } 156 | 157 | #pragma mark - SCPageViewDelegate 158 | 159 | - (void)pageView:(SCPageView *)pageView didChangeNumberOfPagesTo:(NSUInteger)numberOfPages { 160 | self.pageIndicator.numberOfPages = numberOfPages; 161 | } 162 | 163 | - (void)pageView:(SCPageView *)pageView didChangeCurrentPageNumberTo:(NSUInteger)currentPage { 164 | self.pageIndicator.currentPage = currentPage; 165 | } 166 | 167 | - (NSUInteger)numberOfPagesInPageView:(SCPageView *)pageView { 168 | return [self.pages count]; 169 | } 170 | 171 | - (UIView *)pageForPageNumber:(NSUInteger)pageNumber inPageView:(SCPageView *)pageView { 172 | return [self controllerAtPageNumber:pageNumber].view; 173 | } 174 | 175 | - (UIView *)headerViewForPageNumber:(NSUInteger)pageNumber inPageView:(SCPageView *)pageView { 176 | return nil; 177 | } 178 | 179 | #pragma mark - Helpers 180 | 181 | - (UIViewController *)controllerAtPageNumber:(NSUInteger)pageNumber { 182 | if (pageNumber < [self.pages count]) { 183 | id page = [self.pages objectAtIndex:pageNumber]; 184 | if ([page isKindOfClass:[UIViewController class]]) { 185 | return (UIViewController *)page; 186 | } 187 | } 188 | return nil; 189 | } 190 | 191 | #pragma mark - SCPageIndicatorDelegate 192 | 193 | - (void)requestPageChangeTo:(NSUInteger)pageNumber panning:(BOOL)isPanning { 194 | [self.pageView setCurrentPageNumber:pageNumber animated:YES fast:isPanning]; 195 | } 196 | 197 | @end 198 | 199 | #pragma mark - SCPageView 200 | 201 | @implementation UIViewController (SCPageViewControllerAdditions) 202 | 203 | + (id)ancestorOfType:(Class)klass for:(UIViewController *)child { 204 | UIViewController *iter = child.parentViewController; 205 | while (iter) { 206 | if ([iter isKindOfClass:klass]) { 207 | return iter; 208 | } else if (iter.parentViewController && iter.parentViewController != iter) { 209 | iter = iter.parentViewController; 210 | } else { 211 | iter = nil; 212 | } 213 | } 214 | return nil; 215 | } 216 | 217 | - (SCPageViewController *)pageViewController { 218 | return [[self class] ancestorOfType:[SCPageViewController class] for:self]; 219 | } 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /Source/SCPageViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | typedef enum _SCPagingDirection { 27 | SCPagingDirectionVertical = 0, 28 | SCPagingDirectionHorizontal = 1 29 | } SCPagingDirection; 30 | 31 | @class SCPageView; 32 | 33 | @protocol SCPageViewDelegate 34 | 35 | - (NSUInteger)numberOfPagesInPageView:(SCPageView *)pageView; 36 | - (UIView *)pageForPageNumber:(NSUInteger)pageNumber inPageView:(SCPageView *)pageView; 37 | 38 | @optional 39 | 40 | - (void)pageDidBecomeActive:(NSUInteger)pageNumber page:(UIView *)page pageView:(SCPageView *)pageView; 41 | - (UIView *)headerViewForPageNumber:(NSUInteger)pageNumber inPageView:(SCPageView *)pageView; 42 | - (void)pageView:(SCPageView *)pageView didChangeNumberOfPagesTo:(NSUInteger)numberOfPages; 43 | - (void)pageView:(SCPageView *)pageView didChangeCurrentPageNumberTo:(NSUInteger)currentPage; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Source/SCPagingGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPageViewController.h" 25 | #import "SCGridViewDelegate.h" 26 | 27 | @interface SCPagingGridViewController : SCPageViewController 28 | 29 | /* The schema determines how the GridView pages will be laid out. 30 | * 31 | * This property must be set to an array of integer arrays. The paging gridview 32 | * will cycle through the schema array, using the first entry for the first page, 33 | * the second entry for the second page, etc. When it reaches the end of the 34 | * schema array, it repeats the cycle from the beginning. 35 | * 36 | * Example 1: 37 | * self.schema = @[ @[@(2), @(2)], @[@(1), @(3)] ]; 38 | * The first page will be a grid with 2 rows, each row has 2 columns. 39 | * The second page will be a grid with 2 rows, the first row has 1 column. The second row has 3 columns. 40 | * The third page will have the same schema as the first. 41 | * 42 | * Example 2: 43 | * self.schema = @[ @[@(1), @(3), @(2)] ]; 44 | * Every page will have 3 rows. The first row has 1 column, the second row has 3 columns, 45 | * the third row has 2 columns. 46 | * 47 | */ 48 | @property (nonatomic, strong) NSArray *schema; 49 | 50 | // optional, ignored if less than or equal to zero 51 | @property (nonatomic, assign) NSUInteger maxNumberOfPages; 52 | 53 | @property (nonatomic, assign, readonly) NSUInteger totalPageSizes; 54 | 55 | #pragma mark - Calculations 56 | 57 | - (NSUInteger)offsetForPageNumber:(NSUInteger)pageNumber; 58 | - (NSNumber *)sizeForPageSchema:(NSArray *)schema; 59 | - (NSArray *)schemaForPageNumber:(NSUInteger)pageNumber; 60 | 61 | #pragma mark - Cells 62 | 63 | - (NSInteger)numberOfCellsInPageView:(SCPageView *)pageView; 64 | 65 | #pragma mark - Subclass Override Methods 66 | 67 | - (void)configureGridView:(SCGridView *)gridView forPageNumber:(NSUInteger)pageNumber; 68 | - (NSArray *)schemaForShortPage:(NSUInteger)pageNumber numberOfCells:(NSUInteger)numberOfCells originalSchema:(NSArray *)original inGridView:(SCGridView *)gridView; 69 | - (Class)cellClass; 70 | - (void)configureCell:(UIView *)cell atPosition:(NSUInteger)position; 71 | - (void)didSelectCell:(UIView *)cell atPosition:(NSUInteger)position; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Source/SCPagingGridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCPagingGridViewController.h" 25 | #import "SCViewRecycler.h" 26 | #import "SCGridView.h" 27 | #import "SCPageView.h" 28 | 29 | @interface SCPagingGridViewController () 30 | 31 | @property (nonatomic, strong) NSArray *pageSizes; 32 | @property (nonatomic, assign) NSUInteger totalPageSizes; 33 | @property (nonatomic, strong) SCViewRecycler *gridRecycler; 34 | @property (nonatomic, strong) SCViewRecycler *cellRecycler; 35 | 36 | @end 37 | 38 | @implementation SCPagingGridViewController 39 | 40 | - (id)init { 41 | if (self = [super init]) { 42 | _gridRecycler = [[SCViewRecycler alloc] initWithViewClass:[SCGridView class]]; 43 | _cellRecycler = [[SCViewRecycler alloc] initWithViewClass:[self cellClass]]; 44 | } 45 | return self; 46 | } 47 | 48 | #pragma mark - UIViewController 49 | 50 | - (void)viewDidLoad { 51 | [super viewDidLoad]; 52 | self.pageView.direction = SCPagingDirectionHorizontal; 53 | } 54 | 55 | #pragma mark - SCPageViewDelegate 56 | 57 | - (NSUInteger)numberOfPagesInPageView:(SCPageView *)pageView { 58 | NSInteger numberOfCells = [self numberOfCellsInPageView:pageView]; 59 | NSInteger numberOfPages = 0; 60 | while (numberOfCells > 0 && self.totalPageSizes > 0 && (_maxNumberOfPages <= 0 || numberOfPages < _maxNumberOfPages)) { 61 | for (NSNumber *pageSize in self.pageSizes) { 62 | ++numberOfPages; 63 | numberOfCells -= [pageSize integerValue]; 64 | if (numberOfCells <= 0 || (_maxNumberOfPages > 0 && numberOfPages >= _maxNumberOfPages)) { 65 | break; 66 | } 67 | } 68 | } 69 | return numberOfPages; 70 | } 71 | 72 | - (NSInteger)numberOfCellsInPageView:(SCPageView *)pageView { 73 | return 0; 74 | } 75 | 76 | - (UIView *)pageForPageNumber:(NSUInteger)pageNumber inPageView:(SCPageView *)pageView { 77 | SCGridView *result = [self.gridRecycler generateView]; 78 | [self configureGridView:result forPageNumber:pageNumber]; 79 | result.delegate = self; 80 | NSArray *schema = [self schemaForPageNumber:pageNumber]; 81 | NSUInteger offset = [self offsetForPageNumber:pageNumber]; 82 | NSUInteger count = [self numberOfCellsInPageView:pageView]; 83 | NSMutableArray *cells = [NSMutableArray array]; 84 | for (int i = 0; i < [[self sizeForPageSchema:schema] integerValue]; ++i) { 85 | NSUInteger position = offset + i; 86 | if (position < count) { 87 | UIView *view = [self.cellRecycler generateView]; 88 | [self configureCell:view atPosition:position]; 89 | [cells addObject:view]; 90 | } else { 91 | schema = [self schemaForShortPage:pageNumber numberOfCells:i originalSchema:schema inGridView:result]; 92 | break; 93 | } 94 | } 95 | result.schema = schema; 96 | result.cells = cells; 97 | return result; 98 | } 99 | 100 | #pragma mark - SCGridViewDelegate 101 | 102 | - (void)gridView:(SCGridView *)gridView didSelectCell:(UIView *)cell atIndex:(NSUInteger)index { 103 | [self didSelectCell:cell atPosition:index + [self offsetForPageNumber:self.pageView.currentPageNumber]]; 104 | } 105 | 106 | #pragma mark - Public Methods 107 | 108 | - (void)setSchema:(NSArray *)schema { 109 | _totalPageSizes = 0; 110 | NSMutableArray *sizes = [NSMutableArray arrayWithCapacity:[schema count]]; 111 | for (id obj in schema) { 112 | NSNumber *size = [self sizeForPageSchema:obj]; 113 | [sizes addObject:size]; 114 | _totalPageSizes += [size integerValue]; 115 | 116 | } 117 | _pageSizes = [sizes copy]; 118 | _schema = schema; 119 | [self.pageView reloadData]; 120 | } 121 | 122 | #pragma mark - Calcumalations 123 | 124 | - (NSUInteger)offsetForPageNumber:(NSUInteger)pageNumber { 125 | NSUInteger numberOfPageTypes = [self.schema count]; 126 | if (numberOfPageTypes > 0) { 127 | // say there are 3 schema types [2,1,2], [3,3,3], and [1,2,3,4], how many times have we cycled through these? 128 | NSUInteger numberOfSchemaCycles = floor(pageNumber / numberOfPageTypes); 129 | NSUInteger result = numberOfSchemaCycles * self.totalPageSizes; 130 | NSUInteger remainder = pageNumber % numberOfPageTypes; 131 | if (remainder > 0) { 132 | for (NSUInteger i = 0; i < remainder; ++i) { 133 | result += [[self.pageSizes objectAtIndex:i] integerValue]; 134 | } 135 | } 136 | return result; 137 | } 138 | return 0; 139 | } 140 | 141 | - (NSNumber *)sizeForPageSchema:(NSArray *)schema { 142 | NSInteger result = 0; 143 | for (NSNumber *pageSchema in schema) { 144 | result += [pageSchema integerValue]; 145 | } 146 | return @(result); 147 | } 148 | 149 | - (NSArray *)schemaForPageNumber:(NSUInteger)pageNumber { 150 | return [self.schema objectAtIndex:pageNumber % [self.schema count]]; 151 | } 152 | 153 | #pragma mark - Methods for subclass to override 154 | 155 | - (Class)cellClass { 156 | return [UIView class]; 157 | } 158 | 159 | - (void)configureGridView:(SCGridView *)gridView forPageNumber:(NSUInteger)pageNumber { 160 | gridView.backgroundColor = [UIColor blackColor]; 161 | gridView.rowSpacing = 1.0f; 162 | gridView.colSpacing = 1.0f; 163 | } 164 | 165 | - (void)configureCell:(UIView *)cell atPosition:(NSUInteger)position { 166 | // do nothing by default 167 | } 168 | 169 | - (void)didSelectCell:(UIView *)cell atPosition:(NSUInteger)position { 170 | // do nothing by default 171 | } 172 | 173 | - (NSArray *)schemaForShortPage:(NSUInteger)pageNumber numberOfCells:(NSUInteger)numberOfCells originalSchema:(NSArray *)original inGridView:(SCGridView *)gridView { 174 | // sublcass can override to provide for better handling of short pages 175 | return original; 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /Source/SCSwizzle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL); 27 | -------------------------------------------------------------------------------- /Source/SCSwizzle.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCSwizzle.h" 25 | #import 26 | 27 | void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL) 28 | { 29 | Method origMethod = class_getInstanceMethod(c, origSEL); 30 | Method overrideMethod = class_getInstanceMethod(c, overrideSEL); 31 | if(class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) { 32 | class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); 33 | } else { 34 | method_exchangeImplementations(origMethod, overrideMethod); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/SCViewRecycler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface SCViewRecycler : NSObject 27 | 28 | - (id)initWithViewClass:(Class)klass; 29 | - (id)generateView; 30 | - (void)clearCache; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Source/SCViewRecycler.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Andersen on 11/1/12. 3 | // Copyright (c) 2012 Scribd. All rights reserved. 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 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | 24 | #import "SCViewRecycler.h" 25 | #import "SCSwizzle.h" 26 | 27 | @implementation UIView (SCViewRecycling) 28 | 29 | - (void)SCViewRecycling_willMoveToSuperview:(UIView *)view { 30 | // call kvo method 31 | [self willChangeValueForKey:@"superview"]; 32 | // call through to original 33 | [self SCViewRecycling_willMoveToSuperview:view]; 34 | } 35 | 36 | - (void)SCViewRecycling_didMoveToSuperview { 37 | // call through to original 38 | [self SCViewRecycling_didMoveToSuperview]; 39 | // call kvo method 40 | [self didChangeValueForKey:@"superview"]; 41 | } 42 | 43 | @end 44 | 45 | @interface SCViewRecycler () { 46 | Class _klass; 47 | } 48 | 49 | @property (nonatomic, strong) NSMutableArray *availableQueue; 50 | @property (nonatomic, strong) NSMutableArray *unavailableQueue; 51 | 52 | @end 53 | 54 | @implementation SCViewRecycler 55 | 56 | - (void)dealloc { 57 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 58 | 59 | // unhook KVO 60 | for (id obj in _availableQueue) { 61 | if ([obj isKindOfClass:[UIView class]]) { 62 | [obj removeObserver:self forKeyPath:@"superview"]; 63 | } 64 | } 65 | 66 | for (id obj in _unavailableQueue) { 67 | if ([obj isKindOfClass:[UIView class]]) { 68 | [obj removeObserver:self forKeyPath:@"superview"]; 69 | } 70 | } 71 | } 72 | 73 | - (id)initWithViewClass:(Class)klass { 74 | if (self = [super init]) { 75 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_memoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 76 | 77 | _klass = klass; 78 | [self _swizzleClass:_klass]; 79 | 80 | _availableQueue = [NSMutableArray arrayWithCapacity:20]; 81 | _unavailableQueue = [NSMutableArray arrayWithCapacity:20]; 82 | } 83 | return self; 84 | } 85 | 86 | #pragma mark - public methods 87 | 88 | - (void)_swizzleClass:(Class)klass { 89 | // must, must, must ensure this method is run on main thread or we risk swizzling the same class multiple times 90 | if (![NSThread isMainThread]) { 91 | dispatch_async(dispatch_get_main_queue(), ^{ 92 | [self _swizzleClass:klass]; 93 | }); 94 | return; 95 | } 96 | 97 | static NSMutableArray *swizzled; 98 | static dispatch_once_t onceToken; 99 | dispatch_once(&onceToken, ^{ 100 | swizzled = [NSMutableArray array]; 101 | }); 102 | 103 | if (![swizzled containsObject:klass]) { 104 | MethodSwizzle(klass, @selector(willMoveToSuperview:), @selector(SCViewRecycling_willMoveToSuperview:)); 105 | MethodSwizzle(klass, @selector(didMoveToSuperview), @selector(SCViewRecycling_didMoveToSuperview)); 106 | [swizzled addObject:klass]; 107 | } 108 | } 109 | 110 | - (id)generateView { 111 | id result = nil; 112 | if ([self.availableQueue count] > 0) { 113 | NSUInteger last = [self.availableQueue count] - 1; 114 | id object = [self.availableQueue objectAtIndex:last]; 115 | [self.availableQueue removeObjectAtIndex:last]; 116 | if ([object isKindOfClass:_klass]) { 117 | result = object; 118 | } 119 | } 120 | if (!result) { 121 | result = [[_klass alloc] init]; 122 | [result addObserver:self forKeyPath:@"superview" options:0 context:nil]; 123 | } 124 | [self.unavailableQueue addObject:result]; 125 | return result; 126 | } 127 | 128 | - (void)clearCache { 129 | if (![NSThread isMainThread]) { 130 | dispatch_async(dispatch_get_main_queue(), ^{ 131 | [self clearCache]; 132 | }); 133 | return; 134 | } 135 | 136 | for (id obj in self.availableQueue) { 137 | if ([obj isKindOfClass:[UIView class]]) { 138 | [obj removeObserver:self forKeyPath:@"superview"]; 139 | } 140 | } 141 | 142 | [self.availableQueue removeAllObjects]; 143 | } 144 | 145 | #pragma mark - KVO 146 | 147 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 148 | if ([keyPath isEqualToString:@"superview"] && [object isKindOfClass:[UIView class]]) { 149 | UIView *view = object; 150 | if (!view.superview) { 151 | [self.unavailableQueue removeObject:view]; 152 | [self.availableQueue addObject:view]; 153 | } 154 | } 155 | } 156 | 157 | #pragma mark - memory warning 158 | 159 | - (void)_memoryWarning:(NSNotification *)notification { 160 | [self clearCache]; 161 | } 162 | 163 | @end 164 | --------------------------------------------------------------------------------