├── BufferedNavigationController.h ├── BufferedNavigationController.m └── README /BufferedNavigationController.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Andrew Armstrong 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining 4 | * a copy of this software and associated documentation files (the 5 | * "Software"), to deal in the Software without restriction, including 6 | * without limitation the rights to use, copy, modify, merge, publish, 7 | * distribute, sublicense, and/or sell copies of the Software, and to 8 | * permit persons to whom the Software is furnished to do so, subject to 9 | * the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be 12 | * included in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #import 24 | #import 25 | 26 | @interface BufferedNavigationController : UINavigationController 27 | 28 | - (void) pushCodeBlock:(void (^)())codeBlock; 29 | - (void) runNextBlock; 30 | 31 | @property (nonatomic, retain) NSMutableArray* stack; 32 | @property (nonatomic, assign) bool transitioning; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /BufferedNavigationController.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Andrew Armstrong 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining 4 | * a copy of this software and associated documentation files (the 5 | * "Software"), to deal in the Software without restriction, including 6 | * without limitation the rights to use, copy, modify, merge, publish, 7 | * distribute, sublicense, and/or sell copies of the Software, and to 8 | * permit persons to whom the Software is furnished to do so, subject to 9 | * the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be 12 | * included in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #import "BufferedNavigationController.h" 24 | 25 | @implementation BufferedNavigationController 26 | @synthesize transitioning = _transitioning; 27 | @synthesize stack = _stack; 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | self.delegate = self; 33 | self.stack = [[NSMutableArray alloc] init]; 34 | } 35 | 36 | - (UIViewController *)popViewControllerAnimated:(BOOL)animated { 37 | @synchronized(self.stack) { 38 | if (self.transitioning) { 39 | void (^codeBlock)(void) = [^{ 40 | [super popViewControllerAnimated:animated]; 41 | } copy]; 42 | [self.stack addObject:codeBlock]; 43 | 44 | // We cannot show what viewcontroller is currently animated now 45 | return nil; 46 | } else { 47 | return [super popViewControllerAnimated:animated]; 48 | } 49 | } 50 | } 51 | 52 | - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated { 53 | @synchronized(self.stack) { 54 | if (self.transitioning) { 55 | void (^codeBlock)(void) = [^{ 56 | [super popToRootViewControllerAnimated:animated]; 57 | } copy]; 58 | [self.stack addObject:codeBlock]; 59 | 60 | // We cannot show what viewcontroller is currently animated now 61 | return nil; 62 | } else { 63 | return [super popToRootViewControllerAnimated:animated]; 64 | } 65 | } 66 | } 67 | 68 | - (NSArray*) popToViewController:(UIViewController *)viewController animated:(BOOL)animated { 69 | @synchronized(self.stack) { 70 | if (self.transitioning) { 71 | void (^codeBlock)(void) = [^{ 72 | [super popToViewController:viewController animated:animated]; 73 | } copy]; 74 | [self.stack addObject:codeBlock]; 75 | 76 | // We cannot show what viewcontroller is currently animated now 77 | return nil; 78 | } else { 79 | return [super popToViewController:viewController animated:animated]; 80 | } 81 | } 82 | } 83 | 84 | - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated { 85 | @synchronized(self.stack) { 86 | if (self.transitioning) { 87 | // Copy block so its no longer on the (real software) stack 88 | void (^codeBlock)(void) = [^{ 89 | [super setViewControllers:viewControllers animated:animated]; 90 | } copy]; 91 | 92 | // Add to the stack list and then release 93 | [self.stack addObject:codeBlock]; 94 | } else { 95 | [super setViewControllers:viewControllers animated:animated]; 96 | } 97 | } 98 | } 99 | 100 | - (void) pushCodeBlock:(void (^)())codeBlock{ 101 | @synchronized(self.stack) { 102 | [self.stack addObject:[codeBlock copy]]; 103 | 104 | if (!self.transitioning) 105 | [self runNextBlock]; 106 | } 107 | } 108 | 109 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { 110 | @synchronized(self.stack) { 111 | if (self.transitioning) { 112 | void (^codeBlock)(void) = [^{ 113 | [super pushViewController:viewController animated:animated]; 114 | } copy]; 115 | [self.stack addObject:codeBlock]; 116 | } else { 117 | [super pushViewController:viewController animated:animated]; 118 | } 119 | } 120 | } 121 | 122 | - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 123 | 124 | @synchronized (self.stack) { 125 | self.transitioning = true; 126 | 127 | if ([navigationController.topViewController respondsToSelector:@selector(transitionCoordinator)]) { 128 | id transitionCoordinator = navigationController.topViewController.transitionCoordinator; 129 | [transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id context) { 130 | if ([context isCancelled]) { 131 | @synchronized (self.stack) { 132 | self.transitioning = false; 133 | } 134 | } 135 | }]; 136 | } 137 | } 138 | } 139 | 140 | - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 141 | 142 | @synchronized(self.stack) { 143 | self.transitioning = false; 144 | 145 | [self runNextBlock]; 146 | } 147 | } 148 | 149 | - (void) runNextBlock { 150 | if (self.stack.count == 0) 151 | return; 152 | 153 | void (^codeBlock)(void) = [self.stack objectAtIndex:0]; 154 | 155 | // Execute block, then remove it from the stack (which will dealloc) 156 | codeBlock(); 157 | 158 | [self.stack removeObjectAtIndex:0]; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | BufferedNavigationController extends UINavigationController to automatically queue up transitions between view controllers. 2 | 3 | This prevents you receiving errors such as: 4 | "Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted." 5 | 6 | This can happen if you try to pushViewController during an existing transition. 7 | 8 | To use, simply add the provided files to your project and change your UINavigationController class to inherit from BufferedNavigationController in Interface Builder. 9 | 10 | - Andrew Armstrong (phplasma@gmail.com) 11 | - http://cherrypopapp.com 12 | --------------------------------------------------------------------------------