├── Demo ├── AppDelegate.h ├── AppDelegate.m ├── DataViewController.h ├── DataViewController.m ├── DataViewController.xib ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── ModelController.h ├── ModelController.m ├── PageViewController.h ├── PageViewController.m ├── PageViewController.xib ├── WLPageViewController-Info.plist └── main.m ├── LICENSE ├── README.md ├── WLContainerControllers ├── Demo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── WLContainerControllers-Info.plist │ └── main.m ├── LICENSE ├── README.md ├── WLContainerControllers.xcodeproj │ ├── an0.mode1v3 │ ├── an0.pbxuser │ ├── an0.perspectivev3 │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── an0.xcuserdatad │ │ │ └── WorkspaceState.xcuserstate │ └── xcuserdata │ │ └── an0.xcuserdatad │ │ └── xcschemes │ │ ├── WLContainerControllers.xcscheme │ │ └── xcschememanagement.plist └── WLContainerControllers │ ├── WLCompositeController.h │ ├── WLCompositeController.m │ ├── WLContainerController.h │ ├── WLContainerController.m │ ├── WLSelectionController.h │ └── WLSelectionController.m ├── WLPageViewController.xcodeproj └── project.pbxproj ├── WLPageViewController ├── WLPageViewController.h ├── WLPageViewController.m └── damping.m └── demo.gif /Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 6/7/13. 6 | // Copyright (c) 2013 Moke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 6/7/13. 6 | // Copyright (c) 2013 Moke. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "PageViewController.h" 11 | 12 | @implementation AppDelegate 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 = [PageViewController new]; 19 | [self.window makeKeyAndVisible]; 20 | return YES; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Demo/DataViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DataViewController.h 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DataViewController : UIViewController 12 | @property (strong, nonatomic) IBOutlet UILabel *dataLabel; 13 | @property (strong, nonatomic) id dataObject; 14 | @end 15 | -------------------------------------------------------------------------------- /Demo/DataViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DataViewController.m 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DataViewController.h" 10 | 11 | @implementation DataViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | // Do any additional setup after loading the view, typically from a nib. 17 | self.title = [self.dataObject description]; 18 | } 19 | 20 | - (void)viewWillAppear:(BOOL)animated 21 | { 22 | [super viewWillAppear:animated]; 23 | self.dataLabel.text = [self.dataObject description]; 24 | NSLog(@"%@ viewWillAppear", self.dataObject); 25 | } 26 | 27 | - (void)viewDidAppear:(BOOL)animated { 28 | [super viewDidAppear:animated]; 29 | NSLog(@"%@ viewDidAppear", self.dataObject); 30 | } 31 | 32 | - (void)viewWillDisappear:(BOOL)animated { 33 | [super viewWillDisappear:animated]; 34 | NSLog(@"%@ viewWillDisappear", self.dataObject); 35 | } 36 | 37 | - (void)viewDidDisappear:(BOOL)animated { 38 | [super viewDidDisappear:animated]; 39 | NSLog(@"%@ viewDidDisappear", self.dataObject); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Demo/DataViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 10J869 6 | 1845 7 | 1038.35 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 845 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIView 17 | IBUILabel 18 | 19 | 20 | YES 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | YES 25 | 26 | YES 27 | 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | 44 | YES 45 | 46 | 47 | 274 48 | {{8, 37}, {304, 415}} 49 | 50 | 51 | 52 | 53 | 3 54 | MQA 55 | 56 | 2 57 | 58 | 59 | IBCocoaTouchFramework 60 | 61 | 62 | 63 | 290 64 | {{8, 8}, {304, 21}} 65 | 66 | 67 | 68 | NO 69 | YES 70 | 7 71 | NO 72 | IBCocoaTouchFramework 73 | Label 74 | 75 | 1 76 | MCAwIDAAA 77 | 78 | 79 | 1 80 | 10 81 | 1 82 | 83 | 1 84 | 4 85 | 86 | 87 | Helvetica-Light 88 | 14 89 | 16 90 | 91 | 92 | 93 | {{0, 20}, {320, 460}} 94 | 95 | 96 | 97 | 98 | 1 99 | MC45NzgyNjA4Njk2IDAuOTE4NDgxMzEwOCAwLjczOTE0MjYzNDQAA 100 | 101 | 102 | IBCocoaTouchFramework 103 | 104 | 105 | 106 | 107 | YES 108 | 109 | 110 | view 111 | 112 | 113 | 114 | 3 115 | 116 | 117 | 118 | dataLabel 119 | 120 | 121 | 122 | 6 123 | 124 | 125 | 126 | 127 | YES 128 | 129 | 0 130 | 131 | 132 | 133 | 134 | 135 | 1 136 | 137 | 138 | YES 139 | 140 | 141 | 142 | 143 | 144 | 145 | -1 146 | 147 | 148 | File's Owner 149 | 150 | 151 | -2 152 | 153 | 154 | 155 | 156 | 4 157 | 158 | 159 | 160 | 161 | 5 162 | 163 | 164 | 165 | 166 | 167 | 168 | YES 169 | 170 | YES 171 | -1.CustomClassName 172 | -2.CustomClassName 173 | 1.IBEditorWindowLastContentRect 174 | 1.IBPluginDependency 175 | 4.IBPluginDependency 176 | 5.IBPluginDependency 177 | 178 | 179 | YES 180 | DataViewController 181 | UIResponder 182 | {{354, 412}, {320, 480}} 183 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 184 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 185 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 186 | 187 | 188 | 189 | YES 190 | 191 | 192 | 193 | 194 | 195 | YES 196 | 197 | 198 | 199 | 200 | 6 201 | 202 | 203 | 204 | YES 205 | 206 | DataViewController 207 | UIViewController 208 | 209 | dataLabel 210 | UILabel 211 | 212 | 213 | dataLabel 214 | 215 | dataLabel 216 | UILabel 217 | 218 | 219 | 220 | IBProjectSource 221 | ./Classes/DataViewController.h 222 | 223 | 224 | 225 | 226 | 0 227 | IBCocoaTouchFramework 228 | 229 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 230 | 231 | 232 | YES 233 | 3 234 | 845 235 | 236 | 237 | -------------------------------------------------------------------------------- /Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x", 6 | "size" : "57x57" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "2x", 11 | "size" : "57x57" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "60x60" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "scale" : "1x", 21 | "size" : "72x72" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "scale" : "2x", 26 | "size" : "72x72" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "scale" : "1x", 31 | "size" : "76x76" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "scale" : "2x", 36 | "size" : "76x76" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "1x", 41 | "size" : "29x29" 42 | }, 43 | { 44 | "idiom" : "iphone", 45 | "scale" : "2x", 46 | "size" : "29x29" 47 | }, 48 | { 49 | "idiom" : "iphone", 50 | "scale" : "2x", 51 | "size" : "40x40" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "50x50" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "50x50" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "29x29" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "29x29" 82 | } 83 | ], 84 | "info" : { 85 | "version" : 1, 86 | "author" : "xcode" 87 | } 88 | } -------------------------------------------------------------------------------- /Demo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x", 6 | "orientation" : "portrait" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "2x", 11 | "orientation" : "portrait" 12 | }, 13 | { 14 | "orientation" : "portrait", 15 | "idiom" : "iphone", 16 | "subtype" : "retina4", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "orientation" : "portrait", 27 | "idiom" : "iphone", 28 | "minimum-system-version" : "7.0", 29 | "subtype" : "retina4", 30 | "scale" : "2x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "to-status-bar", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "orientation" : "portrait", 40 | "idiom" : "ipad", 41 | "extent" : "to-status-bar", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "to-status-bar", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "orientation" : "landscape", 52 | "idiom" : "ipad", 53 | "extent" : "to-status-bar", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "minimum-system-version" : "7.0", 60 | "extent" : "full-screen", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "orientation" : "portrait", 65 | "idiom" : "ipad", 66 | "minimum-system-version" : "7.0", 67 | "extent" : "full-screen", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "orientation" : "landscape", 72 | "idiom" : "ipad", 73 | "minimum-system-version" : "7.0", 74 | "extent" : "full-screen", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "orientation" : "landscape", 79 | "idiom" : "ipad", 80 | "minimum-system-version" : "7.0", 81 | "extent" : "full-screen", 82 | "scale" : "2x" 83 | } 84 | ], 85 | "info" : { 86 | "version" : 1, 87 | "author" : "xcode" 88 | } 89 | } -------------------------------------------------------------------------------- /Demo/ModelController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ModelController.h 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WLPageViewController.h" 11 | 12 | @class DataViewController; 13 | 14 | @interface ModelController : NSObject 15 | - (DataViewController *)viewControllerAtIndex:(NSUInteger)index; 16 | - (NSUInteger)indexOfViewController:(DataViewController *)viewController; 17 | @end 18 | -------------------------------------------------------------------------------- /Demo/ModelController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ModelController.m 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ModelController.h" 10 | 11 | #import "DataViewController.h" 12 | 13 | /* 14 | A controller object that manages a simple model -- a collection of month names. 15 | 16 | The controller serves as the data source for the page view controller; it therefore implements pageViewController:viewControllerBeforeViewController: and pageViewController:viewControllerAfterViewController:. 17 | It also implements a custom method, viewControllerAtIndex: which is useful in the implementation of the data source methods, and in the initial configuration of the application. 18 | 19 | There is no need to actually create view controllers for each page in advance -- indeed doing so incurs unnecessary overhead. Given the data model, these methods create, configure, and return a new view controller on demand. 20 | */ 21 | 22 | @interface ModelController() 23 | @property (readonly, strong, nonatomic) NSArray *pageData; 24 | @end 25 | 26 | @implementation ModelController 27 | 28 | - (id)init 29 | { 30 | self = [super init]; 31 | if (self) { 32 | // Create the data model. 33 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 34 | _pageData = [[dateFormatter monthSymbols] copy]; 35 | } 36 | return self; 37 | } 38 | 39 | - (DataViewController *)viewControllerAtIndex:(NSUInteger)index 40 | { 41 | // Return the data view controller for the given index. 42 | if (([self.pageData count] == 0) || (index >= [self.pageData count])) { 43 | return nil; 44 | } 45 | 46 | // Create a new view controller and pass suitable data. 47 | DataViewController *dataViewController = [[DataViewController alloc] initWithNibName:@"DataViewController" bundle:nil]; 48 | dataViewController.dataObject = (self.pageData)[index]; 49 | return dataViewController; 50 | } 51 | 52 | - (NSUInteger)indexOfViewController:(DataViewController *)viewController 53 | { 54 | /* 55 | Return the index of the given data view controller. 56 | For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index. 57 | */ 58 | return [self.pageData indexOfObject:viewController.dataObject]; 59 | } 60 | 61 | #pragma mark - Page View Controller Data Source 62 | 63 | - (UIViewController *)pageViewController:(WLPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController 64 | { 65 | NSUInteger index = [self indexOfViewController:(DataViewController *)viewController]; 66 | if (index == NSNotFound) { 67 | return nil; 68 | } 69 | // NSUInteger count = [self.pageData count]; 70 | // index = (index - 1 + count) % count; 71 | if (index == 0) return nil; 72 | return [self viewControllerAtIndex:index - 1]; 73 | } 74 | 75 | - (UIViewController *)pageViewController:(WLPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController 76 | { 77 | NSUInteger index = [self indexOfViewController:(DataViewController *)viewController]; 78 | if (index == NSNotFound) { 79 | return nil; 80 | } 81 | 82 | // index = (index + 1) % [self.pageData count]; 83 | if (index == [self.pageData count] - 1) return nil; 84 | return [self viewControllerAtIndex:index + 1]; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /Demo/PageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.h 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WLPageViewController.h" 11 | 12 | @interface PageViewController : UIViewController 13 | 14 | @property (strong, nonatomic) WLPageViewController *pageViewController; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/PageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.m 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 7/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "PageViewController.h" 10 | 11 | #import "ModelController.h" 12 | 13 | #import "DataViewController.h" 14 | 15 | @interface PageViewController () 16 | @property (readonly, strong, nonatomic) ModelController *modelController; 17 | @end 18 | 19 | @implementation PageViewController 20 | 21 | @synthesize modelController = _modelController; 22 | 23 | - (void)viewDidLoad 24 | { 25 | [super viewDidLoad]; 26 | // Configure the page view controller and add it as a child view controller. 27 | DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0]; 28 | self.pageViewController = [[WLPageViewController alloc] initWithViewController:startingViewController pageSpacing:80]; 29 | // self.pageViewController.enableTapPageTurning = YES; 30 | self.pageViewController.dataSource = self.modelController; 31 | self.pageViewController.delegate = self; 32 | 33 | [self addChildViewController:self.pageViewController]; 34 | [self.view addSubview:self.pageViewController.view]; 35 | 36 | // Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages. 37 | CGRect pageViewRect = self.view.bounds; 38 | self.pageViewController.view.frame = pageViewRect; 39 | 40 | [self.pageViewController didMoveToParentViewController:self]; 41 | 42 | // Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily. 43 | // self.view.gestureRecognizers = self.pageViewController.gestureRecognizers; 44 | } 45 | 46 | - (ModelController *)modelController 47 | { 48 | /* 49 | Return the model controller object, creating it if necessary. 50 | In more complex implementations, the model controller may be passed to the view controller. 51 | */ 52 | if (!_modelController) { 53 | _modelController = [[ModelController alloc] init]; 54 | } 55 | return _modelController; 56 | } 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Demo/PageViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C60 6 | 2843 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1929 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIView 17 | 18 | 19 | YES 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | PluginDependencyRecalculationVersion 24 | 25 | 26 | 27 | YES 28 | 29 | IBFilesOwner 30 | IBCocoaTouchFramework 31 | 32 | 33 | IBFirstResponder 34 | IBCocoaTouchFramework 35 | 36 | 37 | 38 | 274 39 | {{0, 20}, {320, 460}} 40 | 41 | 1 42 | MC42MDAwMDAwMjM4IDAuNDAwMDAwMDA2IDAuMjAwMDAwMDAzAA 43 | 44 | 45 | IBCocoaTouchFramework 46 | 47 | 48 | 49 | 50 | YES 51 | 52 | 53 | view 54 | 55 | 56 | 57 | 6 58 | 59 | 60 | 61 | 62 | YES 63 | 64 | 0 65 | 66 | YES 67 | 68 | 69 | 70 | 71 | 72 | 1 73 | 74 | 75 | 76 | 77 | -1 78 | 79 | 80 | File's Owner 81 | 82 | 83 | -2 84 | 85 | 86 | 87 | 88 | 89 | 90 | YES 91 | 92 | YES 93 | -1.CustomClassName 94 | -1.IBPluginDependency 95 | -2.CustomClassName 96 | -2.IBPluginDependency 97 | 1.IBPluginDependency 98 | 99 | 100 | YES 101 | PageViewController 102 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 103 | UIResponder 104 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 105 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 106 | 107 | 108 | 109 | YES 110 | 111 | 112 | 113 | 114 | 115 | YES 116 | 117 | 118 | 119 | 120 | 6 121 | 122 | 123 | 0 124 | IBCocoaTouchFramework 125 | 126 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 127 | 128 | 129 | YES 130 | 3 131 | 1929 132 | 133 | 134 | -------------------------------------------------------------------------------- /Demo/WLPageViewController-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIcons 12 | 13 | CFBundleIcons~ipad 14 | 15 | CFBundleIdentifier 16 | $(PRODUCT_BUNDLE_IDENTIFIER) 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | ${PRODUCT_NAME} 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | 1.0 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | 1.0 29 | LSRequiresIPhoneOS 30 | 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WLPageViewController 4 | // 5 | // Created by Ling Wang on 6/7/13. 6 | // Copyright (c) 2013 Moke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ling Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WLPageViewController 2 | ====================== 3 | 4 | WLPageViewController is a custom implementation of UIPageViewController with scrolling transition support. 5 | 6 | Demo 7 | 8 | WLPageViewController was initially created for iOS 5 wherein UIPageViewController only supported page curl transition. It is still used in iOS 6 or even iOS 7 projects for its synchronous navigation title transition support. 9 | 10 | It uses CAKeyframeAnimation to build a damping system to implement inertial scrolling and bouncing. With the introduction of UIKit Dynamics in iOS 7, one might be able to implement it with simpler code. But still, it is a good sample of how to do physics based animation from the ground up. -------------------------------------------------------------------------------- /WLContainerControllers/Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WLContainerControllers 4 | // 5 | // Created by Wang Ling on 7/16/10. 6 | // Copyright I Wonder Phone 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @property (nonatomic, retain) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /WLContainerControllers/Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WLContainerControllers 4 | // 5 | // Created by Wang Ling on 7/16/10. 6 | // Copyright I Wonder Phone 2010. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | 14 | #pragma mark - Application lifecycle 15 | 16 | - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 17 | return YES; 18 | } 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /WLContainerControllers/Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "60x60" 7 | }, 8 | { 9 | "idiom" : "ipad", 10 | "scale" : "1x", 11 | "size" : "76x76" 12 | }, 13 | { 14 | "idiom" : "ipad", 15 | "scale" : "2x", 16 | "size" : "76x76" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "2x", 21 | "size" : "40x40" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "scale" : "1x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "29x29" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "scale" : "1x", 41 | "size" : "29x29" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "2x", 46 | "size" : "29x29" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /WLContainerControllers/Demo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "ipad", 6 | "minimum-system-version" : "7.0", 7 | "extent" : "full-screen", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "landscape", 12 | "idiom" : "ipad", 13 | "minimum-system-version" : "7.0", 14 | "extent" : "full-screen", 15 | "scale" : "1x" 16 | }, 17 | { 18 | "orientation" : "landscape", 19 | "idiom" : "ipad", 20 | "minimum-system-version" : "7.0", 21 | "extent" : "full-screen", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "orientation" : "portrait", 26 | "idiom" : "iphone", 27 | "minimum-system-version" : "7.0", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "minimum-system-version" : "7.0", 34 | "subtype" : "retina4", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "orientation" : "portrait", 39 | "idiom" : "ipad", 40 | "minimum-system-version" : "7.0", 41 | "extent" : "full-screen", 42 | "scale" : "1x" 43 | } 44 | ], 45 | "info" : { 46 | "version" : 1, 47 | "author" : "xcode" 48 | } 49 | } -------------------------------------------------------------------------------- /WLContainerControllers/Demo/WLContainerControllers-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | LSRequiresIPhoneOS 24 | 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | UIInterfaceOrientationLandscapeLeft 30 | UIInterfaceOrientationLandscapeRight 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /WLContainerControllers/Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WLContainerControllers 4 | // 5 | // Created by Wang Ling on 7/16/10. 6 | // Copyright I Wonder Phone 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char *argv[]) { 13 | int retVal = 0; 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 16 | } 17 | return retVal; 18 | } 19 | -------------------------------------------------------------------------------- /WLContainerControllers/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ling Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /WLContainerControllers/README.md: -------------------------------------------------------------------------------- 1 | WLContainerControllers 2 | ====================== 3 | 4 | Base classes of container controllers in WLKit. 5 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/an0.mode1v3: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ActivePerspectiveName 6 | Project 7 | AllowedModules 8 | 9 | 10 | BundleLoadPath 11 | 12 | MaxInstances 13 | n 14 | Module 15 | PBXSmartGroupTreeModule 16 | Name 17 | Groups and Files Outline View 18 | 19 | 20 | BundleLoadPath 21 | 22 | MaxInstances 23 | n 24 | Module 25 | PBXNavigatorGroup 26 | Name 27 | Editor 28 | 29 | 30 | BundleLoadPath 31 | 32 | MaxInstances 33 | n 34 | Module 35 | XCTaskListModule 36 | Name 37 | Task List 38 | 39 | 40 | BundleLoadPath 41 | 42 | MaxInstances 43 | n 44 | Module 45 | XCDetailModule 46 | Name 47 | File and Smart Group Detail Viewer 48 | 49 | 50 | BundleLoadPath 51 | 52 | MaxInstances 53 | 1 54 | Module 55 | PBXBuildResultsModule 56 | Name 57 | Detailed Build Results Viewer 58 | 59 | 60 | BundleLoadPath 61 | 62 | MaxInstances 63 | 1 64 | Module 65 | PBXProjectFindModule 66 | Name 67 | Project Batch Find Tool 68 | 69 | 70 | BundleLoadPath 71 | 72 | MaxInstances 73 | n 74 | Module 75 | XCProjectFormatConflictsModule 76 | Name 77 | Project Format Conflicts List 78 | 79 | 80 | BundleLoadPath 81 | 82 | MaxInstances 83 | n 84 | Module 85 | PBXBookmarksModule 86 | Name 87 | Bookmarks Tool 88 | 89 | 90 | BundleLoadPath 91 | 92 | MaxInstances 93 | n 94 | Module 95 | PBXClassBrowserModule 96 | Name 97 | Class Browser 98 | 99 | 100 | BundleLoadPath 101 | 102 | MaxInstances 103 | n 104 | Module 105 | PBXCVSModule 106 | Name 107 | Source Code Control Tool 108 | 109 | 110 | BundleLoadPath 111 | 112 | MaxInstances 113 | n 114 | Module 115 | PBXDebugBreakpointsModule 116 | Name 117 | Debug Breakpoints Tool 118 | 119 | 120 | BundleLoadPath 121 | 122 | MaxInstances 123 | n 124 | Module 125 | XCDockableInspector 126 | Name 127 | Inspector 128 | 129 | 130 | BundleLoadPath 131 | 132 | MaxInstances 133 | n 134 | Module 135 | PBXOpenQuicklyModule 136 | Name 137 | Open Quickly Tool 138 | 139 | 140 | BundleLoadPath 141 | 142 | MaxInstances 143 | 1 144 | Module 145 | PBXDebugSessionModule 146 | Name 147 | Debugger 148 | 149 | 150 | BundleLoadPath 151 | 152 | MaxInstances 153 | 1 154 | Module 155 | PBXDebugCLIModule 156 | Name 157 | Debug Console 158 | 159 | 160 | BundleLoadPath 161 | 162 | MaxInstances 163 | n 164 | Module 165 | XCSnapshotModule 166 | Name 167 | Snapshots Tool 168 | 169 | 170 | BundlePath 171 | /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources 172 | Description 173 | DefaultDescriptionKey 174 | DockingSystemVisible 175 | 176 | Extension 177 | mode1v3 178 | FavBarConfig 179 | 180 | PBXProjectModuleGUID 181 | C9D0ACF711F0968000A84A80 182 | XCBarModuleItemNames 183 | 184 | XCBarModuleItems 185 | 186 | 187 | FirstTimeWindowDisplayed 188 | 189 | Identifier 190 | com.apple.perspectives.project.mode1v3 191 | MajorVersion 192 | 33 193 | MinorVersion 194 | 0 195 | Name 196 | Default 197 | Notifications 198 | 199 | OpenEditors 200 | 201 | PerspectiveWidths 202 | 203 | -1 204 | -1 205 | 206 | Perspectives 207 | 208 | 209 | ChosenToolbarItems 210 | 211 | active-combo-popup 212 | action 213 | NSToolbarFlexibleSpaceItem 214 | debugger-enable-breakpoints 215 | build-and-go 216 | com.apple.ide.PBXToolbarStopButton 217 | get-info 218 | NSToolbarFlexibleSpaceItem 219 | com.apple.pbx.toolbar.searchfield 220 | 221 | ControllerClassBaseName 222 | 223 | IconName 224 | WindowOfProjectWithEditor 225 | Identifier 226 | perspective.project 227 | IsVertical 228 | 229 | Layout 230 | 231 | 232 | ContentConfiguration 233 | 234 | PBXBottomSmartGroupGIDs 235 | 236 | 1C37FBAC04509CD000000102 237 | 1C37FAAC04509CD000000102 238 | 1C37FABC05509CD000000102 239 | 1C37FABC05539CD112110102 240 | E2644B35053B69B200211256 241 | 1C37FABC04509CD000100104 242 | 1CC0EA4004350EF90044410B 243 | 1CC0EA4004350EF90041110B 244 | 245 | PBXProjectModuleGUID 246 | 1CE0B1FE06471DED0097A5F4 247 | PBXProjectModuleLabel 248 | Files 249 | PBXProjectStructureProvided 250 | yes 251 | PBXSmartGroupTreeModuleColumnData 252 | 253 | PBXSmartGroupTreeModuleColumnWidthsKey 254 | 255 | 254 256 | 257 | PBXSmartGroupTreeModuleColumnsKey_v4 258 | 259 | MainColumn 260 | 261 | 262 | PBXSmartGroupTreeModuleOutlineStateKey_v7 263 | 264 | PBXSmartGroupTreeModuleOutlineStateExpansionKey 265 | 266 | 29B97314FDCFA39411CA2CEA 267 | 080E96DDFE201D6D7F000001 268 | C9D0ACFD11F096C400A84A80 269 | 29B97317FDCFA39411CA2CEA 270 | 1C37FBAC04509CD000000102 271 | 1C37FABC05509CD000000102 272 | 273 | PBXSmartGroupTreeModuleOutlineStateSelectionKey 274 | 275 | 276 | 5 277 | 3 278 | 1 279 | 0 280 | 281 | 282 | PBXSmartGroupTreeModuleOutlineStateVisibleRectKey 283 | {{0, 0}, {254, 973}} 284 | 285 | PBXTopSmartGroupGIDs 286 | 287 | XCIncludePerspectivesSwitch 288 | 289 | XCSharingToken 290 | com.apple.Xcode.GFSharingToken 291 | 292 | GeometryConfiguration 293 | 294 | Frame 295 | {{0, 0}, {271, 991}} 296 | GroupTreeTableConfiguration 297 | 298 | MainColumn 299 | 254 300 | 301 | RubberWindowFrame 302 | 522 146 1250 1032 0 0 1920 1178 303 | 304 | Module 305 | PBXSmartGroupTreeModule 306 | Proportion 307 | 271pt 308 | 309 | 310 | Dock 311 | 312 | 313 | BecomeActive 314 | 315 | ContentConfiguration 316 | 317 | PBXProjectModuleGUID 318 | 1CE0B20306471E060097A5F4 319 | PBXProjectModuleLabel 320 | WLContainerController.m 321 | PBXSplitModuleInNavigatorKey 322 | 323 | Split0 324 | 325 | PBXProjectModuleGUID 326 | 1CE0B20406471E060097A5F4 327 | PBXProjectModuleLabel 328 | WLContainerController.m 329 | _historyCapacity 330 | 0 331 | bookmark 332 | C9907DA712018EC100F2BC6D 333 | history 334 | 335 | C9C633CE11F1C6F200A09C37 336 | C9B0D18811FFDCF700B1FB11 337 | C9907D4C1201755200F2BC6D 338 | C9907D65120183E400F2BC6D 339 | C9907D7F1201870C00F2BC6D 340 | C9907D801201870C00F2BC6D 341 | C9907D811201870C00F2BC6D 342 | C9907D8B12018B5400F2BC6D 343 | 344 | 345 | SplitCount 346 | 1 347 | 348 | StatusBarVisibility 349 | 350 | 351 | GeometryConfiguration 352 | 353 | Frame 354 | {{0, 0}, {974, 986}} 355 | RubberWindowFrame 356 | 522 146 1250 1032 0 0 1920 1178 357 | 358 | Module 359 | PBXNavigatorGroup 360 | Proportion 361 | 986pt 362 | 363 | 364 | ContentConfiguration 365 | 366 | PBXProjectModuleGUID 367 | 1CE0B20506471E060097A5F4 368 | PBXProjectModuleLabel 369 | Detail 370 | 371 | GeometryConfiguration 372 | 373 | Frame 374 | {{0, 991}, {974, 0}} 375 | RubberWindowFrame 376 | 522 146 1250 1032 0 0 1920 1178 377 | 378 | Module 379 | XCDetailModule 380 | Proportion 381 | 0pt 382 | 383 | 384 | Proportion 385 | 974pt 386 | 387 | 388 | Name 389 | Project 390 | ServiceClasses 391 | 392 | XCModuleDock 393 | PBXSmartGroupTreeModule 394 | XCModuleDock 395 | PBXNavigatorGroup 396 | XCDetailModule 397 | 398 | TableOfContents 399 | 400 | C9907DA812018EC100F2BC6D 401 | 1CE0B1FE06471DED0097A5F4 402 | C9907DA912018EC100F2BC6D 403 | 1CE0B20306471E060097A5F4 404 | 1CE0B20506471E060097A5F4 405 | 406 | ToolbarConfigUserDefaultsMinorVersion 407 | 2 408 | ToolbarConfiguration 409 | xcode.toolbar.config.defaultV3 410 | 411 | 412 | ControllerClassBaseName 413 | 414 | IconName 415 | WindowOfProject 416 | Identifier 417 | perspective.morph 418 | IsVertical 419 | 0 420 | Layout 421 | 422 | 423 | BecomeActive 424 | 1 425 | ContentConfiguration 426 | 427 | PBXBottomSmartGroupGIDs 428 | 429 | 1C37FBAC04509CD000000102 430 | 1C37FAAC04509CD000000102 431 | 1C08E77C0454961000C914BD 432 | 1C37FABC05509CD000000102 433 | 1C37FABC05539CD112110102 434 | E2644B35053B69B200211256 435 | 1C37FABC04509CD000100104 436 | 1CC0EA4004350EF90044410B 437 | 1CC0EA4004350EF90041110B 438 | 439 | PBXProjectModuleGUID 440 | 11E0B1FE06471DED0097A5F4 441 | PBXProjectModuleLabel 442 | Files 443 | PBXProjectStructureProvided 444 | yes 445 | PBXSmartGroupTreeModuleColumnData 446 | 447 | PBXSmartGroupTreeModuleColumnWidthsKey 448 | 449 | 186 450 | 451 | PBXSmartGroupTreeModuleColumnsKey_v4 452 | 453 | MainColumn 454 | 455 | 456 | PBXSmartGroupTreeModuleOutlineStateKey_v7 457 | 458 | PBXSmartGroupTreeModuleOutlineStateExpansionKey 459 | 460 | 29B97314FDCFA39411CA2CEA 461 | 1C37FABC05509CD000000102 462 | 463 | PBXSmartGroupTreeModuleOutlineStateSelectionKey 464 | 465 | 466 | 0 467 | 468 | 469 | PBXSmartGroupTreeModuleOutlineStateVisibleRectKey 470 | {{0, 0}, {186, 337}} 471 | 472 | PBXTopSmartGroupGIDs 473 | 474 | XCIncludePerspectivesSwitch 475 | 1 476 | XCSharingToken 477 | com.apple.Xcode.GFSharingToken 478 | 479 | GeometryConfiguration 480 | 481 | Frame 482 | {{0, 0}, {203, 355}} 483 | GroupTreeTableConfiguration 484 | 485 | MainColumn 486 | 186 487 | 488 | RubberWindowFrame 489 | 373 269 690 397 0 0 1440 878 490 | 491 | Module 492 | PBXSmartGroupTreeModule 493 | Proportion 494 | 100% 495 | 496 | 497 | Name 498 | Morph 499 | PreferredWidth 500 | 300 501 | ServiceClasses 502 | 503 | XCModuleDock 504 | PBXSmartGroupTreeModule 505 | 506 | TableOfContents 507 | 508 | 11E0B1FE06471DED0097A5F4 509 | 510 | ToolbarConfiguration 511 | xcode.toolbar.config.default.shortV3 512 | 513 | 514 | PerspectivesBarVisible 515 | 516 | ShelfIsVisible 517 | 518 | SourceDescription 519 | file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec' 520 | StatusbarIsVisible 521 | 522 | TimeStamp 523 | 0.0 524 | ToolbarConfigUserDefaultsMinorVersion 525 | 2 526 | ToolbarDisplayMode 527 | 1 528 | ToolbarIsVisible 529 | 530 | ToolbarSizeMode 531 | 1 532 | Type 533 | Perspectives 534 | UpdateMessage 535 | The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? 536 | WindowJustification 537 | 5 538 | WindowOrderList 539 | 540 | C9D0ACF811F0968000A84A80 541 | /Users/an0/dev/Projects/iOS/WLKit/WLContainerControllers/WLContainerControllers.xcodeproj 542 | 543 | WindowString 544 | 522 146 1250 1032 0 0 1920 1178 545 | WindowToolsV3 546 | 547 | 548 | FirstTimeWindowDisplayed 549 | 550 | Identifier 551 | windowTool.build 552 | IsVertical 553 | 554 | Layout 555 | 556 | 557 | Dock 558 | 559 | 560 | ContentConfiguration 561 | 562 | PBXProjectModuleGUID 563 | 1CD0528F0623707200166675 564 | PBXProjectModuleLabel 565 | 566 | StatusBarVisibility 567 | 568 | 569 | GeometryConfiguration 570 | 571 | Frame 572 | {{0, 0}, {896, 252}} 573 | RubberWindowFrame 574 | 126 184 896 534 0 0 1920 1178 575 | 576 | Module 577 | PBXNavigatorGroup 578 | Proportion 579 | 252pt 580 | 581 | 582 | ContentConfiguration 583 | 584 | PBXProjectModuleGUID 585 | XCMainBuildResultsModuleGUID 586 | PBXProjectModuleLabel 587 | Build Results 588 | XCBuildResultsTrigger_Collapse 589 | 1021 590 | XCBuildResultsTrigger_Open 591 | 1011 592 | 593 | GeometryConfiguration 594 | 595 | Frame 596 | {{0, 257}, {896, 236}} 597 | RubberWindowFrame 598 | 126 184 896 534 0 0 1920 1178 599 | 600 | Module 601 | PBXBuildResultsModule 602 | Proportion 603 | 236pt 604 | 605 | 606 | Proportion 607 | 493pt 608 | 609 | 610 | Name 611 | Build Results 612 | ServiceClasses 613 | 614 | PBXBuildResultsModule 615 | 616 | StatusbarIsVisible 617 | 618 | TableOfContents 619 | 620 | C9D0ACF811F0968000A84A80 621 | C9907DAA12018EC100F2BC6D 622 | 1CD0528F0623707200166675 623 | XCMainBuildResultsModuleGUID 624 | 625 | ToolbarConfiguration 626 | xcode.toolbar.config.buildV3 627 | WindowContentMinSize 628 | 486 300 629 | WindowString 630 | 126 184 896 534 0 0 1920 1178 631 | WindowToolGUID 632 | C9D0ACF811F0968000A84A80 633 | WindowToolIsVisible 634 | 635 | 636 | 637 | FirstTimeWindowDisplayed 638 | 639 | Identifier 640 | windowTool.debugger 641 | IsVertical 642 | 643 | Layout 644 | 645 | 646 | Dock 647 | 648 | 649 | ContentConfiguration 650 | 651 | Debugger 652 | 653 | HorizontalSplitView 654 | 655 | _collapsingFrameDimension 656 | 0.0 657 | _indexOfCollapsedView 658 | 0 659 | _percentageOfCollapsedView 660 | 0.0 661 | isCollapsed 662 | yes 663 | sizes 664 | 665 | {{0, 0}, {316, 203}} 666 | {{316, 0}, {378, 203}} 667 | 668 | 669 | VerticalSplitView 670 | 671 | _collapsingFrameDimension 672 | 0.0 673 | _indexOfCollapsedView 674 | 0 675 | _percentageOfCollapsedView 676 | 0.0 677 | isCollapsed 678 | yes 679 | sizes 680 | 681 | {{0, 0}, {694, 203}} 682 | {{0, 203}, {694, 178}} 683 | 684 | 685 | 686 | LauncherConfigVersion 687 | 8 688 | PBXProjectModuleGUID 689 | 1C162984064C10D400B95A72 690 | PBXProjectModuleLabel 691 | Debug - GLUTExamples (Underwater) 692 | 693 | GeometryConfiguration 694 | 695 | DebugConsoleVisible 696 | None 697 | DebugConsoleWindowFrame 698 | {{200, 200}, {500, 300}} 699 | DebugSTDIOWindowFrame 700 | {{200, 200}, {500, 300}} 701 | Frame 702 | {{0, 0}, {694, 381}} 703 | PBXDebugSessionStackFrameViewKey 704 | 705 | DebugVariablesTableConfiguration 706 | 707 | Name 708 | 120 709 | Value 710 | 85 711 | Summary 712 | 148 713 | 714 | Frame 715 | {{316, 0}, {378, 203}} 716 | RubberWindowFrame 717 | 848 366 694 422 0 0 1920 1178 718 | 719 | RubberWindowFrame 720 | 848 366 694 422 0 0 1920 1178 721 | 722 | Module 723 | PBXDebugSessionModule 724 | Proportion 725 | 381pt 726 | 727 | 728 | Proportion 729 | 381pt 730 | 731 | 732 | Name 733 | Debugger 734 | ServiceClasses 735 | 736 | PBXDebugSessionModule 737 | 738 | StatusbarIsVisible 739 | 740 | TableOfContents 741 | 742 | 1CD10A99069EF8BA00B06720 743 | C9907D68120183E400F2BC6D 744 | 1C162984064C10D400B95A72 745 | C9907D69120183E400F2BC6D 746 | C9907D6A120183E400F2BC6D 747 | C9907D6B120183E400F2BC6D 748 | C9907D6C120183E400F2BC6D 749 | C9907D6D120183E400F2BC6D 750 | 751 | ToolbarConfiguration 752 | xcode.toolbar.config.debugV3 753 | WindowString 754 | 848 366 694 422 0 0 1920 1178 755 | WindowToolGUID 756 | 1CD10A99069EF8BA00B06720 757 | WindowToolIsVisible 758 | 759 | 760 | 761 | Identifier 762 | windowTool.find 763 | Layout 764 | 765 | 766 | Dock 767 | 768 | 769 | Dock 770 | 771 | 772 | ContentConfiguration 773 | 774 | PBXProjectModuleGUID 775 | 1CDD528C0622207200134675 776 | PBXProjectModuleLabel 777 | <No Editor> 778 | PBXSplitModuleInNavigatorKey 779 | 780 | Split0 781 | 782 | PBXProjectModuleGUID 783 | 1CD0528D0623707200166675 784 | 785 | SplitCount 786 | 1 787 | 788 | StatusBarVisibility 789 | 1 790 | 791 | GeometryConfiguration 792 | 793 | Frame 794 | {{0, 0}, {781, 167}} 795 | RubberWindowFrame 796 | 62 385 781 470 0 0 1440 878 797 | 798 | Module 799 | PBXNavigatorGroup 800 | Proportion 801 | 781pt 802 | 803 | 804 | Proportion 805 | 50% 806 | 807 | 808 | BecomeActive 809 | 1 810 | ContentConfiguration 811 | 812 | PBXProjectModuleGUID 813 | 1CD0528E0623707200166675 814 | PBXProjectModuleLabel 815 | Project Find 816 | 817 | GeometryConfiguration 818 | 819 | Frame 820 | {{8, 0}, {773, 254}} 821 | RubberWindowFrame 822 | 62 385 781 470 0 0 1440 878 823 | 824 | Module 825 | PBXProjectFindModule 826 | Proportion 827 | 50% 828 | 829 | 830 | Proportion 831 | 428pt 832 | 833 | 834 | Name 835 | Project Find 836 | ServiceClasses 837 | 838 | PBXProjectFindModule 839 | 840 | StatusbarIsVisible 841 | 1 842 | TableOfContents 843 | 844 | 1C530D57069F1CE1000CFCEE 845 | 1C530D58069F1CE1000CFCEE 846 | 1C530D59069F1CE1000CFCEE 847 | 1CDD528C0622207200134675 848 | 1C530D5A069F1CE1000CFCEE 849 | 1CE0B1FE06471DED0097A5F4 850 | 1CD0528E0623707200166675 851 | 852 | WindowString 853 | 62 385 781 470 0 0 1440 878 854 | WindowToolGUID 855 | 1C530D57069F1CE1000CFCEE 856 | WindowToolIsVisible 857 | 0 858 | 859 | 860 | Identifier 861 | MENUSEPARATOR 862 | 863 | 864 | FirstTimeWindowDisplayed 865 | 866 | Identifier 867 | windowTool.debuggerConsole 868 | IsVertical 869 | 870 | Layout 871 | 872 | 873 | Dock 874 | 875 | 876 | ContentConfiguration 877 | 878 | PBXProjectModuleGUID 879 | 1C78EAAC065D492600B07095 880 | PBXProjectModuleLabel 881 | Debugger Console 882 | 883 | GeometryConfiguration 884 | 885 | Frame 886 | {{0, 0}, {650, 209}} 887 | RubberWindowFrame 888 | 848 538 650 250 0 0 1920 1178 889 | 890 | Module 891 | PBXDebugCLIModule 892 | Proportion 893 | 209pt 894 | 895 | 896 | Proportion 897 | 209pt 898 | 899 | 900 | Name 901 | Debugger Console 902 | ServiceClasses 903 | 904 | PBXDebugCLIModule 905 | 906 | StatusbarIsVisible 907 | 908 | TableOfContents 909 | 910 | 1C78EAAD065D492600B07095 911 | C9907D6E120183E400F2BC6D 912 | 1C78EAAC065D492600B07095 913 | 914 | ToolbarConfiguration 915 | xcode.toolbar.config.consoleV3 916 | WindowString 917 | 848 538 650 250 0 0 1920 1178 918 | WindowToolGUID 919 | 1C78EAAD065D492600B07095 920 | WindowToolIsVisible 921 | 922 | 923 | 924 | Identifier 925 | windowTool.snapshots 926 | Layout 927 | 928 | 929 | Dock 930 | 931 | 932 | Module 933 | XCSnapshotModule 934 | Proportion 935 | 100% 936 | 937 | 938 | Proportion 939 | 100% 940 | 941 | 942 | Name 943 | Snapshots 944 | ServiceClasses 945 | 946 | XCSnapshotModule 947 | 948 | StatusbarIsVisible 949 | Yes 950 | ToolbarConfiguration 951 | xcode.toolbar.config.snapshots 952 | WindowString 953 | 315 824 300 550 0 0 1440 878 954 | WindowToolIsVisible 955 | Yes 956 | 957 | 958 | Identifier 959 | windowTool.scm 960 | Layout 961 | 962 | 963 | Dock 964 | 965 | 966 | ContentConfiguration 967 | 968 | PBXProjectModuleGUID 969 | 1C78EAB2065D492600B07095 970 | PBXProjectModuleLabel 971 | <No Editor> 972 | PBXSplitModuleInNavigatorKey 973 | 974 | Split0 975 | 976 | PBXProjectModuleGUID 977 | 1C78EAB3065D492600B07095 978 | 979 | SplitCount 980 | 1 981 | 982 | StatusBarVisibility 983 | 1 984 | 985 | GeometryConfiguration 986 | 987 | Frame 988 | {{0, 0}, {452, 0}} 989 | RubberWindowFrame 990 | 743 379 452 308 0 0 1280 1002 991 | 992 | Module 993 | PBXNavigatorGroup 994 | Proportion 995 | 0pt 996 | 997 | 998 | BecomeActive 999 | 1 1000 | ContentConfiguration 1001 | 1002 | PBXProjectModuleGUID 1003 | 1CD052920623707200166675 1004 | PBXProjectModuleLabel 1005 | SCM 1006 | 1007 | GeometryConfiguration 1008 | 1009 | ConsoleFrame 1010 | {{0, 259}, {452, 0}} 1011 | Frame 1012 | {{0, 7}, {452, 259}} 1013 | RubberWindowFrame 1014 | 743 379 452 308 0 0 1280 1002 1015 | TableConfiguration 1016 | 1017 | Status 1018 | 30 1019 | FileName 1020 | 199 1021 | Path 1022 | 197.0950012207031 1023 | 1024 | TableFrame 1025 | {{0, 0}, {452, 250}} 1026 | 1027 | Module 1028 | PBXCVSModule 1029 | Proportion 1030 | 262pt 1031 | 1032 | 1033 | Proportion 1034 | 266pt 1035 | 1036 | 1037 | Name 1038 | SCM 1039 | ServiceClasses 1040 | 1041 | PBXCVSModule 1042 | 1043 | StatusbarIsVisible 1044 | 1 1045 | TableOfContents 1046 | 1047 | 1C78EAB4065D492600B07095 1048 | 1C78EAB5065D492600B07095 1049 | 1C78EAB2065D492600B07095 1050 | 1CD052920623707200166675 1051 | 1052 | ToolbarConfiguration 1053 | xcode.toolbar.config.scm 1054 | WindowString 1055 | 743 379 452 308 0 0 1280 1002 1056 | 1057 | 1058 | Identifier 1059 | windowTool.breakpoints 1060 | IsVertical 1061 | 0 1062 | Layout 1063 | 1064 | 1065 | Dock 1066 | 1067 | 1068 | BecomeActive 1069 | 1 1070 | ContentConfiguration 1071 | 1072 | PBXBottomSmartGroupGIDs 1073 | 1074 | 1C77FABC04509CD000000102 1075 | 1076 | PBXProjectModuleGUID 1077 | 1CE0B1FE06471DED0097A5F4 1078 | PBXProjectModuleLabel 1079 | Files 1080 | PBXProjectStructureProvided 1081 | no 1082 | PBXSmartGroupTreeModuleColumnData 1083 | 1084 | PBXSmartGroupTreeModuleColumnWidthsKey 1085 | 1086 | 168 1087 | 1088 | PBXSmartGroupTreeModuleColumnsKey_v4 1089 | 1090 | MainColumn 1091 | 1092 | 1093 | PBXSmartGroupTreeModuleOutlineStateKey_v7 1094 | 1095 | PBXSmartGroupTreeModuleOutlineStateExpansionKey 1096 | 1097 | 1C77FABC04509CD000000102 1098 | 1099 | PBXSmartGroupTreeModuleOutlineStateSelectionKey 1100 | 1101 | 1102 | 0 1103 | 1104 | 1105 | PBXSmartGroupTreeModuleOutlineStateVisibleRectKey 1106 | {{0, 0}, {168, 350}} 1107 | 1108 | PBXTopSmartGroupGIDs 1109 | 1110 | XCIncludePerspectivesSwitch 1111 | 0 1112 | 1113 | GeometryConfiguration 1114 | 1115 | Frame 1116 | {{0, 0}, {185, 368}} 1117 | GroupTreeTableConfiguration 1118 | 1119 | MainColumn 1120 | 168 1121 | 1122 | RubberWindowFrame 1123 | 315 424 744 409 0 0 1440 878 1124 | 1125 | Module 1126 | PBXSmartGroupTreeModule 1127 | Proportion 1128 | 185pt 1129 | 1130 | 1131 | ContentConfiguration 1132 | 1133 | PBXProjectModuleGUID 1134 | 1CA1AED706398EBD00589147 1135 | PBXProjectModuleLabel 1136 | Detail 1137 | 1138 | GeometryConfiguration 1139 | 1140 | Frame 1141 | {{190, 0}, {554, 368}} 1142 | RubberWindowFrame 1143 | 315 424 744 409 0 0 1440 878 1144 | 1145 | Module 1146 | XCDetailModule 1147 | Proportion 1148 | 554pt 1149 | 1150 | 1151 | Proportion 1152 | 368pt 1153 | 1154 | 1155 | MajorVersion 1156 | 3 1157 | MinorVersion 1158 | 0 1159 | Name 1160 | Breakpoints 1161 | ServiceClasses 1162 | 1163 | PBXSmartGroupTreeModule 1164 | XCDetailModule 1165 | 1166 | StatusbarIsVisible 1167 | 1 1168 | TableOfContents 1169 | 1170 | 1CDDB66807F98D9800BB5817 1171 | 1CDDB66907F98D9800BB5817 1172 | 1CE0B1FE06471DED0097A5F4 1173 | 1CA1AED706398EBD00589147 1174 | 1175 | ToolbarConfiguration 1176 | xcode.toolbar.config.breakpointsV3 1177 | WindowString 1178 | 315 424 744 409 0 0 1440 878 1179 | WindowToolGUID 1180 | 1CDDB66807F98D9800BB5817 1181 | WindowToolIsVisible 1182 | 1 1183 | 1184 | 1185 | Identifier 1186 | windowTool.debugAnimator 1187 | Layout 1188 | 1189 | 1190 | Dock 1191 | 1192 | 1193 | Module 1194 | PBXNavigatorGroup 1195 | Proportion 1196 | 100% 1197 | 1198 | 1199 | Proportion 1200 | 100% 1201 | 1202 | 1203 | Name 1204 | Debug Visualizer 1205 | ServiceClasses 1206 | 1207 | PBXNavigatorGroup 1208 | 1209 | StatusbarIsVisible 1210 | 1 1211 | ToolbarConfiguration 1212 | xcode.toolbar.config.debugAnimatorV3 1213 | WindowString 1214 | 100 100 700 500 0 0 1280 1002 1215 | 1216 | 1217 | Identifier 1218 | windowTool.bookmarks 1219 | Layout 1220 | 1221 | 1222 | Dock 1223 | 1224 | 1225 | Module 1226 | PBXBookmarksModule 1227 | Proportion 1228 | 100% 1229 | 1230 | 1231 | Proportion 1232 | 100% 1233 | 1234 | 1235 | Name 1236 | Bookmarks 1237 | ServiceClasses 1238 | 1239 | PBXBookmarksModule 1240 | 1241 | StatusbarIsVisible 1242 | 0 1243 | WindowString 1244 | 538 42 401 187 0 0 1280 1002 1245 | 1246 | 1247 | Identifier 1248 | windowTool.projectFormatConflicts 1249 | Layout 1250 | 1251 | 1252 | Dock 1253 | 1254 | 1255 | Module 1256 | XCProjectFormatConflictsModule 1257 | Proportion 1258 | 100% 1259 | 1260 | 1261 | Proportion 1262 | 100% 1263 | 1264 | 1265 | Name 1266 | Project Format Conflicts 1267 | ServiceClasses 1268 | 1269 | XCProjectFormatConflictsModule 1270 | 1271 | StatusbarIsVisible 1272 | 0 1273 | WindowContentMinSize 1274 | 450 300 1275 | WindowString 1276 | 50 850 472 307 0 0 1440 877 1277 | 1278 | 1279 | Identifier 1280 | windowTool.classBrowser 1281 | Layout 1282 | 1283 | 1284 | Dock 1285 | 1286 | 1287 | BecomeActive 1288 | 1 1289 | ContentConfiguration 1290 | 1291 | OptionsSetName 1292 | Hierarchy, all classes 1293 | PBXProjectModuleGUID 1294 | 1CA6456E063B45B4001379D8 1295 | PBXProjectModuleLabel 1296 | Class Browser - NSObject 1297 | 1298 | GeometryConfiguration 1299 | 1300 | ClassesFrame 1301 | {{0, 0}, {374, 96}} 1302 | ClassesTreeTableConfiguration 1303 | 1304 | PBXClassNameColumnIdentifier 1305 | 208 1306 | PBXClassBookColumnIdentifier 1307 | 22 1308 | 1309 | Frame 1310 | {{0, 0}, {630, 331}} 1311 | MembersFrame 1312 | {{0, 105}, {374, 395}} 1313 | MembersTreeTableConfiguration 1314 | 1315 | PBXMemberTypeIconColumnIdentifier 1316 | 22 1317 | PBXMemberNameColumnIdentifier 1318 | 216 1319 | PBXMemberTypeColumnIdentifier 1320 | 97 1321 | PBXMemberBookColumnIdentifier 1322 | 22 1323 | 1324 | PBXModuleWindowStatusBarHidden2 1325 | 1 1326 | RubberWindowFrame 1327 | 385 179 630 352 0 0 1440 878 1328 | 1329 | Module 1330 | PBXClassBrowserModule 1331 | Proportion 1332 | 332pt 1333 | 1334 | 1335 | Proportion 1336 | 332pt 1337 | 1338 | 1339 | Name 1340 | Class Browser 1341 | ServiceClasses 1342 | 1343 | PBXClassBrowserModule 1344 | 1345 | StatusbarIsVisible 1346 | 0 1347 | TableOfContents 1348 | 1349 | 1C0AD2AF069F1E9B00FABCE6 1350 | 1C0AD2B0069F1E9B00FABCE6 1351 | 1CA6456E063B45B4001379D8 1352 | 1353 | ToolbarConfiguration 1354 | xcode.toolbar.config.classbrowser 1355 | WindowString 1356 | 385 179 630 352 0 0 1440 878 1357 | WindowToolGUID 1358 | 1C0AD2AF069F1E9B00FABCE6 1359 | WindowToolIsVisible 1360 | 0 1361 | 1362 | 1363 | Identifier 1364 | windowTool.refactoring 1365 | IncludeInToolsMenu 1366 | 0 1367 | Layout 1368 | 1369 | 1370 | Dock 1371 | 1372 | 1373 | BecomeActive 1374 | 1 1375 | GeometryConfiguration 1376 | 1377 | Frame 1378 | {0, 0}, {500, 335} 1379 | RubberWindowFrame 1380 | {0, 0}, {500, 335} 1381 | 1382 | Module 1383 | XCRefactoringModule 1384 | Proportion 1385 | 100% 1386 | 1387 | 1388 | Proportion 1389 | 100% 1390 | 1391 | 1392 | Name 1393 | Refactoring 1394 | ServiceClasses 1395 | 1396 | XCRefactoringModule 1397 | 1398 | WindowString 1399 | 200 200 500 356 0 0 1920 1200 1400 | 1401 | 1402 | 1403 | 1404 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/an0.pbxuser: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | 1D3623240D0F684500981E51 /* WLContainerControllersAppDelegate.h */ = { 4 | uiCtxt = { 5 | sepNavIntBoundsRect = "{{0, 0}, {979, 1031}}"; 6 | sepNavSelRange = "{227, 0}"; 7 | sepNavVisRange = "{0, 571}"; 8 | }; 9 | }; 10 | 1D3623250D0F684500981E51 /* WLContainerControllersAppDelegate.m */ = { 11 | uiCtxt = { 12 | sepNavIntBoundsRect = "{{0, 0}, {979, 3045}}"; 13 | sepNavSelRange = "{2060, 0}"; 14 | sepNavVisRange = "{1462, 1356}"; 15 | }; 16 | }; 17 | 1D6058900D05DD3D006BFB54 /* WLContainerControllers */ = { 18 | activeExec = 0; 19 | executables = ( 20 | C9D0AC9411F0941800A84A80 /* WLContainerControllers */, 21 | ); 22 | }; 23 | 28A0AAE50D9B0CCF005BE974 /* WLContainerControllers_Prefix.pch */ = { 24 | uiCtxt = { 25 | sepNavIntBoundsRect = "{{0, 0}, {979, 728}}"; 26 | sepNavSelRange = "{351, 414}"; 27 | sepNavVisRange = "{0, 766}"; 28 | }; 29 | }; 30 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 31 | activeBuildConfigurationName = Debug; 32 | activeExecutable = C9D0AC9411F0941800A84A80 /* WLContainerControllers */; 33 | activeSDKPreference = iphonesimulator4.2; 34 | activeTarget = 1D6058900D05DD3D006BFB54 /* WLContainerControllers */; 35 | addToTargets = ( 36 | 1D6058900D05DD3D006BFB54 /* WLContainerControllers */, 37 | ); 38 | breakpoints = ( 39 | ); 40 | codeSenseManager = C9D0ACA211F0943100A84A80 /* Code sense */; 41 | executables = ( 42 | C9D0AC9411F0941800A84A80 /* WLContainerControllers */, 43 | ); 44 | perUserDictionary = { 45 | PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { 46 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 47 | PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; 48 | PBXFileTableDataSourceColumnWidthsKey = ( 49 | 22, 50 | 300, 51 | 688.58349609375, 52 | ); 53 | PBXFileTableDataSourceColumnsKey = ( 54 | PBXExecutablesDataSource_ActiveFlagID, 55 | PBXExecutablesDataSource_NameID, 56 | PBXExecutablesDataSource_CommentsID, 57 | ); 58 | }; 59 | PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { 60 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 61 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 62 | PBXFileTableDataSourceColumnWidthsKey = ( 63 | 20, 64 | 10, 65 | 20, 66 | 48, 67 | 43, 68 | 43, 69 | 20, 70 | ); 71 | PBXFileTableDataSourceColumnsKey = ( 72 | PBXFileDataSource_FiletypeID, 73 | PBXFileDataSource_Filename_ColumnID, 74 | PBXFileDataSource_Built_ColumnID, 75 | PBXFileDataSource_ObjectSize_ColumnID, 76 | PBXFileDataSource_Errors_ColumnID, 77 | PBXFileDataSource_Warnings_ColumnID, 78 | PBXFileDataSource_Target_ColumnID, 79 | ); 80 | }; 81 | PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = { 82 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 83 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 84 | PBXFileTableDataSourceColumnWidthsKey = ( 85 | 20, 86 | 20, 87 | 711, 88 | 20, 89 | 48, 90 | 43, 91 | 43, 92 | 20, 93 | ); 94 | PBXFileTableDataSourceColumnsKey = ( 95 | PBXFileDataSource_SCM_ColumnID, 96 | PBXFileDataSource_FiletypeID, 97 | PBXFileDataSource_Filename_ColumnID, 98 | PBXFileDataSource_Built_ColumnID, 99 | PBXFileDataSource_ObjectSize_ColumnID, 100 | PBXFileDataSource_Errors_ColumnID, 101 | PBXFileDataSource_Warnings_ColumnID, 102 | PBXFileDataSource_Target_ColumnID, 103 | ); 104 | }; 105 | PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { 106 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 107 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 108 | PBXFileTableDataSourceColumnWidthsKey = ( 109 | 20, 110 | 664, 111 | 60, 112 | 20, 113 | 48, 114 | 43, 115 | 43, 116 | ); 117 | PBXFileTableDataSourceColumnsKey = ( 118 | PBXFileDataSource_FiletypeID, 119 | PBXFileDataSource_Filename_ColumnID, 120 | PBXTargetDataSource_PrimaryAttribute, 121 | PBXFileDataSource_Built_ColumnID, 122 | PBXFileDataSource_ObjectSize_ColumnID, 123 | PBXFileDataSource_Errors_ColumnID, 124 | PBXFileDataSource_Warnings_ColumnID, 125 | ); 126 | }; 127 | PBXPerProjectTemplateStateSaveDate = 319510393; 128 | PBXWorkspaceStateSaveDate = 319510393; 129 | }; 130 | perUserProjectItems = { 131 | C90A663C12CF9E8A00E075A6 /* PBXTextBookmark */ = C90A663C12CF9E8A00E075A6 /* PBXTextBookmark */; 132 | C90BC39C12D294FF002D24F5 /* PBXTextBookmark */ = C90BC39C12D294FF002D24F5 /* PBXTextBookmark */; 133 | C90BC49012D29D64002D24F5 /* PBXTextBookmark */ = C90BC49012D29D64002D24F5 /* PBXTextBookmark */; 134 | C90BC56F12D2A379002D24F5 /* PBXTextBookmark */ = C90BC56F12D2A379002D24F5 /* PBXTextBookmark */; 135 | C90BC59112D2A4FE002D24F5 /* PBXTextBookmark */ = C90BC59112D2A4FE002D24F5 /* PBXTextBookmark */; 136 | C90BC7CD12D2EF8B002D24F5 /* PBXTextBookmark */ = C90BC7CD12D2EF8B002D24F5 /* PBXTextBookmark */; 137 | C90BC83412D2F293002D24F5 /* PBXTextBookmark */ = C90BC83412D2F293002D24F5 /* PBXTextBookmark */; 138 | C90BC83512D2F293002D24F5 /* PBXTextBookmark */ = C90BC83512D2F293002D24F5 /* PBXTextBookmark */; 139 | C90BC83712D2F293002D24F5 /* PBXTextBookmark */ = C90BC83712D2F293002D24F5 /* PBXTextBookmark */; 140 | C90BC84412D2F2D2002D24F5 /* PBXTextBookmark */ = C90BC84412D2F2D2002D24F5 /* PBXTextBookmark */; 141 | C93CCE8D130B579800B20020 /* PBXTextBookmark */ = C93CCE8D130B579800B20020 /* PBXTextBookmark */; 142 | C93CCE8E130B579800B20020 /* PBXTextBookmark */ = C93CCE8E130B579800B20020 /* PBXTextBookmark */; 143 | C93CCE93130B579A00B20020 /* PBXTextBookmark */ = C93CCE93130B579A00B20020 /* PBXTextBookmark */; 144 | C9E3ABF1130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF1130B3F26004A4940 /* PBXTextBookmark */; 145 | C9E3ABF2130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF2130B3F26004A4940 /* PBXTextBookmark */; 146 | C9E3ABF3130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF3130B3F26004A4940 /* PBXTextBookmark */; 147 | C9E3ABF4130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF4130B3F26004A4940 /* PBXTextBookmark */; 148 | C9E3ABF5130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF5130B3F26004A4940 /* PBXTextBookmark */; 149 | C9E3ABF7130B3F26004A4940 /* PBXTextBookmark */ = C9E3ABF7130B3F26004A4940 /* PBXTextBookmark */; 150 | C9F9E79F12CF086F009D4DDC /* PBXTextBookmark */ = C9F9E79F12CF086F009D4DDC /* PBXTextBookmark */; 151 | C9F9E7DE12CF0AB9009D4DDC /* PlistBookmark */ = C9F9E7DE12CF0AB9009D4DDC /* PlistBookmark */; 152 | }; 153 | sourceControlManager = C9D0ACA111F0943100A84A80 /* Source Control */; 154 | userBuildSettings = { 155 | }; 156 | }; 157 | C90A663512CF9A9B00E075A6 /* UIView.h */ = { 158 | isa = PBXFileReference; 159 | lastKnownFileType = sourcecode.c.h; 160 | name = UIView.h; 161 | path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIView.h; 162 | sourceTree = ""; 163 | }; 164 | C90A663C12CF9E8A00E075A6 /* PBXTextBookmark */ = { 165 | isa = PBXTextBookmark; 166 | fRef = C90A663512CF9A9B00E075A6 /* UIView.h */; 167 | name = "UIView.h: 1"; 168 | rLen = 0; 169 | rLoc = 0; 170 | rType = 0; 171 | vrLen = 1731; 172 | vrLoc = 8064; 173 | }; 174 | C90BC36A12D291EE002D24F5 /* DetailViewController.h */ = { 175 | uiCtxt = { 176 | sepNavIntBoundsRect = "{{0, 0}, {979, 1056}}"; 177 | sepNavSelRange = "{0, 0}"; 178 | sepNavVisRange = "{0, 249}"; 179 | }; 180 | }; 181 | C90BC36B12D291EE002D24F5 /* DetailViewController.m */ = { 182 | uiCtxt = { 183 | sepNavFolds = "{\n c = (\n {\n l = DetailViewController;\n r = \"{4059, 24}\";\n s = 1;\n },\n {\n l = DetailViewController;\n r = \"{4110, 24}\";\n s = 1;\n },\n {\n l = \"Nib name\";\n r = \"{4160, 12}\";\n s = 1;\n }\n );\n r = \"{0, 4961}\";\n s = 0;\n}"; 184 | sepNavIntBoundsRect = "{{0, 0}, {979, 4202}}"; 185 | sepNavSelRange = "{4796, 100}"; 186 | sepNavVisRange = "{3820, 1083}"; 187 | }; 188 | }; 189 | C90BC39C12D294FF002D24F5 /* PBXTextBookmark */ = { 190 | isa = PBXTextBookmark; 191 | fRef = C9D0AC9C11F0943000A84A80 /* ContentController.h */; 192 | name = "ContentController.h: 1"; 193 | rLen = 0; 194 | rLoc = 0; 195 | rType = 0; 196 | vrLen = 238; 197 | vrLoc = 0; 198 | }; 199 | C90BC48712D29CF2002D24F5 /* MasterViewController.h */ = { 200 | uiCtxt = { 201 | sepNavIntBoundsRect = "{{0, 0}, {979, 1056}}"; 202 | sepNavSelRange = "{0, 0}"; 203 | sepNavVisRange = "{0, 249}"; 204 | }; 205 | }; 206 | C90BC48812D29CF2002D24F5 /* MasterViewController.m */ = { 207 | uiCtxt = { 208 | sepNavFolds = "{\n c = (\n {\n l = DetailViewController;\n r = \"{3984, 24}\";\n s = 1;\n },\n {\n l = DetailViewController;\n r = \"{4035, 24}\";\n s = 1;\n },\n {\n l = \"Nib name\";\n r = \"{4085, 12}\";\n s = 1;\n }\n );\n r = \"{0, 4886}\";\n s = 0;\n}"; 209 | sepNavIntBoundsRect = "{{0, 0}, {979, 4013}}"; 210 | sepNavSelRange = "{3978, 0}"; 211 | sepNavVisRange = "{3729, 1099}"; 212 | }; 213 | }; 214 | C90BC49012D29D64002D24F5 /* PBXTextBookmark */ = { 215 | isa = PBXTextBookmark; 216 | fRef = C9D0AC9D11F0943000A84A80 /* ContentController.m */; 217 | name = "ContentController.m: 87"; 218 | rLen = 100; 219 | rLoc = 2210; 220 | rType = 0; 221 | vrLen = 1135; 222 | vrLoc = 1182; 223 | }; 224 | C90BC56F12D2A379002D24F5 /* PBXTextBookmark */ = { 225 | isa = PBXTextBookmark; 226 | fRef = 1D3623240D0F684500981E51 /* WLContainerControllersAppDelegate.h */; 227 | name = "WLContainerControllersAppDelegate.h: 10"; 228 | rLen = 0; 229 | rLoc = 227; 230 | rType = 0; 231 | vrLen = 571; 232 | vrLoc = 0; 233 | }; 234 | C90BC59112D2A4FE002D24F5 /* PBXTextBookmark */ = { 235 | isa = PBXTextBookmark; 236 | fRef = 28A0AAE50D9B0CCF005BE974 /* WLContainerControllers_Prefix.pch */; 237 | name = "WLContainerControllers_Prefix.pch: 16"; 238 | rLen = 414; 239 | rLoc = 351; 240 | rType = 0; 241 | vrLen = 766; 242 | vrLoc = 0; 243 | }; 244 | C90BC7CD12D2EF8B002D24F5 /* PBXTextBookmark */ = { 245 | isa = PBXTextBookmark; 246 | fRef = 1D3623250D0F684500981E51 /* WLContainerControllersAppDelegate.m */; 247 | name = "WLContainerControllersAppDelegate.m: 50"; 248 | rLen = 0; 249 | rLoc = 2060; 250 | rType = 0; 251 | vrLen = 1356; 252 | vrLoc = 1462; 253 | }; 254 | C90BC83412D2F293002D24F5 /* PBXTextBookmark */ = { 255 | isa = PBXTextBookmark; 256 | fRef = C941FBF612A6EF5100FB1ADD /* WLSplitViewController.m */; 257 | name = "WLSplitViewController.m: 446"; 258 | rLen = 0; 259 | rLoc = 16536; 260 | rType = 0; 261 | vrLen = 1683; 262 | vrLoc = 15016; 263 | }; 264 | C90BC83512D2F293002D24F5 /* PBXTextBookmark */ = { 265 | isa = PBXTextBookmark; 266 | fRef = C90BC48712D29CF2002D24F5 /* MasterViewController.h */; 267 | name = "MasterViewController.h: 1"; 268 | rLen = 0; 269 | rLoc = 0; 270 | rType = 0; 271 | vrLen = 249; 272 | vrLoc = 0; 273 | }; 274 | C90BC83712D2F293002D24F5 /* PBXTextBookmark */ = { 275 | isa = PBXTextBookmark; 276 | fRef = C90BC36A12D291EE002D24F5 /* DetailViewController.h */; 277 | name = "DetailViewController.h: 1"; 278 | rLen = 0; 279 | rLoc = 0; 280 | rType = 0; 281 | vrLen = 249; 282 | vrLoc = 0; 283 | }; 284 | C90BC84412D2F2D2002D24F5 /* PBXTextBookmark */ = { 285 | isa = PBXTextBookmark; 286 | fRef = C90BC36B12D291EE002D24F5 /* DetailViewController.m */; 287 | name = "DetailViewController.m: 181"; 288 | rLen = 100; 289 | rLoc = 4853; 290 | rType = 0; 291 | vrLen = 1140; 292 | vrLoc = 3820; 293 | }; 294 | C93CCE8D130B579800B20020 /* PBXTextBookmark */ = { 295 | isa = PBXTextBookmark; 296 | fRef = C9E3ABEF130B3743004A4940 /* UIViewController+WLViewController.m */; 297 | name = "UIViewController+WLViewController.m: 44"; 298 | rLen = 0; 299 | rLoc = 1183; 300 | rType = 0; 301 | vrLen = 1192; 302 | vrLoc = 0; 303 | }; 304 | C93CCE8E130B579800B20020 /* PBXTextBookmark */ = { 305 | isa = PBXTextBookmark; 306 | fRef = C9E3ABEE130B3743004A4940 /* UIViewController+WLViewController.h */; 307 | name = "UIViewController+WLViewController.h: 19"; 308 | rLen = 0; 309 | rLoc = 443; 310 | rType = 0; 311 | vrLen = 450; 312 | vrLoc = 0; 313 | }; 314 | C93CCE93130B579A00B20020 /* PBXTextBookmark */ = { 315 | isa = PBXTextBookmark; 316 | fRef = C9E3ABEE130B3743004A4940 /* UIViewController+WLViewController.h */; 317 | name = "UIViewController+WLViewController.h: 19"; 318 | rLen = 0; 319 | rLoc = 443; 320 | rType = 0; 321 | vrLen = 450; 322 | vrLoc = 0; 323 | }; 324 | C941FBF512A6EF5100FB1ADD /* WLSplitViewController.h */ = { 325 | uiCtxt = { 326 | sepNavIntBoundsRect = "{{0, 0}, {979, 1659}}"; 327 | sepNavSelRange = "{693, 0}"; 328 | sepNavVisRange = "{0, 1828}"; 329 | }; 330 | }; 331 | C941FBF612A6EF5100FB1ADD /* WLSplitViewController.m */ = { 332 | uiCtxt = { 333 | sepNavIntBoundsRect = "{{0, 0}, {979, 10794}}"; 334 | sepNavSelRange = "{16172, 0}"; 335 | sepNavVisRange = "{15016, 1683}"; 336 | }; 337 | }; 338 | C9D0AC9411F0941800A84A80 /* WLContainerControllers */ = { 339 | isa = PBXExecutable; 340 | activeArgIndices = ( 341 | ); 342 | argumentStrings = ( 343 | ); 344 | autoAttachOnCrash = 1; 345 | breakpointsEnabled = 0; 346 | configStateDict = { 347 | }; 348 | customDataFormattersEnabled = 1; 349 | dataTipCustomDataFormattersEnabled = 1; 350 | dataTipShowTypeColumn = 1; 351 | dataTipSortType = 0; 352 | debuggerPlugin = GDBDebugging; 353 | disassemblyDisplayState = 0; 354 | dylibVariantSuffix = ""; 355 | enableDebugStr = 1; 356 | environmentEntries = ( 357 | { 358 | active = YES; 359 | name = NSZombieEnabled; 360 | value = YES; 361 | }, 362 | ); 363 | executableSystemSymbolLevel = 0; 364 | executableUserSymbolLevel = 0; 365 | libgmallocEnabled = 0; 366 | name = WLContainerControllers; 367 | savedGlobals = { 368 | }; 369 | showTypeColumn = 0; 370 | sourceDirectories = ( 371 | ); 372 | variableFormatDictionary = { 373 | }; 374 | }; 375 | C9D0AC9811F0943000A84A80 /* WLSwitchController.h */ = { 376 | uiCtxt = { 377 | sepNavIntBoundsRect = "{{0, 0}, {979, 1575}}"; 378 | sepNavSelRange = "{0, 0}"; 379 | sepNavVisRange = "{781, 2365}"; 380 | }; 381 | }; 382 | C9D0AC9911F0943000A84A80 /* WLSwitchController.m */ = { 383 | uiCtxt = { 384 | sepNavIntBoundsRect = "{{0, 0}, {979, 6069}}"; 385 | sepNavSelRange = "{4090, 0}"; 386 | sepNavVisRange = "{3355, 1749}"; 387 | }; 388 | }; 389 | C9D0AC9A11F0943000A84A80 /* WLContainerController.h */ = { 390 | uiCtxt = { 391 | sepNavIntBoundsRect = "{{0, 0}, {979, 1491}}"; 392 | sepNavSelRange = "{364, 0}"; 393 | sepNavVisRange = "{0, 2305}"; 394 | }; 395 | }; 396 | C9D0AC9B11F0943000A84A80 /* WLContainerController.m */ = { 397 | uiCtxt = { 398 | sepNavIntBoundsRect = "{{0, 0}, {979, 7014}}"; 399 | sepNavSelRange = "{785, 106}"; 400 | sepNavVisRange = "{0, 1243}"; 401 | }; 402 | }; 403 | C9D0AC9C11F0943000A84A80 /* ContentController.h */ = { 404 | uiCtxt = { 405 | sepNavIntBoundsRect = "{{0, 0}, {979, 1033}}"; 406 | sepNavSelRange = "{0, 0}"; 407 | sepNavVisRange = "{0, 238}"; 408 | sepNavWindowFrame = "{{15, 110}, {1192, 1063}}"; 409 | }; 410 | }; 411 | C9D0AC9D11F0943000A84A80 /* ContentController.m */ = { 412 | uiCtxt = { 413 | sepNavIntBoundsRect = "{{0, 0}, {979, 2058}}"; 414 | sepNavSelRange = "{2210, 100}"; 415 | sepNavVisRange = "{1182, 1135}"; 416 | }; 417 | }; 418 | C9D0ACA111F0943100A84A80 /* Source Control */ = { 419 | isa = PBXSourceControlManager; 420 | fallbackIsa = XCSourceControlManager; 421 | isSCMEnabled = 0; 422 | scmConfiguration = { 423 | repositoryNamesForRoots = { 424 | "" = ""; 425 | }; 426 | }; 427 | }; 428 | C9D0ACA211F0943100A84A80 /* Code sense */ = { 429 | isa = PBXCodeSenseManager; 430 | indexTemplatePath = ""; 431 | }; 432 | C9E3ABE6130B36FD004A4940 /* NSObject+Swizzle.h */ = { 433 | uiCtxt = { 434 | sepNavIntBoundsRect = "{{0, 0}, {979, 1056}}"; 435 | sepNavSelRange = "{0, 0}"; 436 | sepNavVisRange = "{0, 344}"; 437 | }; 438 | }; 439 | C9E3ABEE130B3743004A4940 /* UIViewController+WLViewController.h */ = { 440 | uiCtxt = { 441 | sepNavIntBoundsRect = "{{0, 0}, {979, 1056}}"; 442 | sepNavSelRange = "{443, 0}"; 443 | sepNavVisRange = "{0, 450}"; 444 | }; 445 | }; 446 | C9E3ABEF130B3743004A4940 /* UIViewController+WLViewController.m */ = { 447 | uiCtxt = { 448 | sepNavIntBoundsRect = "{{0, 0}, {979, 1071}}"; 449 | sepNavSelRange = "{1183, 0}"; 450 | sepNavVisRange = "{0, 1192}"; 451 | }; 452 | }; 453 | C9E3ABF1130B3F26004A4940 /* PBXTextBookmark */ = { 454 | isa = PBXTextBookmark; 455 | fRef = C90BC48812D29CF2002D24F5 /* MasterViewController.m */; 456 | name = "MasterViewController.m: 149"; 457 | rLen = 0; 458 | rLoc = 3978; 459 | rType = 0; 460 | vrLen = 1156; 461 | vrLoc = 3729; 462 | }; 463 | C9E3ABF2130B3F26004A4940 /* PBXTextBookmark */ = { 464 | isa = PBXTextBookmark; 465 | fRef = C9D0AC9811F0943000A84A80 /* WLSwitchController.h */; 466 | name = "WLSwitchController.h: 1"; 467 | rLen = 0; 468 | rLoc = 0; 469 | rType = 0; 470 | vrLen = 2365; 471 | vrLoc = 781; 472 | }; 473 | C9E3ABF3130B3F26004A4940 /* PBXTextBookmark */ = { 474 | isa = PBXTextBookmark; 475 | fRef = C941FBF512A6EF5100FB1ADD /* WLSplitViewController.h */; 476 | name = "WLSplitViewController.h: 26"; 477 | rLen = 0; 478 | rLoc = 693; 479 | rType = 0; 480 | vrLen = 1828; 481 | vrLoc = 0; 482 | }; 483 | C9E3ABF4130B3F26004A4940 /* PBXTextBookmark */ = { 484 | isa = PBXTextBookmark; 485 | fRef = C9D0AC9A11F0943000A84A80 /* WLContainerController.h */; 486 | name = "WLContainerController.h: 15"; 487 | rLen = 0; 488 | rLoc = 364; 489 | rType = 0; 490 | vrLen = 2305; 491 | vrLoc = 0; 492 | }; 493 | C9E3ABF5130B3F26004A4940 /* PBXTextBookmark */ = { 494 | isa = PBXTextBookmark; 495 | fRef = C9D0AC9B11F0943000A84A80 /* WLContainerController.m */; 496 | name = "WLContainerController.m: 33"; 497 | rLen = 106; 498 | rLoc = 785; 499 | rType = 0; 500 | vrLen = 1243; 501 | vrLoc = 0; 502 | }; 503 | C9E3ABF7130B3F26004A4940 /* PBXTextBookmark */ = { 504 | isa = PBXTextBookmark; 505 | fRef = C9E3ABE6130B36FD004A4940 /* NSObject+Swizzle.h */; 506 | name = "NSObject+Swizzle.h: 1"; 507 | rLen = 0; 508 | rLoc = 0; 509 | rType = 0; 510 | vrLen = 344; 511 | vrLoc = 0; 512 | }; 513 | C9F9E5E612CEDFE6009D4DDC /* UISplitViewController.h */ = { 514 | isa = PBXFileReference; 515 | lastKnownFileType = sourcecode.c.h; 516 | name = UISplitViewController.h; 517 | path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/System/Library/Frameworks/UIKit.framework/Headers/UISplitViewController.h; 518 | sourceTree = ""; 519 | }; 520 | C9F9E79F12CF086F009D4DDC /* PBXTextBookmark */ = { 521 | isa = PBXTextBookmark; 522 | fRef = C9F9E5E612CEDFE6009D4DDC /* UISplitViewController.h */; 523 | name = "UISplitViewController.h: 38"; 524 | rLen = 10; 525 | rLoc = 1094; 526 | rType = 0; 527 | vrLen = 1640; 528 | vrLoc = 44; 529 | }; 530 | C9F9E7DE12CF0AB9009D4DDC /* PlistBookmark */ = { 531 | isa = PlistBookmark; 532 | fRef = 8D1107310486CEB800E47090 /* WLContainerControllers-Info.plist */; 533 | fallbackIsa = PBXBookmark; 534 | isK = 0; 535 | kPath = ( 536 | UISupportedInterfaceOrientations, 537 | ); 538 | name = "/Users/an0/dev/Projects/iOS/WLKit/WLContainerControllers/WLContainerControllers-Info.plist"; 539 | rLen = 0; 540 | rLoc = 9223372036854775808; 541 | }; 542 | } 543 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 11 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 12 | C93614EC1A7D7B7300AFECB4 /* WLCompositeController.m in Sources */ = {isa = PBXBuildFile; fileRef = C93614EB1A7D7B7300AFECB4 /* WLCompositeController.m */; }; 13 | C93614F01A7D7BBC00AFECB4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C93614EE1A7D7BBC00AFECB4 /* AppDelegate.m */; }; 14 | C93614F11A7D7BBC00AFECB4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C93614EF1A7D7BBC00AFECB4 /* Images.xcassets */; }; 15 | C93614F51A7D7BCE00AFECB4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C93614F21A7D7BCE00AFECB4 /* main.m */; }; 16 | C9F05FF4177A46D100978AF2 /* WLContainerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9F05FEF177A46D100978AF2 /* WLContainerController.m */; }; 17 | C9F05FF5177A46D100978AF2 /* WLSelectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9F05FF1177A46D100978AF2 /* WLSelectionController.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 22 | 1D6058910D05DD3D006BFB54 /* WLContainerControllers.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WLContainerControllers.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 24 | C93614EA1A7D7B7300AFECB4 /* WLCompositeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WLCompositeController.h; path = WLContainerControllers/WLCompositeController.h; sourceTree = SOURCE_ROOT; }; 25 | C93614EB1A7D7B7300AFECB4 /* WLCompositeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WLCompositeController.m; path = WLContainerControllers/WLCompositeController.m; sourceTree = SOURCE_ROOT; }; 26 | C93614ED1A7D7BBC00AFECB4 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Demo/AppDelegate.h; sourceTree = SOURCE_ROOT; }; 27 | C93614EE1A7D7BBC00AFECB4 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Demo/AppDelegate.m; sourceTree = SOURCE_ROOT; }; 28 | C93614EF1A7D7BBC00AFECB4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Demo/Images.xcassets; sourceTree = SOURCE_ROOT; }; 29 | C93614F21A7D7BCE00AFECB4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Demo/main.m; sourceTree = SOURCE_ROOT; }; 30 | C93614F41A7D7BCE00AFECB4 /* WLContainerControllers-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "WLContainerControllers-Info.plist"; path = "Demo/WLContainerControllers-Info.plist"; sourceTree = SOURCE_ROOT; }; 31 | C9F05FEE177A46D100978AF2 /* WLContainerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WLContainerController.h; path = WLContainerControllers/WLContainerController.h; sourceTree = SOURCE_ROOT; }; 32 | C9F05FEF177A46D100978AF2 /* WLContainerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WLContainerController.m; path = WLContainerControllers/WLContainerController.m; sourceTree = SOURCE_ROOT; }; 33 | C9F05FF0177A46D100978AF2 /* WLSelectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WLSelectionController.h; path = WLContainerControllers/WLSelectionController.h; sourceTree = SOURCE_ROOT; }; 34 | C9F05FF1177A46D100978AF2 /* WLSelectionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WLSelectionController.m; path = WLContainerControllers/WLSelectionController.m; sourceTree = SOURCE_ROOT; }; 35 | C9F06007177A520F00978AF2 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 36 | C9F06008177A520F00978AF2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = SOURCE_ROOT; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 45 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | 080E96DDFE201D6D7F000001 /* Demo */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | C93614ED1A7D7BBC00AFECB4 /* AppDelegate.h */, 56 | C93614EE1A7D7BBC00AFECB4 /* AppDelegate.m */, 57 | C93614EF1A7D7BBC00AFECB4 /* Images.xcassets */, 58 | C9F325AE17EB5F6A0033A498 /* Supporting Files */, 59 | ); 60 | name = Demo; 61 | path = Classes; 62 | sourceTree = ""; 63 | }; 64 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 1D6058910D05DD3D006BFB54 /* WLContainerControllers.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | C9D0ACFD11F096C400A84A80 /* WLContainerControllers */, 76 | 080E96DDFE201D6D7F000001 /* Demo */, 77 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 78 | 19C28FACFE9D520D11CA2CBB /* Products */, 79 | ); 80 | name = CustomTemplate; 81 | sourceTree = ""; 82 | }; 83 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 87 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | C9D0ACFD11F096C400A84A80 /* WLContainerControllers */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | C9F06008177A520F00978AF2 /* README.md */, 96 | C9F06007177A520F00978AF2 /* LICENSE */, 97 | C9F05FEE177A46D100978AF2 /* WLContainerController.h */, 98 | C9F05FEF177A46D100978AF2 /* WLContainerController.m */, 99 | C9F05FF0177A46D100978AF2 /* WLSelectionController.h */, 100 | C9F05FF1177A46D100978AF2 /* WLSelectionController.m */, 101 | C93614EA1A7D7B7300AFECB4 /* WLCompositeController.h */, 102 | C93614EB1A7D7B7300AFECB4 /* WLCompositeController.m */, 103 | ); 104 | name = WLContainerControllers; 105 | path = Classes; 106 | sourceTree = ""; 107 | }; 108 | C9F325AE17EB5F6A0033A498 /* Supporting Files */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | C93614F21A7D7BCE00AFECB4 /* main.m */, 112 | C93614F41A7D7BCE00AFECB4 /* WLContainerControllers-Info.plist */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 1D6058900D05DD3D006BFB54 /* WLContainerControllers */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "WLContainerControllers" */; 123 | buildPhases = ( 124 | 1D60588D0D05DD3D006BFB54 /* Resources */, 125 | 1D60588E0D05DD3D006BFB54 /* Sources */, 126 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 127 | C9D0AD2011F09BDE00A84A80 /* ShellScript */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = WLContainerControllers; 134 | productName = WLContainerControllers; 135 | productReference = 1D6058910D05DD3D006BFB54 /* WLContainerControllers.app */; 136 | productType = "com.apple.product-type.application"; 137 | }; 138 | /* End PBXNativeTarget section */ 139 | 140 | /* Begin PBXProject section */ 141 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 142 | isa = PBXProject; 143 | attributes = { 144 | LastUpgradeCheck = 0700; 145 | ORGANIZATIONNAME = "I Wonder Phone"; 146 | }; 147 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "WLContainerControllers" */; 148 | compatibilityVersion = "Xcode 3.2"; 149 | developmentRegion = English; 150 | hasScannedForEncodings = 1; 151 | knownRegions = ( 152 | English, 153 | Japanese, 154 | French, 155 | German, 156 | en, 157 | ); 158 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 159 | projectDirPath = ""; 160 | projectRoot = ""; 161 | targets = ( 162 | 1D6058900D05DD3D006BFB54 /* WLContainerControllers */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | C93614F11A7D7BBC00AFECB4 /* Images.xcassets in Resources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXResourcesBuildPhase section */ 177 | 178 | /* Begin PBXShellScriptBuildPhase section */ 179 | C9D0AD2011F09BDE00A84A80 /* ShellScript */ = { 180 | isa = PBXShellScriptBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | ); 184 | inputPaths = ( 185 | ); 186 | outputPaths = ( 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | shellPath = /bin/sh; 190 | shellScript = "#appledoc -p \"$PROJECT_NAME\" -i \"$SRCROOT\" -o \"$TMPDIR/doc\" --xhtml --docset"; 191 | }; 192 | /* End PBXShellScriptBuildPhase section */ 193 | 194 | /* Begin PBXSourcesBuildPhase section */ 195 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 196 | isa = PBXSourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | C93614F51A7D7BCE00AFECB4 /* main.m in Sources */, 200 | C9F05FF4177A46D100978AF2 /* WLContainerController.m in Sources */, 201 | C93614EC1A7D7B7300AFECB4 /* WLCompositeController.m in Sources */, 202 | C9F05FF5177A46D100978AF2 /* WLSelectionController.m in Sources */, 203 | C93614F01A7D7BBC00AFECB4 /* AppDelegate.m in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 215 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 216 | CLANG_ENABLE_MODULES = YES; 217 | CLANG_ENABLE_OBJC_ARC = YES; 218 | COPY_PHASE_STRIP = NO; 219 | GCC_DYNAMIC_NO_PIC = NO; 220 | GCC_OPTIMIZATION_LEVEL = 0; 221 | INFOPLIST_FILE = "Demo/WLContainerControllers-Info.plist"; 222 | PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}"; 223 | PRODUCT_NAME = WLContainerControllers; 224 | }; 225 | name = Debug; 226 | }; 227 | 1D6058950D05DD3E006BFB54 /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | ALWAYS_SEARCH_USER_PATHS = NO; 231 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 232 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | COPY_PHASE_STRIP = YES; 236 | INFOPLIST_FILE = "Demo/WLContainerControllers-Info.plist"; 237 | PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}"; 238 | PRODUCT_NAME = WLContainerControllers; 239 | VALIDATE_PRODUCT = YES; 240 | }; 241 | name = Release; 242 | }; 243 | C01FCF4F08A954540054247B /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 249 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 250 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 251 | CLANG_WARN_UNREACHABLE_CODE = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 254 | ENABLE_STRICT_OBJC_MSGSEND = YES; 255 | ENABLE_TESTABILITY = YES; 256 | GCC_C_LANGUAGE_STANDARD = c99; 257 | GCC_NO_COMMON_BLOCKS = YES; 258 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 259 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 262 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | ONLY_ACTIVE_ARCH = YES; 268 | OTHER_CFLAGS = ( 269 | "-Wall", 270 | "-Wextra", 271 | "-Wno-unused-parameter", 272 | ); 273 | SDKROOT = iphoneos; 274 | TARGETED_DEVICE_FAMILY = "1,2"; 275 | }; 276 | name = Debug; 277 | }; 278 | C01FCF5008A954540054247B /* Release */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 282 | CLANG_WARN_EMPTY_BODY = YES; 283 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 284 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 285 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 286 | CLANG_WARN_UNREACHABLE_CODE = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_C_LANGUAGE_STANDARD = c99; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 294 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 295 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | OTHER_CFLAGS = ( 301 | "-Wall", 302 | "-Wextra", 303 | "-Wno-unused-parameter", 304 | ); 305 | SDKROOT = iphoneos; 306 | TARGETED_DEVICE_FAMILY = "1,2"; 307 | }; 308 | name = Release; 309 | }; 310 | /* End XCBuildConfiguration section */ 311 | 312 | /* Begin XCConfigurationList section */ 313 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "WLContainerControllers" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | 1D6058940D05DD3E006BFB54 /* Debug */, 317 | 1D6058950D05DD3E006BFB54 /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | defaultConfigurationName = Release; 321 | }; 322 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "WLContainerControllers" */ = { 323 | isa = XCConfigurationList; 324 | buildConfigurations = ( 325 | C01FCF4F08A954540054247B /* Debug */, 326 | C01FCF5008A954540054247B /* Release */, 327 | ); 328 | defaultConfigurationIsVisible = 0; 329 | defaultConfigurationName = Release; 330 | }; 331 | /* End XCConfigurationList section */ 332 | }; 333 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 334 | } 335 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/xcuserdata/an0.xcuserdatad/xcschemes/WLContainerControllers.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 63 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers.xcodeproj/xcuserdata/an0.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WLContainerControllers.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 1D6058900D05DD3D006BFB54 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLCompositeController.h: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 1/31/15. 2 | // Copyright (c) 2015 I Wonder Phone. All rights reserved. 3 | 4 | #import 5 | 6 | @interface WLCompositeController : UIViewController 7 | 8 | @property(nonatomic, copy) NSArray<__kindof UIViewController *> *viewControllers; 9 | @property(nonatomic, readonly) UIView *contentView; 10 | 11 | #pragma mark - Protected 12 | 13 | - (void)layoutContentView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLCompositeController.m: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 1/31/15. 2 | // Copyright (c) 2015 I Wonder Phone. All rights reserved. 3 | 4 | #import "WLCompositeController.h" 5 | 6 | @implementation WLCompositeController { 7 | NSMutableArray<__kindof UIViewController *> *_viewControllers; 8 | } 9 | 10 | #pragma mark - View lifecycle 11 | 12 | - (void)viewDidLoad { 13 | [super viewDidLoad]; 14 | 15 | for (UIViewController *vc in _viewControllers) { 16 | [self.contentView addSubview:vc.view]; 17 | } 18 | [self layoutContentView]; 19 | } 20 | 21 | - (UIView *)contentView { 22 | return self.view; 23 | } 24 | 25 | - (BOOL)shouldAutorotate { 26 | for (UIViewController *vc in _viewControllers) { 27 | if (![vc shouldAutorotate]) { 28 | return NO; 29 | } 30 | } 31 | 32 | return YES; 33 | } 34 | 35 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 36 | NSUInteger mask; 37 | if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { 38 | mask = UIInterfaceOrientationMaskAll; 39 | } else { 40 | mask = UIInterfaceOrientationMaskAllButUpsideDown; 41 | } 42 | 43 | for (UIViewController *vc in _viewControllers) { 44 | mask &= [vc supportedInterfaceOrientations]; 45 | } 46 | 47 | return mask; 48 | } 49 | 50 | - (void)viewWillLayoutSubviews { 51 | [super viewWillLayoutSubviews]; 52 | [self layoutContentView]; 53 | } 54 | 55 | - (void)layoutContentView { 56 | 57 | } 58 | 59 | #pragma mark - Managing the View Controllers 60 | 61 | - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers { 62 | [self setViewControllers:viewControllers animated:NO]; 63 | } 64 | 65 | - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers animated:(BOOL)animated { 66 | if ([_viewControllers isEqualToArray:viewControllers]) return; 67 | 68 | for (UIViewController *vc in _viewControllers) { 69 | [vc willMoveToParentViewController:nil]; 70 | if (self.isViewLoaded) { 71 | [vc.view removeFromSuperview]; 72 | } 73 | [vc removeFromParentViewController]; 74 | } 75 | 76 | for (UIViewController *vc in viewControllers) { 77 | [self addChildViewController:vc]; 78 | if (self.isViewLoaded) { 79 | [self.contentView addSubview:vc.view]; 80 | } 81 | } 82 | 83 | if (self.isViewLoaded) { 84 | [self layoutContentView]; 85 | } 86 | 87 | for (UIViewController *vc in viewControllers) { 88 | [vc didMoveToParentViewController:self]; 89 | } 90 | 91 | _viewControllers = [viewControllers mutableCopy]; 92 | } 93 | 94 | - (void)replaceViewControllerAtIndex:(NSUInteger)index withViewController:(UIViewController *)viewController { 95 | UIViewController *vc = _viewControllers[index]; 96 | if (vc == viewController) return; 97 | 98 | [self addChildViewController:viewController]; 99 | [vc willMoveToParentViewController:nil]; 100 | if (self.isViewLoaded) { 101 | [vc.view removeFromSuperview]; 102 | [self.contentView addSubview:viewController.view]; 103 | [self layoutContentView]; 104 | } 105 | [viewController didMoveToParentViewController:self]; 106 | [vc removeFromParentViewController]; 107 | 108 | _viewControllers[index] = viewController; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLContainerController.h: -------------------------------------------------------------------------------- 1 | // Created by Wang Ling on 7/16/10. 2 | // Copyright I Wonder Phone 2010. All rights reserved. 3 | 4 | #import 5 | 6 | /** 7 | The WLContainerController class implements a generic container controller that manages a content controller. 8 | */ 9 | @interface WLContainerController : UIViewController { 10 | @protected 11 | UIViewController *_contentController; 12 | BOOL _isTransitioningContentView; 13 | } 14 | 15 | @property (nonatomic, retain) UIViewController *contentController; 16 | @property (nonatomic, assign) UIEdgeInsets contentInset; 17 | @property (nonatomic, readonly) UIView *contentView; 18 | @property (nonatomic, strong) UIView *backgroundView; 19 | @property (nonatomic, assign) BOOL inheritsTitle; 20 | @property (nonatomic, assign) BOOL inheritsTitleView; 21 | @property (nonatomic, assign) BOOL inheritsLeftBarButtonItems; 22 | @property (nonatomic, assign) BOOL inheritsRightBarButtonItems; 23 | @property (nonatomic, assign) BOOL inheritsBackBarButtonItem; 24 | @property (nonatomic, assign) BOOL inheritsToolbarItems; 25 | @property (nonatomic, assign) BOOL inheritsTabBarItem; 26 | 27 | @property (nonatomic, readonly) BOOL isViewVisible; 28 | 29 | #pragma mark - Protected methods 30 | 31 | - (void)unregisterKVOForNavigationBar; 32 | - (void)unregisterKVOForToolbar; 33 | - (void)unregisterKVOForTabBar; 34 | - (void)updateNavigationBar; 35 | - (void)updateToolbar; 36 | - (void)updateTabBar; 37 | - (void)layoutContentView:(UIView *)contentView; 38 | 39 | @end 40 | 41 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLContainerController.m: -------------------------------------------------------------------------------- 1 | // Created by Wang Ling on 7/16/10. 2 | // Copyright I Wonder Phone 2010. All rights reserved. 3 | 4 | #import "WLContainerController.h" 5 | 6 | @interface WLContainerController () { 7 | BOOL _observesTitle; 8 | BOOL _observesTitleView; 9 | BOOL _observesLeftButtonItems; 10 | BOOL _observesRightButtonItems; 11 | BOOL _observesBackButtonItem; 12 | BOOL _observesToolbarItems; 13 | BOOL _observesTabBarItem; 14 | } 15 | 16 | @end 17 | 18 | @implementation WLContainerController 19 | 20 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 21 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 22 | if (self) { 23 | self.automaticallyAdjustsScrollViewInsets = NO; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)dealloc { 29 | [self unregisterKVOForNavigationBar]; 30 | [self unregisterKVOForToolbar]; 31 | [self unregisterKVOForTabBar]; 32 | } 33 | 34 | - (void)unregisterKVOForNavigationBar { 35 | if (_observesTitle) { 36 | [_contentController removeObserver:self forKeyPath:@"title"]; 37 | [_contentController removeObserver:self forKeyPath:@"navigationItem.title"]; 38 | _observesTitle = NO; 39 | } 40 | 41 | if (_observesTitleView) { 42 | [_contentController removeObserver:self forKeyPath:@"navigationItem.titleView"]; 43 | _observesTitleView = NO; 44 | } 45 | 46 | if (_observesLeftButtonItems) { 47 | [_contentController removeObserver:self forKeyPath:@"navigationItem.leftBarButtonItem"]; 48 | [_contentController removeObserver:self forKeyPath:@"navigationItem.leftBarButtonItems"]; 49 | _observesLeftButtonItems = NO; 50 | } 51 | 52 | if (_observesRightButtonItems) { 53 | [_contentController removeObserver:self forKeyPath:@"navigationItem.rightBarButtonItem"]; 54 | [_contentController removeObserver:self forKeyPath:@"navigationItem.rightBarButtonItems"]; 55 | _observesRightButtonItems = NO; 56 | } 57 | 58 | if (_observesBackButtonItem) { 59 | [_contentController removeObserver:self forKeyPath:@"navigationItem.backBarButtonItem"]; 60 | _observesBackButtonItem = NO; 61 | } 62 | } 63 | 64 | - (void)unregisterKVOForToolbar { 65 | if (_observesToolbarItems) { 66 | [_contentController removeObserver:self forKeyPath:@"toolbarItems"]; 67 | _observesToolbarItems = NO; 68 | } 69 | } 70 | 71 | - (void)unregisterKVOForTabBar { 72 | if (_observesTabBarItem) { 73 | [_contentController removeObserver:self forKeyPath:@"tabBarItem.title"]; 74 | [_contentController removeObserver:self forKeyPath:@"tabBarItem.badgeValue"]; 75 | [_contentController removeObserver:self forKeyPath:@"tabBarItem.enabled"]; 76 | _observesTabBarItem = NO; 77 | } 78 | } 79 | 80 | #pragma mark - Content View management 81 | 82 | - (void)setContentController:(UIViewController *)contentController { 83 | if (_contentController == contentController) return; 84 | 85 | [self unregisterKVOForNavigationBar]; 86 | [self unregisterKVOForToolbar]; 87 | [self unregisterKVOForTabBar]; 88 | 89 | [_contentController willMoveToParentViewController:nil]; 90 | if (contentController) { 91 | [self addChildViewController:contentController]; 92 | } 93 | 94 | if (self.isViewLoaded) { 95 | if (_contentController.view.superview == self.view) { 96 | [_contentController.view removeFromSuperview]; 97 | } 98 | if (contentController) { 99 | UIView *contentView = contentController.view; 100 | if (contentView.superview != self.view) { 101 | [self layoutContentView:self.contentView]; 102 | [self.view addSubview:contentView]; 103 | } 104 | } 105 | } 106 | 107 | if (contentController) { 108 | [contentController didMoveToParentViewController:self]; 109 | } 110 | [_contentController removeFromParentViewController]; 111 | 112 | _contentController = contentController; 113 | 114 | if (self.isViewLoaded && _contentController) { 115 | // !!!: Update bar items after loading content view since content controller's bar items usually are configured in its viewDidLoad method, but before adding content view because otherwise viewWillLayoutSubviews may be called too early during nav bar & toolbar updating before self.contentView is updated to the new value. 116 | [self updateNavigationBar]; 117 | [self updateToolbar]; 118 | } 119 | 120 | if (_contentController) { 121 | // Tab bar item should be configured before view is loaded. Otherwise all content view controllers are required to load their views before tab bar can be configured. 122 | [self updateTabBar]; 123 | } 124 | } 125 | 126 | - (UIView *)contentView { 127 | return self.contentController.view; 128 | } 129 | 130 | - (void)layoutContentView:(UIView *)contentView { 131 | contentView.frame = UIEdgeInsetsInsetRect(self.view.bounds, self.contentInset); 132 | } 133 | 134 | - (void)setContentInset:(UIEdgeInsets)insets { 135 | if (UIEdgeInsetsEqualToEdgeInsets(insets, _contentInset)) return; 136 | 137 | _contentInset = insets; 138 | [self.view setNeedsLayout]; 139 | } 140 | 141 | - (void)setBackgroundView:(UIView *)backgroundView { 142 | if (_backgroundView == backgroundView) return; 143 | 144 | [_backgroundView removeFromSuperview]; 145 | _backgroundView = backgroundView; 146 | if (_backgroundView) { 147 | _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 148 | if (self.isViewLoaded) { 149 | _backgroundView.frame = self.view.bounds; 150 | [self.view insertSubview:_backgroundView atIndex:0]; 151 | } 152 | } 153 | } 154 | 155 | - (void)viewWillLayoutSubviews { 156 | [super viewWillLayoutSubviews]; 157 | if (!_isTransitioningContentView) { 158 | [self layoutContentView:self.contentView]; 159 | } 160 | } 161 | 162 | #pragma mark - Update navigation bar, toolbar, tab bar 163 | 164 | - (void)updateNavigationBar { 165 | UIViewController *contentController = self.contentController; 166 | 167 | if (_inheritsTitle) { 168 | if (!_observesTitle) { 169 | [contentController addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 170 | [contentController addObserver:self forKeyPath:@"navigationItem.title" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 171 | _observesTitle = YES; 172 | } 173 | } 174 | if (_inheritsTitleView) { 175 | if (!_observesTitleView) { 176 | [contentController addObserver:self forKeyPath:@"navigationItem.titleView" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 177 | _observesTitleView = YES; 178 | } 179 | } 180 | if (_inheritsLeftBarButtonItems) { 181 | if (!_observesLeftButtonItems) { 182 | [contentController addObserver:self forKeyPath:@"navigationItem.leftBarButtonItem" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 183 | [contentController addObserver:self forKeyPath:@"navigationItem.leftBarButtonItems" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 184 | _observesLeftButtonItems = YES; 185 | } 186 | } 187 | if (_inheritsRightBarButtonItems) { 188 | if (!_observesRightButtonItems) { 189 | [contentController addObserver:self forKeyPath:@"navigationItem.rightBarButtonItem" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 190 | [contentController addObserver:self forKeyPath:@"navigationItem.rightBarButtonItems" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 191 | _observesRightButtonItems = YES; 192 | } 193 | } 194 | if (_inheritsBackBarButtonItem) { 195 | if (!_observesBackButtonItem) { 196 | [contentController addObserver:self forKeyPath:@"navigationItem.backBarButtonItem" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 197 | _observesBackButtonItem = YES; 198 | } 199 | } 200 | } 201 | 202 | - (void)updateToolbar { 203 | UIViewController *contentController = self.contentController; 204 | 205 | if (_inheritsToolbarItems) { 206 | if (!_observesToolbarItems) { 207 | [contentController addObserver:self forKeyPath:@"toolbarItems" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 208 | _observesToolbarItems = YES; 209 | } 210 | } 211 | } 212 | 213 | - (void)updateTabBar { 214 | UIViewController *contentController = self.contentController; 215 | 216 | if (_inheritsTabBarItem) { 217 | if (!_observesTabBarItem) { 218 | [contentController addObserver:self forKeyPath:@"tabBarItem.title" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 219 | [contentController addObserver:self forKeyPath:@"tabBarItem.badgeValue" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 220 | [contentController addObserver:self forKeyPath:@"tabBarItem.enabled" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 221 | _observesTabBarItem = YES; 222 | } 223 | } 224 | } 225 | 226 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 227 | if (object == _contentController) { 228 | id value = change[NSKeyValueChangeNewKey]; 229 | if (value == [NSNull null]) { 230 | value = nil; 231 | } 232 | 233 | [self setValue:value forKeyPath:keyPath]; 234 | } 235 | } 236 | 237 | #pragma mark - View events 238 | 239 | - (void)viewDidLoad { 240 | [super viewDidLoad]; 241 | 242 | // Add background view. 243 | if (_backgroundView) { 244 | _backgroundView.frame = self.view.bounds; 245 | [self.view insertSubview:_backgroundView atIndex:0]; 246 | } 247 | 248 | // Add content view. 249 | if (self.contentView) { 250 | [self layoutContentView:self.contentView]; 251 | [self.view addSubview:self.contentView]; 252 | } 253 | 254 | // Update bar items after loading content view since content controller's bar items usually are configured in its viewDidLoad method. 255 | if (_contentController) { 256 | [self updateNavigationBar]; 257 | [self updateToolbar]; 258 | } 259 | } 260 | 261 | - (void)viewDidAppear:(BOOL)animated { 262 | [super viewDidAppear:animated]; 263 | _isViewVisible = YES; 264 | } 265 | 266 | - (void)viewDidDisappear:(BOOL)animated { 267 | [super viewDidDisappear:animated]; 268 | _isViewVisible = NO; 269 | } 270 | 271 | #pragma mark - Rotation support 272 | 273 | - (BOOL)shouldAutorotate { 274 | BOOL result = YES; 275 | if (_contentController) { 276 | result = [_contentController shouldAutorotate]; 277 | } 278 | return result; 279 | } 280 | 281 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 282 | NSUInteger mask; 283 | 284 | if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { 285 | mask = UIInterfaceOrientationMaskAll; 286 | } else { 287 | mask = UIInterfaceOrientationMaskAllButUpsideDown; 288 | } 289 | 290 | if (_contentController) { 291 | mask &= [_contentController supportedInterfaceOrientations]; 292 | } 293 | 294 | return mask; 295 | } 296 | 297 | #pragma mark - State Preservation and Restoration 298 | 299 | #define kStateKeyTitle @"title" 300 | #define kStateKeyContentViewController @"content_view_controller" 301 | #define kStateKeyContentInset @"content_inset" 302 | #define kStateKeyInheritsTitle @"inherits_title" 303 | #define kStateKeyInheritsTitleView @"inherits_title_view" 304 | #define kStateKeyInheritsLeftBarButtonItems @"inherits_left_bar_button_items" 305 | #define kStateKeyInheritsRightBarButtonItems @"inherits_right_bar_button_items" 306 | #define kStateKeyInheritsBackBarButtonItem @"inherits_back_bar_button_item" 307 | #define kStateKeyInheritsToolbarItems @"inherits_toolbar_items" 308 | 309 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder { 310 | [super encodeRestorableStateWithCoder:coder]; 311 | 312 | [coder encodeObject:self.title forKey:kStateKeyTitle]; 313 | [coder encodeObject:self.contentController forKey:kStateKeyContentViewController]; 314 | [coder encodeUIEdgeInsets:self.contentInset forKey:kStateKeyContentInset]; 315 | [coder encodeBool:self.inheritsTitle forKey:kStateKeyInheritsTitle]; 316 | [coder encodeBool:self.inheritsTitleView forKey:kStateKeyInheritsTitleView]; 317 | [coder encodeBool:self.inheritsLeftBarButtonItems forKey:kStateKeyInheritsLeftBarButtonItems]; 318 | [coder encodeBool:self.inheritsRightBarButtonItems forKey:kStateKeyInheritsRightBarButtonItems]; 319 | [coder encodeBool:self.inheritsBackBarButtonItem forKey:kStateKeyInheritsBackBarButtonItem]; 320 | [coder encodeBool:self.inheritsToolbarItems forKey:kStateKeyInheritsToolbarItems]; 321 | } 322 | 323 | - (void)decodeRestorableStateWithCoder:(NSCoder *)coder { 324 | [super decodeRestorableStateWithCoder:coder]; 325 | 326 | self.inheritsTitle = [coder decodeBoolForKey:kStateKeyInheritsTitle]; 327 | self.inheritsTitleView = [coder decodeBoolForKey:kStateKeyInheritsTitleView]; 328 | self.inheritsLeftBarButtonItems = [coder decodeBoolForKey:kStateKeyInheritsLeftBarButtonItems]; 329 | self.inheritsRightBarButtonItems = [coder decodeBoolForKey:kStateKeyInheritsRightBarButtonItems]; 330 | self.inheritsBackBarButtonItem = [coder decodeBoolForKey:kStateKeyInheritsBackBarButtonItem]; 331 | self.inheritsToolbarItems = [coder decodeBoolForKey:kStateKeyInheritsToolbarItems]; 332 | self.contentInset = [coder decodeUIEdgeInsetsForKey:kStateKeyContentInset]; 333 | self.contentController = [coder decodeObjectForKey:kStateKeyContentViewController]; 334 | } 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLSelectionController.h: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 8/25/11. 2 | // Copyright (c) 2011 I Wonder Phone. All rights reserved. 3 | 4 | #import "WLContainerController.h" 5 | 6 | @interface WLSelectionController : WLContainerController 7 | 8 | @property(nonatomic, copy) NSArray<__kindof UIViewController *> *viewControllers; 9 | @property(nonatomic, strong) UIViewController *selectedViewController; 10 | @property(nonatomic, assign) NSUInteger selectedIndex; 11 | @property(nonatomic, assign) BOOL selectsLastByDefault; 12 | 13 | - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers animated:(BOOL)animated; 14 | - (BOOL)replaceViewControllerAtIndex:(NSUInteger)index withViewController:(UIViewController *)newViewController; 15 | - (BOOL)exchangeViewControllerAtIndex:(NSUInteger)index1 withViewControllerAtIndex:(NSUInteger)index2; // Exchange view controllers, keep selectedIndex not selectedViewController. 16 | 17 | // These two methods only do view controller management, no view configuration. 18 | - (void)addViewController:(UIViewController *)viewController; 19 | - (void)removeViewController:(UIViewController *)viewController; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /WLContainerControllers/WLContainerControllers/WLSelectionController.m: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 8/25/11. 2 | // Copyright (c) 2011 I Wonder Phone. All rights reserved. 3 | 4 | #import "WLSelectionController.h" 5 | 6 | @implementation WLSelectionController { 7 | NSMutableArray<__kindof UIViewController *> *_viewControllers; 8 | } 9 | 10 | #pragma mark - View lifecycle 11 | 12 | - (void)viewDidLoad { 13 | [super viewDidLoad]; 14 | // Initially select the first/last controller if none is pre-selected. 15 | if (_selectedViewController == nil && _viewControllers.count > 0) { 16 | self.selectedIndex = _selectsLastByDefault ? _viewControllers.count - 1 : 0; 17 | } 18 | } 19 | 20 | - (BOOL)shouldAutorotate { 21 | for (UIViewController *controller in _viewControllers) { 22 | if (![controller shouldAutorotate]) { 23 | return NO; 24 | } 25 | } 26 | 27 | return YES; 28 | } 29 | 30 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 31 | NSUInteger mask; 32 | if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { 33 | mask = UIInterfaceOrientationMaskAll; 34 | } else { 35 | mask = UIInterfaceOrientationMaskAllButUpsideDown; 36 | } 37 | 38 | for (UIViewController *controller in _viewControllers) { 39 | mask &= [controller supportedInterfaceOrientations]; 40 | } 41 | 42 | return mask; 43 | } 44 | 45 | #pragma mark - Managing the View Controllers 46 | 47 | - (void)setContentController:(UIViewController *)contentController { 48 | if (_contentController == contentController) return; 49 | 50 | [self unregisterKVOForNavigationBar]; 51 | [self unregisterKVOForToolbar]; 52 | 53 | if (self.isViewLoaded) { 54 | if (_contentController.view.superview == self.view) { 55 | [_contentController.view removeFromSuperview]; 56 | } 57 | UIView *contentView = contentController.view; 58 | if (contentView.superview != self.view) { 59 | [self layoutContentView:self.contentView]; 60 | [self.view addSubview:contentView]; 61 | } 62 | } 63 | 64 | _contentController = contentController; 65 | 66 | if (self.isViewLoaded) { 67 | // !!!: Update bar items after loading content view since content controller's bar items usually are configured in its viewDidLoad method, but before adding content view because otherwise viewWillLayoutSubviews may be called too early during nav bar & toolbar updating before self.contentView is updated to the new value. 68 | [self updateNavigationBar]; 69 | [self updateToolbar]; 70 | } 71 | } 72 | 73 | - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers { 74 | [self setViewControllers:viewControllers animated:NO]; 75 | } 76 | 77 | - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers animated:(BOOL)animated { 78 | if ([_viewControllers isEqualToArray:viewControllers]) return; 79 | 80 | for (UIViewController *controller in _viewControllers) { 81 | [controller willMoveToParentViewController:nil]; 82 | [controller removeFromParentViewController]; 83 | } 84 | 85 | for (UIViewController *controller in viewControllers) { 86 | [self addChildViewController:controller]; 87 | [controller didMoveToParentViewController:self]; 88 | } 89 | 90 | // Update the selected view controller. 91 | UIViewController *controllerToSelect; 92 | NSUInteger index; 93 | if (_selectsLastByDefault) { 94 | index = viewControllers.count - 1; 95 | } else { 96 | // Reuse the selectedIndex if possible. 97 | if (self.selectedIndex < viewControllers.count) { 98 | index = self.selectedIndex; 99 | } else { 100 | index = 0; 101 | } 102 | } 103 | controllerToSelect = viewControllers[index]; 104 | 105 | // Must update the view controller array before the assignment of selected view controller because of the precondition that the selected view controller must be element of the view contronller array. 106 | _viewControllers = [viewControllers mutableCopy]; 107 | 108 | self.selectedViewController = controllerToSelect; 109 | } 110 | 111 | - (BOOL)replaceViewControllerAtIndex:(NSUInteger)index withViewController:(UIViewController *)newViewController { 112 | UIViewController *viewController = _viewControllers[index]; 113 | if (viewController == newViewController) return NO; 114 | 115 | [viewController willMoveToParentViewController:nil]; 116 | [viewController removeFromParentViewController]; 117 | 118 | [self addChildViewController:newViewController]; 119 | [newViewController didMoveToParentViewController:self]; 120 | 121 | _viewControllers[index] = newViewController; 122 | 123 | // Update the selected view controller if viewController is currently selected. 124 | if (self.selectedViewController == viewController) { 125 | self.selectedViewController = newViewController; 126 | } 127 | 128 | return YES; 129 | } 130 | 131 | - (BOOL)exchangeViewControllerAtIndex:(NSUInteger)index1 withViewControllerAtIndex:(NSUInteger)index2 { 132 | if (index1 == index2) return NO; 133 | 134 | UIViewController *viewController1 = _viewControllers[index1]; 135 | UIViewController *viewController2 = _viewControllers[index2]; 136 | UIViewController *selectedViewController = self.selectedViewController; 137 | [_viewControllers exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; 138 | if (selectedViewController == viewController1) { 139 | self.selectedViewController = viewController2; 140 | } else if (selectedViewController == viewController2) { 141 | self.selectedViewController = viewController1; 142 | } 143 | 144 | return YES; 145 | } 146 | 147 | - (void)addViewController:(UIViewController *)viewController { 148 | if (!_viewControllers) _viewControllers = [NSMutableArray arrayWithObject:viewController]; 149 | else [_viewControllers addObject:viewController]; 150 | } 151 | 152 | - (void)removeViewController:(UIViewController *)viewController { 153 | [_viewControllers removeObject:viewController]; 154 | } 155 | 156 | #pragma mark - Managing the Selected View Controller 157 | 158 | - (void)setSelectedViewController:(UIViewController *)viewController { 159 | if (_selectedViewController == viewController) return; 160 | 161 | self.contentController = viewController; 162 | _selectedViewController = viewController; 163 | } 164 | 165 | - (NSUInteger)selectedIndex { 166 | if (_viewControllers) { 167 | return [_viewControllers indexOfObject:_selectedViewController]; 168 | } else { 169 | return NSNotFound; 170 | } 171 | } 172 | 173 | - (void)setSelectedIndex:(NSUInteger)index { 174 | self.selectedViewController = _viewControllers[index]; 175 | } 176 | 177 | #pragma mark - State Preservation and Restoration 178 | 179 | #define kStateKeyChildViewControllers @"child_view_controllers" 180 | #define kStateKeySelectedIndex @"selected_index" 181 | 182 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder { 183 | [super encodeRestorableStateWithCoder:coder]; 184 | 185 | [coder encodeObject:self.viewControllers forKey:kStateKeyChildViewControllers]; 186 | [coder encodeInteger:self.selectedIndex forKey:kStateKeySelectedIndex]; 187 | } 188 | 189 | - (void)decodeRestorableStateWithCoder:(NSCoder *)coder { 190 | [super decodeRestorableStateWithCoder:coder]; 191 | 192 | self.viewControllers = [coder decodeObjectForKey:kStateKeyChildViewControllers]; 193 | self.selectedIndex = [coder decodeIntegerForKey:kStateKeySelectedIndex]; 194 | } 195 | 196 | @end 197 | -------------------------------------------------------------------------------- /WLPageViewController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C93615111A7D806F00AFECB4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C93615071A7D806F00AFECB4 /* AppDelegate.m */; }; 11 | C93615121A7D806F00AFECB4 /* DataViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C93615091A7D806F00AFECB4 /* DataViewController.m */; }; 12 | C93615131A7D806F00AFECB4 /* DataViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C936150A1A7D806F00AFECB4 /* DataViewController.xib */; }; 13 | C93615141A7D806F00AFECB4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C936150B1A7D806F00AFECB4 /* Images.xcassets */; }; 14 | C93615151A7D806F00AFECB4 /* ModelController.m in Sources */ = {isa = PBXBuildFile; fileRef = C936150D1A7D806F00AFECB4 /* ModelController.m */; }; 15 | C93615161A7D806F00AFECB4 /* PageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C936150F1A7D806F00AFECB4 /* PageViewController.m */; }; 16 | C93615171A7D806F00AFECB4 /* PageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C93615101A7D806F00AFECB4 /* PageViewController.xib */; }; 17 | C936151B1A7D807E00AFECB4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C93615181A7D807E00AFECB4 /* main.m */; }; 18 | C952C48717626F620021A0BA /* WLPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C952C48617626F620021A0BA /* WLPageViewController.m */; }; 19 | C98F5E18177B39C2006044A1 /* WLContainerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C98F5E15177B39C2006044A1 /* WLContainerController.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | C93615061A7D806F00AFECB4 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Demo/AppDelegate.h; sourceTree = SOURCE_ROOT; }; 24 | C93615071A7D806F00AFECB4 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Demo/AppDelegate.m; sourceTree = SOURCE_ROOT; }; 25 | C93615081A7D806F00AFECB4 /* DataViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataViewController.h; path = Demo/DataViewController.h; sourceTree = SOURCE_ROOT; }; 26 | C93615091A7D806F00AFECB4 /* DataViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DataViewController.m; path = Demo/DataViewController.m; sourceTree = SOURCE_ROOT; }; 27 | C936150A1A7D806F00AFECB4 /* DataViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DataViewController.xib; path = Demo/DataViewController.xib; sourceTree = SOURCE_ROOT; }; 28 | C936150B1A7D806F00AFECB4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Demo/Images.xcassets; sourceTree = SOURCE_ROOT; }; 29 | C936150C1A7D806F00AFECB4 /* ModelController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModelController.h; path = Demo/ModelController.h; sourceTree = SOURCE_ROOT; }; 30 | C936150D1A7D806F00AFECB4 /* ModelController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ModelController.m; path = Demo/ModelController.m; sourceTree = SOURCE_ROOT; }; 31 | C936150E1A7D806F00AFECB4 /* PageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PageViewController.h; path = Demo/PageViewController.h; sourceTree = SOURCE_ROOT; }; 32 | C936150F1A7D806F00AFECB4 /* PageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PageViewController.m; path = Demo/PageViewController.m; sourceTree = SOURCE_ROOT; }; 33 | C93615101A7D806F00AFECB4 /* PageViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PageViewController.xib; path = Demo/PageViewController.xib; sourceTree = SOURCE_ROOT; }; 34 | C93615181A7D807E00AFECB4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Demo/main.m; sourceTree = SOURCE_ROOT; }; 35 | C93615191A7D807E00AFECB4 /* WLPageViewController-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "WLPageViewController-Info.plist"; path = "Demo/WLPageViewController-Info.plist"; sourceTree = SOURCE_ROOT; }; 36 | C952C46417626F490021A0BA /* WLPageViewController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WLPageViewController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | C952C48517626F620021A0BA /* WLPageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLPageViewController.h; sourceTree = ""; }; 38 | C952C48617626F620021A0BA /* WLPageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLPageViewController.m; sourceTree = ""; }; 39 | C98F5E14177B39C2006044A1 /* WLContainerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WLContainerController.h; path = WLContainerControllers/WLContainerControllers/WLContainerController.h; sourceTree = SOURCE_ROOT; }; 40 | C98F5E15177B39C2006044A1 /* WLContainerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WLContainerController.m; path = WLContainerControllers/WLContainerControllers/WLContainerController.m; sourceTree = SOURCE_ROOT; }; 41 | C98F5E1A177B3C29006044A1 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 42 | C98F5E1B177B3C29006044A1 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = SOURCE_ROOT; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | C952C46117626F490021A0BA /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | C952C45B17626F490021A0BA = { 57 | isa = PBXGroup; 58 | children = ( 59 | C952C46D17626F490021A0BA /* WLPageViewController */, 60 | C952C48417626F4E0021A0BA /* Demo */, 61 | C952C46517626F490021A0BA /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | C952C46517626F490021A0BA /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | C952C46417626F490021A0BA /* WLPageViewController.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | C952C46D17626F490021A0BA /* WLPageViewController */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | C98F5E1B177B3C29006044A1 /* README.md */, 77 | C98F5E1A177B3C29006044A1 /* LICENSE */, 78 | C952C48C17626FD60021A0BA /* Dependence */, 79 | C952C48517626F620021A0BA /* WLPageViewController.h */, 80 | C952C48617626F620021A0BA /* WLPageViewController.m */, 81 | ); 82 | path = WLPageViewController; 83 | sourceTree = ""; 84 | }; 85 | C952C46E17626F490021A0BA /* Supporting Files */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | C93615181A7D807E00AFECB4 /* main.m */, 89 | C93615191A7D807E00AFECB4 /* WLPageViewController-Info.plist */, 90 | ); 91 | name = "Supporting Files"; 92 | sourceTree = ""; 93 | }; 94 | C952C48417626F4E0021A0BA /* Demo */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | C93615061A7D806F00AFECB4 /* AppDelegate.h */, 98 | C93615071A7D806F00AFECB4 /* AppDelegate.m */, 99 | C93615081A7D806F00AFECB4 /* DataViewController.h */, 100 | C93615091A7D806F00AFECB4 /* DataViewController.m */, 101 | C936150A1A7D806F00AFECB4 /* DataViewController.xib */, 102 | C936150B1A7D806F00AFECB4 /* Images.xcassets */, 103 | C936150C1A7D806F00AFECB4 /* ModelController.h */, 104 | C936150D1A7D806F00AFECB4 /* ModelController.m */, 105 | C936150E1A7D806F00AFECB4 /* PageViewController.h */, 106 | C936150F1A7D806F00AFECB4 /* PageViewController.m */, 107 | C93615101A7D806F00AFECB4 /* PageViewController.xib */, 108 | C952C46E17626F490021A0BA /* Supporting Files */, 109 | ); 110 | name = Demo; 111 | path = WLPageViewController; 112 | sourceTree = ""; 113 | }; 114 | C952C48C17626FD60021A0BA /* Dependence */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | C9F06006177A4F7900978AF2 /* WLContainerControllers */, 118 | ); 119 | name = Dependence; 120 | sourceTree = ""; 121 | }; 122 | C9F06006177A4F7900978AF2 /* WLContainerControllers */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | C98F5E14177B39C2006044A1 /* WLContainerController.h */, 126 | C98F5E15177B39C2006044A1 /* WLContainerController.m */, 127 | ); 128 | name = WLContainerControllers; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXNativeTarget section */ 134 | C952C46317626F490021A0BA /* WLPageViewController */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = C952C48117626F490021A0BA /* Build configuration list for PBXNativeTarget "WLPageViewController" */; 137 | buildPhases = ( 138 | C952C46017626F490021A0BA /* Sources */, 139 | C952C46117626F490021A0BA /* Frameworks */, 140 | C952C46217626F490021A0BA /* Resources */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | ); 146 | name = WLPageViewController; 147 | productName = WLPageViewController; 148 | productReference = C952C46417626F490021A0BA /* WLPageViewController.app */; 149 | productType = "com.apple.product-type.application"; 150 | }; 151 | /* End PBXNativeTarget section */ 152 | 153 | /* Begin PBXProject section */ 154 | C952C45C17626F490021A0BA /* Project object */ = { 155 | isa = PBXProject; 156 | attributes = { 157 | LastUpgradeCheck = 0700; 158 | ORGANIZATIONNAME = Moke; 159 | }; 160 | buildConfigurationList = C952C45F17626F490021A0BA /* Build configuration list for PBXProject "WLPageViewController" */; 161 | compatibilityVersion = "Xcode 3.2"; 162 | developmentRegion = English; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | ); 167 | mainGroup = C952C45B17626F490021A0BA; 168 | productRefGroup = C952C46517626F490021A0BA /* Products */; 169 | projectDirPath = ""; 170 | projectRoot = ""; 171 | targets = ( 172 | C952C46317626F490021A0BA /* WLPageViewController */, 173 | ); 174 | }; 175 | /* End PBXProject section */ 176 | 177 | /* Begin PBXResourcesBuildPhase section */ 178 | C952C46217626F490021A0BA /* Resources */ = { 179 | isa = PBXResourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | C93615141A7D806F00AFECB4 /* Images.xcassets in Resources */, 183 | C93615131A7D806F00AFECB4 /* DataViewController.xib in Resources */, 184 | C93615171A7D806F00AFECB4 /* PageViewController.xib in Resources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXResourcesBuildPhase section */ 189 | 190 | /* Begin PBXSourcesBuildPhase section */ 191 | C952C46017626F490021A0BA /* Sources */ = { 192 | isa = PBXSourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | C93615111A7D806F00AFECB4 /* AppDelegate.m in Sources */, 196 | C952C48717626F620021A0BA /* WLPageViewController.m in Sources */, 197 | C93615151A7D806F00AFECB4 /* ModelController.m in Sources */, 198 | C98F5E18177B39C2006044A1 /* WLContainerController.m in Sources */, 199 | C93615121A7D806F00AFECB4 /* DataViewController.m in Sources */, 200 | C936151B1A7D807E00AFECB4 /* main.m in Sources */, 201 | C93615161A7D806F00AFECB4 /* PageViewController.m in Sources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXSourcesBuildPhase section */ 206 | 207 | /* Begin XCBuildConfiguration section */ 208 | C952C47F17626F490021A0BA /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | ALWAYS_SEARCH_USER_PATHS = NO; 212 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 213 | CLANG_CXX_LIBRARY = "libc++"; 214 | CLANG_ENABLE_MODULES = YES; 215 | CLANG_ENABLE_OBJC_ARC = YES; 216 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 217 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 218 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 221 | COPY_PHASE_STRIP = NO; 222 | ENABLE_TESTABILITY = YES; 223 | GCC_C_LANGUAGE_STANDARD = gnu99; 224 | GCC_DYNAMIC_NO_PIC = NO; 225 | GCC_OPTIMIZATION_LEVEL = 0; 226 | GCC_PREPROCESSOR_DEFINITIONS = ( 227 | "DEBUG=1", 228 | "$(inherited)", 229 | ); 230 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 231 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 234 | ONLY_ACTIVE_ARCH = YES; 235 | OTHER_CFLAGS = ( 236 | "-Wall", 237 | "-Wextra", 238 | "-Wno-unused-parameter", 239 | ); 240 | SDKROOT = iphoneos; 241 | TARGETED_DEVICE_FAMILY = "1,2"; 242 | }; 243 | name = Debug; 244 | }; 245 | C952C48017626F490021A0BA /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 254 | CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; 255 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 258 | COPY_PHASE_STRIP = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 261 | GCC_WARN_UNDECLARED_SELECTOR = YES; 262 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 263 | OTHER_CFLAGS = ( 264 | "-Wall", 265 | "-Wextra", 266 | "-Wno-unused-parameter", 267 | ); 268 | SDKROOT = iphoneos; 269 | TARGETED_DEVICE_FAMILY = "1,2"; 270 | VALIDATE_PRODUCT = YES; 271 | }; 272 | name = Release; 273 | }; 274 | C952C48217626F490021A0BA /* Debug */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 278 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 279 | INFOPLIST_FILE = "Demo/WLPageViewController-Info.plist"; 280 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 281 | PRODUCT_BUNDLE_IDENTIFIER = "com.moke.${PRODUCT_NAME:rfc1034identifier}"; 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | }; 284 | name = Debug; 285 | }; 286 | C952C48317626F490021A0BA /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 291 | INFOPLIST_FILE = "Demo/WLPageViewController-Info.plist"; 292 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 293 | PRODUCT_BUNDLE_IDENTIFIER = "com.moke.${PRODUCT_NAME:rfc1034identifier}"; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | C952C45F17626F490021A0BA /* Build configuration list for PBXProject "WLPageViewController" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | C952C47F17626F490021A0BA /* Debug */, 305 | C952C48017626F490021A0BA /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | C952C48117626F490021A0BA /* Build configuration list for PBXNativeTarget "WLPageViewController" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | C952C48217626F490021A0BA /* Debug */, 314 | C952C48317626F490021A0BA /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | /* End XCConfigurationList section */ 320 | }; 321 | rootObject = C952C45C17626F490021A0BA /* Project object */; 322 | } 323 | -------------------------------------------------------------------------------- /WLPageViewController/WLPageViewController.h: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 7/8/11. 2 | // Copyright 2011 I Wonder Phone. All rights reserved. 3 | 4 | #import "WLContainerController.h" 5 | 6 | @protocol WLPageViewControllerDataSource; 7 | @protocol WLPageViewControllerDelegate; 8 | 9 | @interface WLPageViewController : WLContainerController 10 | 11 | #pragma mark Creating Page View Controllers 12 | - (id)initWithViewController:(UIViewController *)viewController; 13 | - (id)initWithViewController:(UIViewController *)viewController pageSpacing:(CGFloat)pageSpacing; 14 | - (void)turnForward; 15 | - (void)turnBackward; 16 | 17 | @property(nonatomic, weak) id dataSource; 18 | @property(nonatomic, weak) id delegate; 19 | 20 | #pragma mark Configuration 21 | @property(nonatomic, assign) BOOL enableTapPageTurning; 22 | @property(nonatomic, readonly) CGFloat pageSpacing; 23 | 24 | #pragma mark Customizing Appearance 25 | @property(nonatomic, copy) NSDictionary *titleTextAttributes; 26 | 27 | @end 28 | 29 | @protocol WLPageViewControllerDataSource 30 | 31 | - (UIViewController *)pageViewController:(WLPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController; 32 | - (UIViewController *)pageViewController:(WLPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController; 33 | 34 | @end 35 | 36 | @protocol WLPageViewControllerDelegate 37 | 38 | @optional 39 | - (void)pageViewController:(WLPageViewController *)pageViewController willBeginPagingViewController:(UIViewController *)viewController; 40 | - (void)pageViewController:(WLPageViewController *)pageViewController didEndPagingViewController:(UIViewController *)viewController; 41 | 42 | @end 43 | 44 | -------------------------------------------------------------------------------- /WLPageViewController/WLPageViewController.m: -------------------------------------------------------------------------------- 1 | // Created by Ling Wang on 7/8/11. 2 | // Copyright 2011 I Wonder Phone. All rights reserved. 3 | 4 | #import "WLPageViewController.h" 5 | #import 6 | 7 | #define kPagingAnimationDuration 0.4 8 | 9 | @interface WLPageViewController () { 10 | UIPanGestureRecognizer *_panGR; 11 | UITapGestureRecognizer *_tapGR; 12 | UIViewController *_nextViewController; 13 | UIViewController *_previousViewController; 14 | UIViewController *_nnextViewController; 15 | UIViewController *_ppreviousViewController; 16 | UIView *_titleView; 17 | UIView *_previousTitleView; 18 | UIView *_nextTitleView; 19 | UIView *_ppreviousTitleView; 20 | UIView *_nnextTitleView; 21 | BOOL _arePagingAnimationsCancelled; 22 | NSUInteger _pagingAnimationCount; 23 | } 24 | @end 25 | 26 | @implementation WLPageViewController 27 | 28 | - (id)initWithViewController:(UIViewController *)viewController { 29 | return [self initWithViewController:viewController pageSpacing:0]; 30 | } 31 | 32 | - (id)initWithViewController:(UIViewController *)viewController pageSpacing:(CGFloat)pageSpacing { 33 | self = [super init]; 34 | if (self) { 35 | self.contentController = viewController; 36 | _pageSpacing = pageSpacing; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)dealloc { 42 | _panGR.delegate = nil; 43 | } 44 | 45 | #pragma mark - View lifecycle 46 | 47 | - (void)viewDidLoad 48 | { 49 | [super viewDidLoad]; 50 | 51 | // Pan gesture recognizer. 52 | _panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; 53 | _panGR.delegate = self; 54 | [self.view addGestureRecognizer:_panGR]; 55 | 56 | // Tap gesture recognizer. 57 | _tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(turnPage:)]; 58 | _tapGR.enabled = _enableTapPageTurning; 59 | [self.view addGestureRecognizer:_tapGR]; 60 | 61 | // Init navigation bar title view. 62 | self.navigationItem.titleView = [UIView new]; 63 | _titleView = [self setupSubTitleViewWith:_contentController]; 64 | self.navigationItem.titleView.bounds = _titleView.bounds; 65 | [self.navigationItem.titleView addSubview:_titleView]; 66 | } 67 | 68 | - (void)didReceiveMemoryWarning { 69 | [self unloadInvisiblePages]; 70 | [super didReceiveMemoryWarning]; 71 | } 72 | 73 | #pragma mark - Providing Content 74 | 75 | - (void)setContentController:(UIViewController *)contentController { 76 | if (_contentController == contentController) return; 77 | 78 | [self unregisterKVOForNavigationBar]; 79 | [self unregisterKVOForToolbar]; 80 | 81 | [self addChildViewController:contentController]; 82 | if (self.isViewLoaded) { 83 | if (contentController.view.superview != self.view) { 84 | [self layoutContentView:contentController.view]; 85 | [self.view addSubview:contentController.view]; 86 | } 87 | } 88 | [contentController didMoveToParentViewController:self]; 89 | 90 | _contentController = contentController; 91 | 92 | if (self.isViewLoaded) { 93 | [self updateNavigationBar]; 94 | [self updateToolbar]; 95 | } 96 | 97 | if (self.isViewLoaded) { 98 | // !!!: Unloading invisible pages is not only for saving memory but is also necessary for scroll-to-top of content scroll view to work because if there are more than one sub scroll views tapping on status bar does not trigger scroll-to-top. 99 | [self unloadInvisiblePages]; 100 | } 101 | } 102 | 103 | - (UIViewController *)loadPreviousPage { 104 | UIViewController *previousViewController = [_dataSource pageViewController:self viewControllerBeforeViewController:_contentController]; 105 | if (previousViewController == nil) return nil; 106 | 107 | BOOL isNewlyAdded = ![self.childViewControllers containsObject:previousViewController]; 108 | if (isNewlyAdded) { 109 | [self addChildViewController:previousViewController]; 110 | } 111 | CGRect previousFrame = _contentController.view.frame; 112 | previousFrame.origin.x -= _pageSpacing + self.view.bounds.size.width; 113 | previousViewController.view.frame = previousFrame; 114 | [self.view addSubview:previousViewController.view]; 115 | if (isNewlyAdded) { 116 | [previousViewController didMoveToParentViewController:self]; 117 | } 118 | 119 | [_previousTitleView removeFromSuperview]; 120 | _previousTitleView = [self setupSubTitleViewWith:previousViewController]; 121 | // Center title view. 122 | _previousTitleView.center = _titleView.center; 123 | [self.navigationItem.titleView addSubview:_previousTitleView]; 124 | 125 | return previousViewController; 126 | } 127 | 128 | - (UIViewController *)loadNextPage { 129 | UIViewController *nextViewController = [_dataSource pageViewController:self viewControllerAfterViewController:_contentController]; 130 | if (nextViewController == nil) return nil; 131 | 132 | BOOL isNewlyAdded = ![self.childViewControllers containsObject:nextViewController]; 133 | if (isNewlyAdded) { 134 | [self addChildViewController:nextViewController]; 135 | } 136 | CGRect nextFrame = _contentController.view.frame; 137 | nextFrame.origin.x += _pageSpacing + self.view.bounds.size.width; 138 | nextViewController.view.frame = nextFrame; 139 | [self.view addSubview:nextViewController.view]; 140 | if (isNewlyAdded) { 141 | [nextViewController didMoveToParentViewController:self]; 142 | } 143 | 144 | [_nextTitleView removeFromSuperview]; 145 | _nextTitleView = [self setupSubTitleViewWith:nextViewController]; 146 | // Center title view. 147 | _nextTitleView.center = _titleView.center; 148 | [self.navigationItem.titleView addSubview:_nextTitleView]; 149 | 150 | return nextViewController; 151 | } 152 | 153 | - (UIViewController *)loadPPreviousPage { 154 | UIViewController *ppreviousViewController = [_dataSource pageViewController:self viewControllerBeforeViewController:_previousViewController]; 155 | if (ppreviousViewController == nil) return nil; 156 | 157 | BOOL isNewlyAdded = ![self.childViewControllers containsObject:ppreviousViewController]; 158 | if (isNewlyAdded) { 159 | [self addChildViewController:ppreviousViewController]; 160 | } 161 | CGRect previousFrame = _previousViewController.view.frame; 162 | previousFrame.origin.x -= _pageSpacing + self.view.bounds.size.width; 163 | ppreviousViewController.view.frame = previousFrame; 164 | [self.view addSubview:ppreviousViewController.view]; 165 | if (isNewlyAdded) { 166 | [ppreviousViewController didMoveToParentViewController:self]; 167 | } 168 | 169 | [_ppreviousTitleView removeFromSuperview]; 170 | _ppreviousTitleView = [self setupSubTitleViewWith:ppreviousViewController]; 171 | // Center title view. 172 | _ppreviousTitleView.center = _titleView.center; 173 | [self.navigationItem.titleView addSubview:_ppreviousTitleView]; 174 | 175 | return ppreviousViewController; 176 | } 177 | 178 | - (UIViewController *)loadNNextPage { 179 | UIViewController *nnextViewController = [_dataSource pageViewController:self viewControllerAfterViewController:_nextViewController]; 180 | if (nnextViewController == nil) return nil; 181 | 182 | BOOL isNewlyAdded = ![self.childViewControllers containsObject:nnextViewController]; 183 | if (isNewlyAdded) { 184 | [self addChildViewController:nnextViewController]; 185 | } 186 | CGRect nextFrame = _nextViewController.view.frame; 187 | nextFrame.origin.x += _pageSpacing + self.view.bounds.size.width; 188 | nnextViewController.view.frame = nextFrame; 189 | [self.view addSubview:nnextViewController.view]; 190 | if (isNewlyAdded) { 191 | [nnextViewController didMoveToParentViewController:self]; 192 | } 193 | 194 | [_nnextTitleView removeFromSuperview]; 195 | _nnextTitleView = [self setupSubTitleViewWith:nnextViewController]; 196 | // Center title view. 197 | _nnextTitleView.center = _titleView.center; 198 | [self.navigationItem.titleView addSubview:_nnextTitleView]; 199 | 200 | return nnextViewController; 201 | } 202 | 203 | - (void)unloadInvisiblePages { 204 | CGRect bounds = self.view.bounds; 205 | NSMutableArray *vcToUnload = [NSMutableArray arrayWithCapacity:self.childViewControllers.count]; 206 | for (UIViewController *vc in self.childViewControllers) { 207 | UIView *v = vc.view; 208 | if (!CGRectIntersectsRect(bounds, v.frame)) { 209 | [vcToUnload addObject:vc]; 210 | } 211 | } 212 | for (UIViewController *vc in vcToUnload) { 213 | if (vc == _previousViewController) { 214 | _previousViewController = nil; 215 | [_ppreviousTitleView removeFromSuperview]; 216 | _ppreviousTitleView = nil; 217 | } else if (vc == _nextViewController) { 218 | _nextViewController = nil; 219 | [_nextTitleView removeFromSuperview]; 220 | _nextTitleView = nil; 221 | } 222 | 223 | if (vc == _ppreviousViewController) { 224 | _ppreviousViewController = nil; 225 | [_ppreviousTitleView removeFromSuperview]; 226 | _ppreviousTitleView = nil; 227 | } else if (vc == _nnextViewController) { 228 | _nnextViewController = nil; 229 | [_nnextTitleView removeFromSuperview]; 230 | _nnextTitleView = nil; 231 | } 232 | UIView *v = vc.view; 233 | [vc willMoveToParentViewController:nil]; 234 | [v removeFromSuperview]; 235 | [vc removeFromParentViewController]; 236 | // FIXME: workaround for paging happens simultaneously with dismissing of page view controller. 237 | if (self.parentViewController == nil) { 238 | [vc viewDidDisappear:NO]; 239 | } 240 | } 241 | } 242 | 243 | - (void)layoutContentView:(UIView *)contentView { 244 | [super layoutContentView:contentView]; 245 | // Maintain the invariant relationships among previous view, content view and next view. 246 | CGFloat pageDistance = _pageSpacing + self.view.bounds.size.width; 247 | if (_previousViewController) { 248 | _previousViewController.view.frame = CGRectOffset(_contentController.view.frame, -pageDistance, 0.f); 249 | } 250 | if (_nextViewController) { 251 | _nextViewController.view.frame = CGRectOffset(_contentController.view.frame, pageDistance, 0.f); 252 | } 253 | if (_ppreviousViewController) { 254 | _ppreviousViewController.view.frame = CGRectOffset(_previousViewController.view.frame, -pageDistance, 0.f); 255 | } 256 | if (_nnextViewController) { 257 | _nnextViewController.view.frame = CGRectOffset(_nextViewController.view.frame, pageDistance, 0.f); 258 | } 259 | } 260 | 261 | #pragma mark - Configuration 262 | 263 | - (void)setEnableTapPageTurning:(BOOL)enableTapPageTurning { 264 | _enableTapPageTurning = enableTapPageTurning; 265 | _tapGR.enabled = _enableTapPageTurning; 266 | } 267 | 268 | #pragma mark - Navigation bar and toolbar configuration 269 | 270 | - (UIView *)setupSubTitleViewWith:(UIViewController *)viewController { 271 | UIView *subTitleView = nil; 272 | CGRect titleViewBounds = self.navigationItem.titleView.bounds; 273 | if (viewController.navigationItem.titleView) { 274 | subTitleView = viewController.navigationItem.titleView; 275 | subTitleView.frame = titleViewBounds; 276 | } else { 277 | UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleViewBounds]; 278 | titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; 279 | titleLabel.textAlignment = NSTextAlignmentCenter; 280 | titleLabel.attributedText = [[NSAttributedString alloc] initWithString:viewController.navigationItem.title attributes:_titleTextAttributes]; 281 | titleLabel.backgroundColor = [UIColor clearColor]; 282 | subTitleView = titleLabel; 283 | } 284 | [subTitleView sizeToFit]; 285 | subTitleView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 286 | return subTitleView; 287 | } 288 | 289 | - (void)setTitleTextAttributes:(NSDictionary *)titleTextAttributes { 290 | if ([_titleTextAttributes isEqual:titleTextAttributes]) return; 291 | 292 | _titleTextAttributes = [titleTextAttributes copy]; 293 | 294 | if (_titleView != _contentController.navigationItem.titleView && _contentController.navigationItem.title != nil) { 295 | ((UILabel *)_titleView).attributedText = [[NSAttributedString alloc] initWithString:_contentController.navigationItem.title attributes:_titleTextAttributes]; 296 | [_titleView sizeToFit]; 297 | } 298 | 299 | if (_previousTitleView != _previousViewController.navigationItem.titleView && _previousViewController.navigationItem.title != nil) { 300 | ((UILabel *)_previousTitleView).attributedText = [[NSAttributedString alloc] initWithString:_previousViewController.navigationItem.title attributes:_titleTextAttributes]; 301 | [_previousTitleView sizeToFit]; 302 | } 303 | 304 | if (_nextTitleView != _nextViewController.navigationItem.titleView && _nextViewController.navigationItem.title != nil) { 305 | ((UILabel *)_nextTitleView).attributedText = [[NSAttributedString alloc] initWithString:_nextViewController.navigationItem.title attributes:_titleTextAttributes]; 306 | [_nextTitleView sizeToFit]; 307 | } 308 | 309 | if (_ppreviousTitleView != _ppreviousViewController.navigationItem.titleView && _ppreviousViewController.navigationItem.title != nil) { 310 | ((UILabel *)_ppreviousTitleView).attributedText = [[NSAttributedString alloc] initWithString:_ppreviousViewController.navigationItem.title attributes:_titleTextAttributes]; 311 | [_ppreviousTitleView sizeToFit]; 312 | } 313 | 314 | if (_nnextTitleView != _nnextViewController.navigationItem.titleView && _nnextViewController.navigationItem.title != nil) { 315 | ((UILabel *)_nnextTitleView).attributedText = [[NSAttributedString alloc] initWithString:_nnextViewController.navigationItem.title attributes:_titleTextAttributes]; 316 | [_nnextTitleView sizeToFit]; 317 | } 318 | 319 | self.navigationItem.titleView.bounds = _titleView.bounds; 320 | } 321 | 322 | #pragma mark - Paging 323 | 324 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 325 | if (gestureRecognizer == _panGR) { 326 | CGPoint velocity = [_panGR velocityInView:_panGR.view]; 327 | return fabs(velocity.x) > 2 * fabs(velocity.y); 328 | } else { 329 | return YES; 330 | } 331 | } 332 | 333 | - (void)pan:(UIPanGestureRecognizer *)gestureRecognizer { 334 | _isTransitioningContentView = YES; 335 | 336 | const CGRect bounds = self.view.bounds; 337 | CGPoint translation = [gestureRecognizer translationInView:self.view]; 338 | const CGPoint boundsCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); 339 | CGPoint center = _contentController.view.center; 340 | CGFloat pageDistance = _pageSpacing + bounds.size.width; 341 | 342 | if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { 343 | [CATransaction setDisableActions:YES]; 344 | if ([_contentController.view.layer animationForKey:@"position.x"]) { 345 | _contentController.view.layer.position = ((CALayer *)_contentController.view.layer.presentationLayer).position; 346 | [_contentController.view.layer removeAnimationForKey:@"position.x"]; 347 | } 348 | if ([_previousViewController.view.layer animationForKey:@"position.x"]) { 349 | CGPoint position = _contentController.view.layer.position; 350 | position.x -= pageDistance; 351 | _previousViewController.view.layer.position = position; 352 | [_previousViewController.view.layer removeAnimationForKey:@"position.x"]; 353 | } 354 | if ([_ppreviousViewController.view.layer animationForKey:@"position.x"]) { 355 | CGPoint position = _contentController.view.layer.position; 356 | position.x -= 2 * pageDistance; 357 | _ppreviousViewController.view.layer.position = position; 358 | [_ppreviousViewController.view.layer removeAnimationForKey:@"position.x"]; 359 | } 360 | if ([_nextViewController.view.layer animationForKey:@"position.x"]) { 361 | CGPoint position = _contentController.view.layer.position; 362 | position.x += pageDistance; 363 | _nextViewController.view.layer.position = position; 364 | [_nextViewController.view.layer removeAnimationForKey:@"position.x"]; 365 | } 366 | if ([_nnextViewController.view.layer animationForKey:@"position.x"]) { 367 | CGPoint position = _contentController.view.layer.position; 368 | position.x += 2 * pageDistance; 369 | _nnextViewController.view.layer.position = position; 370 | [_nnextViewController.view.layer removeAnimationForKey:@"position.x"]; 371 | } 372 | if ([_titleView.layer animationForKey:@"opacity"]) { 373 | _titleView.layer.opacity = ((CALayer *)_titleView.layer.presentationLayer).opacity; 374 | [_titleView.layer removeAnimationForKey:@"opacity"]; 375 | } 376 | if ([_previousTitleView.layer animationForKey:@"opacity"]) { 377 | _previousTitleView.layer.opacity = ((CALayer *)_previousTitleView.layer.presentationLayer).opacity; 378 | [_previousTitleView.layer removeAnimationForKey:@"opacity"]; 379 | } 380 | if ([_ppreviousTitleView.layer animationForKey:@"opacity"]) { 381 | _ppreviousTitleView.layer.opacity = ((CALayer *)_ppreviousTitleView.layer.presentationLayer).opacity; 382 | [_ppreviousTitleView.layer removeAnimationForKey:@"opacity"]; 383 | } 384 | if ([_nextTitleView.layer animationForKey:@"opacity"]) { 385 | _nextTitleView.layer.opacity = ((CALayer *)_nextTitleView.layer.presentationLayer).opacity; 386 | [_nextTitleView.layer removeAnimationForKey:@"opacity"]; 387 | } 388 | if ([_nnextTitleView.layer animationForKey:@"opacity"]) { 389 | _nnextTitleView.layer.opacity = ((CALayer *)_nnextTitleView.layer.presentationLayer).opacity; 390 | [_nnextTitleView.layer removeAnimationForKey:@"opacity"]; 391 | } 392 | [CATransaction setDisableActions:NO]; 393 | 394 | if ([_delegate respondsToSelector:@selector(pageViewController:willBeginPagingViewController:)]) { 395 | [_delegate pageViewController:self willBeginPagingViewController:_contentController]; 396 | } 397 | } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) { 398 | if (center.x < boundsCenter.x) { 399 | if (_nextViewController == nil) { 400 | // The end. Add some damping. 401 | translation.x /= 2.f; 402 | } 403 | } else if (center.x > boundsCenter.x) { 404 | if (_previousViewController == nil) { 405 | // The end. Add some damping. 406 | translation.x /= 2.f; 407 | } 408 | } 409 | 410 | center.x += translation.x; 411 | const CGFloat pageOffset = center.x - boundsCenter.x; 412 | 413 | if (center.x < boundsCenter.x) { 414 | if (_nextViewController == nil) { 415 | _nextViewController = [self loadNextPage]; 416 | } 417 | // Title transition. 418 | if (_nextViewController) { 419 | _titleView.alpha = 1 - fabs(pageOffset) / pageDistance; 420 | _nextTitleView.alpha = 1 - _titleView.alpha; 421 | } 422 | } else if (center.x > boundsCenter.x) { 423 | if (_previousViewController == nil) { 424 | _previousViewController = [self loadPreviousPage]; 425 | } 426 | // Title transition. 427 | if (_previousViewController) { 428 | _titleView.alpha = 1 - fabs(pageOffset) / pageDistance; 429 | _previousTitleView.alpha = 1 - _titleView.alpha; 430 | } 431 | } 432 | 433 | _contentController.view.center = center; 434 | CGPoint previousViewCenter = center; 435 | previousViewCenter.x -= pageDistance; 436 | _previousViewController.view.center = previousViewCenter; 437 | CGPoint nextViewCenter = center; 438 | nextViewCenter.x += pageDistance; 439 | _nextViewController.view.center = nextViewCenter; 440 | 441 | // Reset translation: I'm use incremental translation not accumulative translation. 442 | [gestureRecognizer setTranslation:CGPointZero inView:gestureRecognizer.view]; 443 | } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) { 444 | const CGFloat pageOffset = center.x - boundsCenter.x; 445 | // Elastic paging and bouncing modeled with damping. 446 | CGFloat w_0 = 0.7f; // natural frequency 447 | CGFloat zeta = 0.8f; // damping ratio for under-damping 448 | CGFloat w_d = w_0 * sqrt(1.f - zeta * zeta); // damped frequency 449 | CGFloat x_0 = pageOffset; 450 | 451 | CGPoint velocity = [gestureRecognizer velocityInView:self.view]; 452 | // Scale velocity down. 453 | velocity.x /= 20.f; 454 | velocity.y /= 20.f; 455 | 456 | // Use critical damping to calculate the max displacement: x(t) = (A + B * t) * e^(-w_0 * t) 457 | // Use x(t)' = 0 to get the max x(t) — amplitude. 458 | // x(t)' = [B - w_0 * (A + B * t)] * e^(-w_0 * t) 459 | // x(t)' = 0 => t = 1 / w_0 - A / B. 460 | // x_max = (v_0 / w_0 + x_0) * e^[-v_0 / (v_0 + w_0 * x_0)] 461 | CGFloat A = x_0; 462 | CGFloat B = velocity.x + w_0 * x_0; 463 | CGFloat t_max = MAX(1 / w_0 - A / B, 0); 464 | CGFloat x_max = pow(M_E, -w_0 * t_max) * (A + B * t_max); 465 | // NSLog(@"v_0 = %f, x_0 = %f, t_max = %f, x_max = %f", velocity.x, x_0, t_max, x_max); 466 | 467 | BOOL turnToPreviousPage = NO; 468 | BOOL turnToNextPage = NO; 469 | if (x_max >= 0.5 * pageDistance || velocity.x > 40.f) { 470 | if (_previousViewController == nil) { 471 | _previousViewController = [self loadPreviousPage]; 472 | } 473 | if (_previousViewController) { 474 | turnToPreviousPage = YES; 475 | } 476 | } else if (x_max <= -0.5 * pageDistance || velocity.x < -40.f) { 477 | if (_nextViewController == nil) { 478 | _nextViewController = [self loadNextPage]; 479 | } 480 | if (_nextViewController) { 481 | turnToNextPage = YES; 482 | } 483 | } 484 | 485 | if (turnToPreviousPage) { 486 | // Turn to previous page. 487 | CGPoint previousViewCenter = _previousViewController.view.center; 488 | CGPoint newPreviousViewCenter = boundsCenter; 489 | CGPoint newCenter = CGPointMake(newPreviousViewCenter.x + pageDistance, newPreviousViewCenter.y); 490 | 491 | [CATransaction begin]; 492 | [CATransaction setAnimationDuration:kPagingAnimationDuration]; 493 | 494 | // Equilibrium postion is different, initial replacement is different. 495 | x_0 = previousViewCenter.x - newPreviousViewCenter.x; 496 | 497 | BOOL underDamping = NO; 498 | 499 | // Try critical damping first. 500 | // Limit x_max so that no more than 1 page is scrolled in one direction in one paging. 501 | // When v_0 is large, x_max can be approximated by (v_0 / w_0 + x_0) / e. 502 | const CGFloat X_MAX_LIMIT = 0; 503 | const CGFloat CRITICAL_VELOCITY = w_0 * (X_MAX_LIMIT * M_E - x_0); 504 | if (velocity.x > CRITICAL_VELOCITY) { 505 | underDamping = YES; 506 | velocity.x /= 1.5f; 507 | } 508 | 509 | if (underDamping) { 510 | // NSLog(@"Under-damping"); 511 | // Limit x_max so that no more than 1 page is scrolled in one direction in one paging. 512 | const CGFloat VELOCITY_MAX_LIMIT = 180.f; 513 | if (velocity.x > VELOCITY_MAX_LIMIT) { 514 | velocity.x = VELOCITY_MAX_LIMIT; 515 | } 516 | A = x_0; 517 | B = (zeta * x_0 + velocity.x) / w_d; 518 | CGFloat a = B * w_d - A * zeta * w_0; 519 | CGFloat b = A * w_d + B * zeta * w_0; 520 | CGFloat sin_max = sqrt(a * a / (a * a + b * b)); 521 | CGFloat theta_max; 522 | if (a * b > 0) { 523 | theta_max = asin(sin_max); 524 | } else { 525 | theta_max = M_PI - asin(sin_max); 526 | } 527 | t_max = theta_max / w_d; 528 | if (t_max > 0.f) { 529 | x_max = pow(M_E, -zeta * w_0 * t_max) * (A * cos(w_d * t_max) + B * sin(w_d * t_max)); 530 | // NSLog(@"v_0 = %f, x_0 = %f, t_max = %f, x_max = %f", velocity.x, x_0, t_max, x_max); 531 | if (x_max > 0.f) { 532 | // Part of pre-previous view will be shown temporarily. 533 | _ppreviousViewController = [self loadPPreviousPage]; 534 | } 535 | } 536 | } else { 537 | // NSLog(@"Critical damping"); 538 | A = x_0; 539 | B = velocity.x + w_0 * x_0; 540 | } 541 | 542 | // Current view. 543 | CAKeyframeAnimation *pageAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; 544 | pageAnimation.delegate = self; 545 | pageAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 546 | NSUInteger steps = 100; 547 | NSMutableArray *pageAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 548 | CGFloat value; 549 | for (NSUInteger step = 0; step < steps; ++step) { 550 | CGFloat t = 0.1f * step; 551 | if (underDamping) { 552 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newCenter.x; 553 | } else { 554 | value = pow(M_E, -w_0 * t) * (A + B * t) + newCenter.x; 555 | } 556 | [pageAnimationValues addObject:@(value)]; 557 | } 558 | pageAnimation.values = pageAnimationValues; 559 | [_contentController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 560 | _pagingAnimationCount++; 561 | [CATransaction setDisableActions:YES]; 562 | _contentController.view.layer.position = newCenter; 563 | [CATransaction setDisableActions:NO]; 564 | 565 | CAKeyframeAnimation *titleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; 566 | titleAnimation.delegate = self; 567 | titleAnimation.timingFunction = pageAnimation.timingFunction; 568 | NSMutableArray *titleAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 569 | for (NSUInteger step = 0; step < steps; ++step) { 570 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 571 | [titleAnimationValues addObject:@(value)]; 572 | } 573 | titleAnimation.values = titleAnimationValues; 574 | [_titleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 575 | _pagingAnimationCount++; 576 | [CATransaction setDisableActions:YES]; 577 | _titleView.layer.opacity = 0.f; 578 | [CATransaction setDisableActions:NO]; 579 | 580 | // Previous view. 581 | [pageAnimationValues removeAllObjects]; 582 | for (NSUInteger step = 0; step < steps; ++step) { 583 | CGFloat t = 0.1f * step; 584 | if (underDamping) { 585 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newPreviousViewCenter.x; 586 | } else { 587 | value = pow(M_E, -w_0 * t) * (A + B * t) + newPreviousViewCenter.x; 588 | } 589 | [pageAnimationValues addObject:@(value)]; 590 | } 591 | pageAnimation.values = pageAnimationValues; 592 | [_previousViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 593 | _pagingAnimationCount++; 594 | [CATransaction setDisableActions:YES]; 595 | _previousViewController.view.layer.position = newPreviousViewCenter; 596 | [CATransaction setDisableActions:NO]; 597 | 598 | [titleAnimationValues removeAllObjects]; 599 | for (NSUInteger step = 0; step < steps; ++step) { 600 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 601 | [titleAnimationValues addObject:@(value)]; 602 | } 603 | titleAnimation.values = titleAnimationValues; 604 | [_previousTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 605 | _pagingAnimationCount++; 606 | [CATransaction setDisableActions:YES]; 607 | _previousTitleView.layer.opacity = 1.f; 608 | [CATransaction setDisableActions:NO]; 609 | 610 | if (_ppreviousViewController) { 611 | CGPoint newPPreviousViewCenter = CGPointMake(newPreviousViewCenter.x - pageDistance, newPreviousViewCenter.y); 612 | [pageAnimationValues removeAllObjects]; 613 | for (NSUInteger step = 0; step < steps; ++step) { 614 | CGFloat t = 0.1f * step; 615 | if (underDamping) { 616 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newPPreviousViewCenter.x; 617 | } else { 618 | value = pow(M_E, -w_0 * t) * (A + B * t) + newPPreviousViewCenter.x; 619 | } 620 | [pageAnimationValues addObject:@(value)]; 621 | } 622 | pageAnimation.values = pageAnimationValues; 623 | [_ppreviousViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 624 | _pagingAnimationCount++; 625 | [CATransaction setDisableActions:YES]; 626 | _ppreviousViewController.view.layer.position = newPPreviousViewCenter; 627 | [CATransaction setDisableActions:NO]; 628 | 629 | [titleAnimationValues removeAllObjects]; 630 | for (NSUInteger step = 0; step < steps; ++step) { 631 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 632 | [titleAnimationValues addObject:@(value)]; 633 | } 634 | titleAnimation.values = titleAnimationValues; 635 | [_ppreviousTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 636 | _pagingAnimationCount++; 637 | [CATransaction setDisableActions:YES]; 638 | _ppreviousTitleView.layer.opacity = 0.f; 639 | [CATransaction setDisableActions:NO]; 640 | } 641 | 642 | // Next view. 643 | if (_nextViewController) { 644 | if (_nextViewController != _ppreviousViewController) { 645 | CGPoint newNextViewCenter = CGPointMake(newCenter.x + pageDistance, newCenter.y); 646 | [pageAnimationValues removeAllObjects]; 647 | for (NSUInteger step = 0; step < steps; ++step) { 648 | CGFloat t = 0.1f * step; 649 | if (underDamping) { 650 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newNextViewCenter.x; 651 | } else { 652 | value = pow(M_E, -w_0 * t) * (A + B * t) + newNextViewCenter.x; 653 | } 654 | [pageAnimationValues addObject:@(value)]; 655 | } 656 | pageAnimation.values = pageAnimationValues; 657 | [_nextViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 658 | _pagingAnimationCount++; 659 | [CATransaction setDisableActions:YES]; 660 | _nextViewController.view.layer.position = newNextViewCenter; 661 | [CATransaction setDisableActions:NO]; 662 | } 663 | 664 | if (_nextTitleView != _ppreviousTitleView) { 665 | [titleAnimationValues removeAllObjects]; 666 | for (NSUInteger step = 0; step < steps; ++step) { 667 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 668 | [titleAnimationValues addObject:@(value)]; 669 | } 670 | titleAnimation.values = titleAnimationValues; 671 | [_nextTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 672 | _pagingAnimationCount++; 673 | [CATransaction setDisableActions:YES]; 674 | _nextTitleView.layer.opacity = 0.f; 675 | [CATransaction setDisableActions:NO]; 676 | } 677 | } 678 | 679 | [CATransaction commit]; 680 | } else if (turnToNextPage) { 681 | // Turn to next page. 682 | CGPoint nextViewCenter = _nextViewController.view.center; 683 | CGPoint newNextViewCenter = boundsCenter; 684 | CGPoint newCenter = CGPointMake(newNextViewCenter.x - pageDistance, newNextViewCenter.y); 685 | 686 | [CATransaction begin]; 687 | [CATransaction setAnimationDuration:kPagingAnimationDuration]; 688 | 689 | // Equilibrium postion is different, initial replacement is different. 690 | x_0 = nextViewCenter.x - newNextViewCenter.x; 691 | 692 | BOOL underDamping = NO; 693 | 694 | // Try critical damping first. 695 | // Limit x_max so that no more than 1 page is scrolled in one direction in one paging. 696 | // When v_0 is large, x_max can be approximated by (v_0 / w_0 + x_0) / e. 697 | const CGFloat X_MIN_LIMIT = 0; 698 | const CGFloat VELOCITY_MIN_LIMIT = w_0 * (X_MIN_LIMIT * M_E - x_0); 699 | if (velocity.x < VELOCITY_MIN_LIMIT) { 700 | underDamping = YES; 701 | velocity.x /= 1.5f; 702 | } 703 | 704 | if (underDamping) { 705 | // NSLog(@"Under-damping"); 706 | // Limit x_max so that no more than 1 page is scrolled in one direction in one paging. 707 | const CGFloat VELOCITY_MAX_LIMIT = 180.f; 708 | if (velocity.x < -VELOCITY_MAX_LIMIT) { 709 | velocity.x = -VELOCITY_MAX_LIMIT; 710 | } 711 | A = x_0; 712 | B = (zeta * x_0 + velocity.x) / w_d; 713 | CGFloat a = B * w_d - A * zeta * w_0; 714 | CGFloat b = A * w_d + B * zeta * w_0; 715 | CGFloat sin_max = sqrt(a * a / (a * a + b * b)); 716 | CGFloat theta_max; 717 | if (a * b > 0) { 718 | theta_max = asin(sin_max); 719 | } else { 720 | theta_max = M_PI - asin(sin_max); 721 | } 722 | t_max = theta_max / w_d; 723 | if (t_max > 0.f) { 724 | x_max = pow(M_E, -zeta * w_0 * t_max) * (A * cos(w_d * t_max) + B * sin(w_d * t_max)); 725 | // NSLog(@"v_0 = %f, x_0 = %f, t_max = %f, x_max = %f", velocity.x, x_0, t_max, x_max); 726 | if (x_max < 0.f) { 727 | // Part of next-next view will be shown temporarily. 728 | _nnextViewController = [self loadNNextPage]; 729 | } 730 | } 731 | } else { 732 | // NSLog(@"Critical damping"); 733 | A = x_0; 734 | B = velocity.x + w_0 * x_0; 735 | } 736 | 737 | // Current view. 738 | CAKeyframeAnimation *pageAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; 739 | pageAnimation.delegate = self; 740 | pageAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 741 | NSUInteger steps = 100; 742 | NSMutableArray *pageAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 743 | CGFloat value; 744 | for (NSUInteger step = 0; step < steps; ++step) { 745 | CGFloat t = 0.1f * step; 746 | if (underDamping) { 747 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newCenter.x; 748 | } else { 749 | value = pow(M_E, -w_0 * t) * (A + B * t) + newCenter.x; 750 | } 751 | [pageAnimationValues addObject:@(value)]; 752 | } 753 | pageAnimation.values = pageAnimationValues; 754 | [_contentController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 755 | _pagingAnimationCount++; 756 | [CATransaction setDisableActions:YES]; 757 | _contentController.view.layer.position = newCenter; 758 | [CATransaction setDisableActions:NO]; 759 | 760 | CAKeyframeAnimation *titleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; 761 | titleAnimation.delegate = self; 762 | titleAnimation.timingFunction = pageAnimation.timingFunction; 763 | NSMutableArray *titleAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 764 | for (NSUInteger step = 0; step < steps; ++step) { 765 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 766 | [titleAnimationValues addObject:@(value)]; 767 | } 768 | titleAnimation.values = titleAnimationValues; 769 | [_titleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 770 | _pagingAnimationCount++; 771 | [CATransaction setDisableActions:YES]; 772 | _titleView.layer.opacity = 0.f; 773 | [CATransaction setDisableActions:NO]; 774 | 775 | // Previous view. 776 | if (_previousViewController) { 777 | if (_previousViewController != _nnextViewController) { 778 | CGPoint newPreviousViewCenter = CGPointMake(newCenter.x - pageDistance, newCenter.y); 779 | [pageAnimationValues removeAllObjects]; 780 | for (NSUInteger step = 0; step < steps; ++step) { 781 | CGFloat t = 0.1f * step; 782 | if (underDamping) { 783 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newPreviousViewCenter.x; 784 | } else { 785 | value = pow(M_E, -w_0 * t) * (A + B * t) + newPreviousViewCenter.x; 786 | } 787 | [pageAnimationValues addObject:@(value)]; 788 | } 789 | pageAnimation.values = pageAnimationValues; 790 | [_previousViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 791 | _pagingAnimationCount++; 792 | [CATransaction setDisableActions:YES]; 793 | _previousViewController.view.layer.position = newPreviousViewCenter; 794 | [CATransaction setDisableActions:NO]; 795 | } 796 | 797 | if (_previousTitleView != _nnextTitleView) { 798 | [titleAnimationValues removeAllObjects]; 799 | for (NSUInteger step = 0; step < steps; ++step) { 800 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 801 | [titleAnimationValues addObject:@(value)]; 802 | } 803 | titleAnimation.values = titleAnimationValues; 804 | [_previousTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 805 | _pagingAnimationCount++; 806 | [CATransaction setDisableActions:YES]; 807 | _previousTitleView.layer.opacity = 0.f; 808 | [CATransaction setDisableActions:NO]; 809 | } 810 | } 811 | 812 | // Next view. 813 | [pageAnimationValues removeAllObjects]; 814 | for (NSUInteger step = 0; step < steps; ++step) { 815 | CGFloat t = 0.1f * step; 816 | if (underDamping) { 817 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newNextViewCenter.x; 818 | } else { 819 | value = pow(M_E, -w_0 * t) * (A + B * t) + newNextViewCenter.x; 820 | } 821 | [pageAnimationValues addObject:@(value)]; 822 | } 823 | pageAnimation.values = pageAnimationValues; 824 | [_nextViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 825 | _pagingAnimationCount++; 826 | [CATransaction setDisableActions:YES]; 827 | _nextViewController.view.layer.position = newNextViewCenter; 828 | [CATransaction setDisableActions:NO]; 829 | 830 | [titleAnimationValues removeAllObjects]; 831 | for (NSUInteger step = 0; step < steps; ++step) { 832 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 833 | [titleAnimationValues addObject:@(value)]; 834 | } 835 | titleAnimation.values = titleAnimationValues; 836 | [_nextTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 837 | _pagingAnimationCount++; 838 | [CATransaction setDisableActions:YES]; 839 | _nextTitleView.layer.opacity = 1.f; 840 | [CATransaction setDisableActions:NO]; 841 | 842 | if (_nnextViewController) { 843 | CGPoint newNNextViewCenter = CGPointMake(newNextViewCenter.x + pageDistance, newNextViewCenter.y); 844 | [pageAnimationValues removeAllObjects]; 845 | for (NSUInteger step = 0; step < steps; ++step) { 846 | CGFloat t = 0.1f * step; 847 | if (underDamping) { 848 | value = pow(M_E, -zeta * w_0 * t) * (A * cos(w_d * t) + B * sin(w_d * t)) + newNNextViewCenter.x; 849 | } else { 850 | value = pow(M_E, -w_0 * t) * (A + B * t) + newNNextViewCenter.x; 851 | } 852 | [pageAnimationValues addObject:@(value)]; 853 | } 854 | pageAnimation.values = pageAnimationValues; 855 | [_nnextViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 856 | _pagingAnimationCount++; 857 | [CATransaction setDisableActions:YES]; 858 | _nnextViewController.view.layer.position = newNNextViewCenter; 859 | [CATransaction setDisableActions:NO]; 860 | 861 | [titleAnimationValues removeAllObjects]; 862 | for (NSUInteger step = 0; step < steps; ++step) { 863 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 864 | [titleAnimationValues addObject:@(value)]; 865 | } 866 | titleAnimation.values = titleAnimationValues; 867 | [_nnextTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 868 | _pagingAnimationCount++; 869 | [CATransaction setDisableActions:YES]; 870 | _nnextTitleView.layer.opacity = 0.f; 871 | [CATransaction setDisableActions:NO]; 872 | } 873 | 874 | [CATransaction commit]; 875 | } else { 876 | // Bounce back to restore current page. 877 | [CATransaction begin]; 878 | [CATransaction setAnimationDuration:kPagingAnimationDuration]; 879 | 880 | if (t_max > 0.f) { 881 | if (x_max > 0.f) { 882 | if (_previousViewController == nil) { 883 | _previousViewController = [self loadPreviousPage]; 884 | } 885 | } else if (x_max < 0.f) { 886 | if (_nextViewController == nil) { 887 | _nextViewController = [self loadNextPage]; 888 | } 889 | } 890 | } 891 | 892 | // Current view. 893 | CAKeyframeAnimation *pageAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; 894 | pageAnimation.delegate = self; 895 | pageAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 896 | NSUInteger steps = 100; 897 | NSMutableArray *pageAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 898 | CGFloat value; 899 | for (NSUInteger step = 0; step < steps; ++step) { 900 | CGFloat t = 0.1f * step; 901 | value = pow(M_E, -w_0 * t) * (A + B * t) + boundsCenter.x; 902 | [pageAnimationValues addObject:@(value)]; 903 | } 904 | pageAnimation.values = pageAnimationValues; 905 | [_contentController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 906 | _pagingAnimationCount++; 907 | [CATransaction setDisableActions:YES]; 908 | _contentController.view.layer.position = boundsCenter; 909 | [CATransaction setDisableActions:NO]; 910 | 911 | CAKeyframeAnimation *titleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; 912 | titleAnimation.delegate = self; 913 | titleAnimation.timingFunction = pageAnimation.timingFunction; 914 | NSMutableArray *titleAnimationValues = [NSMutableArray arrayWithCapacity:steps]; 915 | for (NSUInteger step = 0; step < steps; ++step) { 916 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 917 | [titleAnimationValues addObject:@(value)]; 918 | } 919 | titleAnimation.values = titleAnimationValues; 920 | [_titleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 921 | _pagingAnimationCount++; 922 | [CATransaction setDisableActions:YES]; 923 | _titleView.layer.opacity = 1.f; 924 | [CATransaction setDisableActions:NO]; 925 | 926 | // Previous view. 927 | if (_previousViewController) { 928 | CGPoint previousViewCenter = CGPointMake(boundsCenter.x - pageDistance, boundsCenter.y); 929 | [pageAnimationValues removeAllObjects]; 930 | for (NSUInteger step = 0; step < steps; ++step) { 931 | CGFloat t = 0.1f * step; 932 | value = pow(M_E, -w_0 * t) * (A + B * t) + previousViewCenter.x; 933 | [pageAnimationValues addObject:@(value)]; 934 | } 935 | pageAnimation.values = pageAnimationValues; 936 | [_previousViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 937 | _pagingAnimationCount++; 938 | [CATransaction setDisableActions:YES]; 939 | _previousViewController.view.layer.position = previousViewCenter; 940 | [CATransaction setDisableActions:NO]; 941 | 942 | [titleAnimationValues removeAllObjects]; 943 | for (NSUInteger step = 0; step < steps; ++step) { 944 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 945 | [titleAnimationValues addObject:@(value)]; 946 | } 947 | titleAnimation.values = titleAnimationValues; 948 | [_previousTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 949 | _pagingAnimationCount++; 950 | [CATransaction setDisableActions:YES]; 951 | _previousTitleView.layer.opacity = 0.f; 952 | [CATransaction setDisableActions:NO]; 953 | } 954 | 955 | // Next view. 956 | if (_nextViewController) { 957 | CGPoint nextViewCenter = CGPointMake(boundsCenter.x + pageDistance, boundsCenter.y); 958 | [pageAnimationValues removeAllObjects]; 959 | for (NSUInteger step = 0; step < steps; ++step) { 960 | CGFloat t = 0.1f * step; 961 | value = pow(M_E, -w_0 * t) * (A + B * t) + nextViewCenter.x; 962 | [pageAnimationValues addObject:@(value)]; 963 | } 964 | pageAnimation.values = pageAnimationValues; 965 | [_nextViewController.view.layer addAnimation:pageAnimation forKey:pageAnimation.keyPath]; 966 | _pagingAnimationCount++; 967 | [CATransaction setDisableActions:YES]; 968 | _nextViewController.view.layer.position = nextViewCenter; 969 | [CATransaction setDisableActions:NO]; 970 | 971 | [titleAnimationValues removeAllObjects]; 972 | for (NSUInteger step = 0; step < steps; ++step) { 973 | value = 1.f - fabs([pageAnimationValues[step] floatValue] - boundsCenter.x) / pageDistance; 974 | [titleAnimationValues addObject:@(value)]; 975 | } 976 | titleAnimation.values = titleAnimationValues; 977 | [_nextTitleView.layer addAnimation:titleAnimation forKey:titleAnimation.keyPath]; 978 | _pagingAnimationCount++; 979 | [CATransaction setDisableActions:YES]; 980 | _nextTitleView.layer.opacity = 0.f; 981 | [CATransaction setDisableActions:NO]; 982 | } 983 | 984 | [CATransaction commit]; 985 | } 986 | } 987 | } 988 | 989 | - (void)turnPage:(UITapGestureRecognizer *)gestureRecognizer 990 | { 991 | if (_pagingAnimationCount > 0) return; 992 | 993 | if (gestureRecognizer.state != UIGestureRecognizerStateRecognized) return; 994 | 995 | if ([_delegate respondsToSelector:@selector(pageViewController:willBeginPagingViewController:)]) { 996 | [_delegate pageViewController:self willBeginPagingViewController:_contentController]; 997 | } 998 | 999 | UIView *view = gestureRecognizer.view; 1000 | CGPoint location = [gestureRecognizer locationInView:view]; 1001 | 1002 | static const CGFloat TAP_MARGIN = 40.0; 1003 | 1004 | // Tap on left margin, turn backward. 1005 | if (location.x < CGRectGetMinX(view.bounds) + TAP_MARGIN) { 1006 | [self turnBackward]; 1007 | } 1008 | // Tap on right margin, turn forward. 1009 | else if (location.x > CGRectGetMaxX(view.bounds) - TAP_MARGIN) { 1010 | [self turnForward]; 1011 | } 1012 | } 1013 | 1014 | - (void)turnForward { 1015 | _isTransitioningContentView = YES; 1016 | 1017 | if (_nextViewController == nil) { 1018 | _nextViewController = [self loadNextPage];; 1019 | } 1020 | 1021 | if (_nextViewController) { 1022 | CGPoint center = _contentController.view.center; 1023 | CGPoint nextViewCenter = _nextViewController.view.center; 1024 | CGPoint translation = CGPointMake(center.x - nextViewCenter.x, center.y - nextViewCenter.y); 1025 | CGPoint newCenter = CGPointMake(center.x + translation.x, center.y + translation.y); 1026 | [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; 1027 | [UIView animateWithDuration:kPagingAnimationDuration animations:^(void) { 1028 | _contentController.view.center = newCenter; 1029 | _nextViewController.view.center = center; 1030 | _titleView.alpha = 0; 1031 | _nextTitleView.alpha = 1; 1032 | } completion:^(BOOL finished) { 1033 | if (finished) { 1034 | [self pagingDidEnd]; 1035 | } 1036 | [[UIApplication sharedApplication] endIgnoringInteractionEvents]; 1037 | }]; 1038 | } 1039 | } 1040 | 1041 | - (void)turnBackward { 1042 | _isTransitioningContentView = YES; 1043 | 1044 | if (_previousViewController == nil) { 1045 | _previousViewController = [self loadPreviousPage]; 1046 | } 1047 | 1048 | if (_previousViewController) { 1049 | CGPoint center = _contentController.view.center; 1050 | CGPoint previousViewCenter = _previousViewController.view.center; 1051 | CGPoint translation = CGPointMake(center.x - previousViewCenter.x, center.y - previousViewCenter.y); 1052 | CGPoint newCenter = CGPointMake(center.x + translation.x, center.y + translation.y); 1053 | [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; 1054 | [UIView animateWithDuration:kPagingAnimationDuration animations:^(void) { 1055 | _contentController.view.center = newCenter; 1056 | _previousViewController.view.center = center; 1057 | _titleView.alpha = 0; 1058 | _previousTitleView.alpha = 1; 1059 | } completion:^(BOOL finished) { 1060 | if (finished) { 1061 | [self pagingDidEnd]; 1062 | } 1063 | [[UIApplication sharedApplication] endIgnoringInteractionEvents]; 1064 | }]; 1065 | } 1066 | } 1067 | 1068 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 1069 | if (!flag) { 1070 | _arePagingAnimationsCancelled = YES; 1071 | } 1072 | if (--_pagingAnimationCount == 0) { 1073 | [self pagingDidEnd]; 1074 | } 1075 | } 1076 | 1077 | - (void)pagingDidEnd 1078 | { 1079 | UIViewController *oldContentController = _contentController; 1080 | CGPoint center = _contentController.view.center; 1081 | CGRect bounds = self.view.bounds; 1082 | if (center.x > CGRectGetMaxX(bounds) && _previousViewController != nil) { 1083 | // Land in the previous page. 1084 | _nextViewController = _contentController; 1085 | self.contentController = _previousViewController; 1086 | _previousViewController = _ppreviousViewController; 1087 | _ppreviousViewController = nil; 1088 | 1089 | _nextTitleView = _titleView; 1090 | _titleView = _previousTitleView; 1091 | _previousTitleView = _ppreviousTitleView; 1092 | _ppreviousTitleView = nil; 1093 | self.navigationItem.titleView.bounds = _titleView.bounds; 1094 | _titleView.frame = self.navigationItem.titleView.bounds; 1095 | } else if (center.x < CGRectGetMinX(bounds) && _nextViewController != nil) { 1096 | // Land in the next page. 1097 | _previousViewController = _contentController; 1098 | self.contentController = _nextViewController; 1099 | _nextViewController = _nnextViewController; 1100 | _nnextViewController = nil; 1101 | 1102 | _previousTitleView = _titleView; 1103 | _titleView = _nextTitleView; 1104 | _nextTitleView = _nnextTitleView; 1105 | _nnextTitleView = nil; 1106 | self.navigationItem.titleView.bounds = _titleView.bounds; 1107 | _titleView.frame = self.navigationItem.titleView.bounds; 1108 | } else { 1109 | // Land in the current page. 1110 | [self unloadInvisiblePages]; 1111 | } 1112 | 1113 | _isTransitioningContentView = NO; 1114 | _arePagingAnimationsCancelled = NO; 1115 | 1116 | if ([_delegate respondsToSelector:@selector(pageViewController:didEndPagingViewController:)]) { 1117 | [_delegate pageViewController:self didEndPagingViewController:oldContentController]; 1118 | } 1119 | } 1120 | 1121 | @end 1122 | -------------------------------------------------------------------------------- /WLPageViewController/damping.m: -------------------------------------------------------------------------------- 1 | ## k = 80 2 | ## m = 125 3 | ## c = 200 4 | 5 | t = 0:0.1:10; 6 | 7 | ## w_0 = sqrt(k / m) 8 | ## zeta = c / (2 * sqrt(m * k)) 9 | ## zeta_w_0 = c / (2 * m) 10 | w_0 = .7 11 | zeta = 1 12 | 13 | if abs(zeta - 1) < 0.001 14 | # critical damping 15 | printf('Critical damping\n'); 16 | A = x_0 17 | B = v_0 + w_0 * x_0 18 | x = (A + B * t) .* power(e, -w_0 * t); 19 | t_max = 1 / w_0 - A / B 20 | x_max = (v_0 + w_0 * x_0) / w_0 * e^(-v_0 / (v_0 + w_0 * x_0)) 21 | elseif zeta < 1 22 | # under-damping 23 | printf('Under-damping\n'); 24 | w_d = w_0 * sqrt(1 - zeta * zeta) 25 | A = x_0 26 | B = (zeta * x_0 + v_0) / w_d 27 | x = power(e, -zeta * w_0 * t) .* (A * cos(w_d * t) + B * sin(w_d * t)); 28 | a = B * w_d - A * zeta * w_0 29 | b = A * w_d + B * zeta * w_0 30 | sin_max = sqrt(a^2 / (a^2 + b^2)) 31 | if a * b > 0 32 | theta_max = asin(sin_max) 33 | theta_max = [theta_max, theta_max + pi, theta_max + 2 * pi] 34 | else 35 | theta_max = pi - asin(sin_max) 36 | theta_max = [theta_max, theta_max + pi] 37 | end 38 | t_max = theta_max / w_d 39 | x_max = power(e, -zeta * w_0 * t_max) .* (A * cos(w_d * t_max) + B * sin(w_d * t_max)) 40 | else 41 | # over-damping 42 | printf('Over-damping\n'); 43 | g_b = 2 * zeta * w_0; 44 | g_c = w_0 * w_0; 45 | g_delta = sqrt(g_b * g_b - 4 * g_a * g_c); 46 | gamma_1 = (-g_b + g_delta) / 2; 47 | gamma_2 = (-g_b - g_delta) / 2; 48 | A = x_0 + (gamma_1 * x_0 - v_0) / (gamma_2 - gamma_1); 49 | B = -(gamma_1 * x_0 - v_0) / (gamma_2 - gamma_1); 50 | x = A * power(e, gamma_1 * t) + B * power(e, gamma_2 * t); 51 | end 52 | 53 | plot(t, x) 54 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/an0/WLPageViewController/a689b7b7bde78ccc578092dc3587aa69379e15a2/demo.gif --------------------------------------------------------------------------------