diff --git a/LinkedStoryboards/Podfile b/LinkedStoryboards/Podfile index 4531ee6..38cd48c 100644 --- a/LinkedStoryboards/Podfile +++ b/LinkedStoryboards/Podfile @@ -7,5 +7,5 @@ pod 'RBStoryboardLink', :path => '../RBStoryboardLink.podspec' # It is important that we don't include KIF in the main app. # Some of the categories cause the UI to appear unresponsive. target 'UI Tests', :exclusive => true do - pod 'KIF', '~> 2.0' + pod 'KIF', '~> 2.0', :inhibit_warnings => true end diff --git a/LinkedStoryboards/Podfile.lock b/LinkedStoryboards/Podfile.lock index dfa75e9..5b3c4ae 100644 --- a/LinkedStoryboards/Podfile.lock +++ b/LinkedStoryboards/Podfile.lock @@ -11,7 +11,7 @@ EXTERNAL SOURCES: :path: ../RBStoryboardLink.podspec SPEC CHECKSUMS: - KIF: ff3b46deb62ce3f8e99c6a2c786729e1eea241ba + KIF: c01fb8fb593cf8ffeeb69ed8d402ce88143e0697 RBStoryboardLink: 1bcff9be9fa9de9d66ea10518a327be1af729d03 -COCOAPODS: 0.29.0 +COCOAPODS: 0.31.1 diff --git a/LinkedTabs/Podfile b/LinkedTabs/Podfile index 4531ee6..38cd48c 100644 --- a/LinkedTabs/Podfile +++ b/LinkedTabs/Podfile @@ -7,5 +7,5 @@ pod 'RBStoryboardLink', :path => '../RBStoryboardLink.podspec' # It is important that we don't include KIF in the main app. # Some of the categories cause the UI to appear unresponsive. target 'UI Tests', :exclusive => true do - pod 'KIF', '~> 2.0' + pod 'KIF', '~> 2.0', :inhibit_warnings => true end diff --git a/LinkedTabs/Podfile.lock b/LinkedTabs/Podfile.lock index dfa75e9..5b3c4ae 100644 --- a/LinkedTabs/Podfile.lock +++ b/LinkedTabs/Podfile.lock @@ -11,7 +11,7 @@ EXTERNAL SOURCES: :path: ../RBStoryboardLink.podspec SPEC CHECKSUMS: - KIF: ff3b46deb62ce3f8e99c6a2c786729e1eea241ba + KIF: c01fb8fb593cf8ffeeb69ed8d402ce88143e0697 RBStoryboardLink: 1bcff9be9fa9de9d66ea10518a327be1af729d03 -COCOAPODS: 0.29.0 +COCOAPODS: 0.31.1 diff --git a/RBStoryboardLink.h b/RBStoryboardLink.h index 2f16fdf..d2a9196 100644 --- a/RBStoryboardLink.h +++ b/RBStoryboardLink.h @@ -42,4 +42,14 @@ /// This should be set in the Interface Builder identity inspector. @property (nonatomic, copy) NSString * sceneIdentifier; +/// (Optional) Whether the first view controller should have a constraint for +/// the top layout guide in the storyboard. This should be set in the Interface +/// Builder identity inspector. +@property (nonatomic, assign) BOOL needsTopLayoutGuide; + +/// (Optional) Whether the first view controller should have a constraint for +/// the bottom layout guide in the storyboard. This should be set in the +/// Interface Builder identity inspector. +@property (nonatomic, assign) BOOL needsBottomLayoutGuide; + @end diff --git a/RBStoryboardLink.m b/RBStoryboardLink.m index 4940e83..3d1cd23 100644 --- a/RBStoryboardLink.m +++ b/RBStoryboardLink.m @@ -35,27 +35,69 @@ @interface RBStoryboardLink () @implementation RBStoryboardLink +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + + if (self) + { + [self loadDefaults]; + } + + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + + if (self) + { + [self loadDefaults]; + } + + return self; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + [self loadDefaults]; + } + + return self; +} + +- (void)loadDefaults +{ + _needsTopLayoutGuide = YES; + _needsBottomLayoutGuide = YES; +} + - (void)awakeFromNib { [super awakeFromNib]; - + NSAssert([self.storyboardName length], @"No storyboard name"); - + UIStoryboard * storyboard = [UIStoryboard storyboardWithName:self.storyboardName bundle:nil]; UIViewController * scene = nil; - + // Creates the linked scene. if ([self.sceneIdentifier length] == 0) scene = [storyboard instantiateInitialViewController]; else scene = [storyboard instantiateViewControllerWithIdentifier:self.sceneIdentifier]; - + NSAssert(scene, @"No scene found in storyboard: \"%@\" with optional identifier: \"%@\"", self.storyboardName, self.sceneIdentifier); - + self.scene = scene; - + // Grabs the UINavigationItem stuff. UINavigationItem * navItem = self.navigationItem; UINavigationItem * linkedNavItem = scene.navigationItem; @@ -69,16 +111,16 @@ - (void)awakeFromNib { navItem.leftBarButtonItem = linkedNavItem.leftBarButtonItem; navItem.leftBarButtonItems = linkedNavItem.leftBarButtonItems; navItem.leftItemsSupplementBackButton = linkedNavItem.leftItemsSupplementBackButton; - + // Grabs the UITabBarItem // The link overrides the contained view's tab bar item. if (self.tabBarController) scene.tabBarItem = self.tabBarItem; - + // Grabs the edit button. UIBarButtonItem * editButton = self.editButtonItem; UIBarButtonItem * linkedEditButton = scene.editButtonItem; - + if (linkedEditButton) { editButton.enabled = linkedEditButton.enabled; editButton.image = linkedEditButton.image; @@ -95,22 +137,22 @@ - (void)awakeFromNib { editButton.customView = linkedEditButton.customView; editButton.tintColor = linkedEditButton.tintColor; } - + // Grabs the modal properties. self.modalTransitionStyle = scene.modalTransitionStyle; self.modalPresentationStyle = scene.modalPresentationStyle; self.definesPresentationContext = scene.definesPresentationContext; self.providesPresentationContextTransitionStyle = scene.providesPresentationContextTransitionStyle; - + // Grabs the popover properties. self.preferredContentSize = scene.preferredContentSize; self.modalInPopover = scene.modalInPopover; - + // Grabs miscellaneous properties. self.title = scene.title; self.hidesBottomBarWhenPushed = scene.hidesBottomBarWhenPushed; self.editing = scene.editing; - + // Translucent bar properties. self.automaticallyAdjustsScrollViewInsets = scene.automaticallyAdjustsScrollViewInsets; self.edgesForExtendedLayout = scene.edgesForExtendedLayout; @@ -120,21 +162,22 @@ - (void)awakeFromNib { } - (NSString *)vertialConstraintString { - + // Defaults to using top and bottom layout guides. - BOOL needsTopLayoutGuide = YES; - BOOL needsBottomLayoutGuide = YES; - + BOOL needsTopLayoutGuide = self.needsTopLayoutGuide; + BOOL needsBottomLayoutGuide = self.needsBottomLayoutGuide; + + // Prefers to use the layout guide options on the scene. if ([self.scene conformsToProtocol:@protocol(RBStoryboardLinkSource)]) { id source = (id)self.scene; - + if ([source respondsToSelector:@selector(needsTopLayoutGuide)]) needsTopLayoutGuide = [source needsTopLayoutGuide]; - + if ([source respondsToSelector:@selector(needsBottomLayoutGuide)]) needsBottomLayoutGuide = [source needsBottomLayoutGuide]; } - + if (needsTopLayoutGuide && needsBottomLayoutGuide) return @"V:[topGuide][view][bottomGuide]"; else if (needsTopLayoutGuide) @@ -147,21 +190,21 @@ - (NSString *)vertialConstraintString { - (void)viewDidLoad { [super viewDidLoad]; - + // Adds the view controller as a child view. UIViewController * scene = self.scene; [self addChildViewController:scene]; [self.view addSubview:scene.view]; [self.scene didMoveToParentViewController:self]; - + scene.view.translatesAutoresizingMaskIntoConstraints = NO; - + NSDictionary * views = @{ @"topGuide" : self.topLayoutGuide, @"bottomGuide" : self.bottomLayoutGuide, @"view" : scene.view, }; - + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil @@ -174,46 +217,46 @@ - (void)viewDidLoad { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - + if ([self.scene isKindOfClass:[UINavigationController class]] || [self.scene isKindOfClass:[UITabBarController class]]) [self.scene viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - + if ([self.scene isKindOfClass:[UINavigationController class]] || [self.scene isKindOfClass:[UITabBarController class]]) [self.scene viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - + if ([self.scene isKindOfClass:[UINavigationController class]] || [self.scene isKindOfClass:[UITabBarController class]]) [self.scene viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - + if ([self.scene isKindOfClass:[UINavigationController class]] || [self.scene isKindOfClass:[UITabBarController class]]) [self.scene viewDidDisappear:animated]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { - + // The linked scene defines the rotation. return [self.scene shouldAutorotateToInterfaceOrientation:toInterfaceOrientation]; } - (BOOL)shouldAutorotate { - + // The linked scene defines autorotate. return [self.scene shouldAutorotate]; } - (NSUInteger)supportedInterfaceOrientations { - + // The linked scene defines supported orientations. return [self.scene supportedInterfaceOrientations]; } @@ -251,7 +294,7 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { } - (void)forwardInvocation:(NSInvocation *)anInvocation { - + if ([self.scene respondsToSelector:[anInvocation selector]]) [anInvocation invokeWithTarget:self.scene]; else diff --git a/README.md b/README.md index 1cfeac5..ac33195 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,28 @@ ## Summary -`UIStoryboards` are very powerful and useful. However, to make the best use of storyboards, they need to be broken down into natural modules. The problem with having many storyboards is writing the code to transition between them. `RBStoryboardLink` solves this problem by allowing "pseudo-segues" between `UIStoryboards`. These segues can be built without leaving Interface Builder and without writing any extra code. +`UIStoryboards` are very powerful and useful. However, to make the best use of storyboards, they need to be broken down into natural modules. The problem with having many storyboards is writing the code to transition between them. `RBStoryboardLink` solves this problem by allowing "pseudo-segues" between `UIStoryboards`. These segues can be built without leaving Interface Builder and without writing any extra code. ## Dependencies -`RBStoryboardLink` requires iOS 7.0+. With some modifications it can support iOS 6 as well, but I wanted to keep it simple. `RBStoryboardLink` cannot simultaneously support iOS 5 and 7 due to the large functionality differences. +`RBStoryboardLink` requires iOS 7.0+. With some modifications it can support iOS 6 as well, but I wanted to keep it simple. `RBStoryboardLink` cannot simultaneously support iOS 5 and 7 due to the large functionality differences. ## How to use -1. Your app's storyboards must first be decomposed into their natural modules. See this [guide][1] for some tips. +1. Your app's storyboards must first be decomposed into their natural modules. See this [guide][1] for some tips. -2. Where ever you want create a transition into a different storyboard, create a `UIViewController` representing the scene to be pushed. +2. Where ever you want create a transition into a different storyboard, create a `UIViewController` representing the scene to be pushed. -3. Create the desired segue type (Push, Modal, Custom) to these surrogate view controllers. +3. Create the desired segue type (Push, Modal, Custom) to these surrogate view controllers. 4. In the Identity Inspector, change the class type of each surrogate view controller to `RBStoryboardLink`. -5. While still in the Identity Inspector, add one or two User Defined Runtime Attributes. +5. While still in the Identity Inspector, add one or more User Defined Runtime Attributes. - 1. storyboardName (Required) The name of the storyboard to transition into. - - 2. sceneIdentifier (Optional) The identifier of the view controller to transition to. If left blank, this will push the first view controller. + 1. storyboardName (Required) The name of the storyboard to transition into. + 2. sceneIdentifier (Optional) The identifier of the view controller to transition to. If left blank, this will push the first view controller. + 3. needsTopLayoutGuide (Optional) Whether a custom layout constraint should be added to the top layout guide in storyboards. If you notice the background of your navigation bar not getting behind the status bar, set this to `NO`. + 4. needsBottomLayoutGuide (Optional) Same as the one on top, but for the bottom guide. ## Implementation notes @@ -32,13 +33,13 @@ There are two demos that are provided to show how to use `RBStoryboardLink`: -* A standard, straightforward workflow. +* A standard, straightforward workflow. -* A tabbed workflow. +* A tabbed workflow. ## Contribution -Contributions are welcomed. I'm much more responsive to pull requests rather than issues. The sample apps use [KIF][2] for automated testing. Any pull requests must pass the tests before they will be merged. If new functionality is introduced, the pull requests must also add tests for the new behavior. +Contributions are welcomed. I'm much more responsive to pull requests rather than issues. The sample apps use [KIF][2] for automated testing. Any pull requests must pass the tests before they will be merged. If new functionality is introduced, the pull requests must also add tests for the new behavior. ## License