Skip to content

Commit

Permalink
Top and bottom layout guide options can now be set in IB.
Browse files Browse the repository at this point in the history
  • Loading branch information
rob-brown committed Apr 12, 2014
1 parent 3d55541 commit 8053eef
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 50 deletions.
2 changes: 1 addition & 1 deletion LinkedStoryboards/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions LinkedStoryboards/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion LinkedTabs/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions LinkedTabs/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions RBStoryboardLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
107 changes: 75 additions & 32 deletions RBStoryboardLink.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<RBStoryboardLinkSource> source = (id<RBStoryboardLinkSource>)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)
Expand All @@ -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
Expand All @@ -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];
}
Expand Down Expand Up @@ -251,7 +294,7 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {

if ([self.scene respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:self.scene];
else
Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down

0 comments on commit 8053eef

Please sign in to comment.