Skip to content

Instantly share code, notes, and snippets.

@jamesmoschou
Last active December 30, 2015 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesmoschou/7783126 to your computer and use it in GitHub Desktop.
Save jamesmoschou/7783126 to your computer and use it in GitHub Desktop.
StoryboardController: A storyboard controller manages a set of view controllers described in a storyboard file that make up a portion of your app’s user interface. A storyboard controller is to a storyboard what a view controller is to a view. Whereas a view controller would manage a view heirachy that allows a user to perform a single task, a s…
//
// StoryboardController.h
// Ideas
//
// Created by James Moschou on 12/3/13.
// Copyright (c) 2013 James Moschou. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol StoryboardControllerDelegate;
/**
A storyboard controller manages a set of view controllers described in a
storyboard file that make up a portion of your app’s user interface.
A storyboard controller is to a storyboard what a view controller is to a view.
Whereas a view controller would manage a view heirachy that allows a user to
perform a single task, a storyboard controller would manage a set of view
controllers that make up a single flow. For example, in the creation of a
complex model object, the app may guide the user through a series of optional
stages, each associated with a different view controller; a storyboard
controller encapsulates this flow and can provide a common interface through
which to be notified the flow has finished.
*/
@interface StoryboardController : NSObject
- (instancetype)initWithStoryboardName:(NSString *)storyboardNameOrNil bundle:(NSBundle *)storyboardBundleOrNil;
@property (nonatomic, readonly, copy) NSString *storyboardName;
@property (nonatomic, readonly, strong) NSBundle *storyboardBundle;
- (void)storyboardDidLoad;
@property (nonatomic, readonly, strong) UIStoryboard *storyboard;
@property (nonatomic, weak) id <StoryboardControllerDelegate> delegate;
/** Displays the storyboard.
To use this method, you must first provide a delegate object that implements
the storyboardControllerViewControllerForPresentation: method. The view
controller returned by that method is used to present the storyboard modally.
@param animated Specify YES to animate the appearance of the storyboard or NO
to display it immediately.
*/
- (void)presentStoryboardAnimated:(BOOL)animated;
/** Displays the storyboard and anchors it to the specified location in the
view.
@param rect The rectangle in view at which to anchor the storyboard.
@param view The view containing the anchor rectangle for the
storyboard.
@param arrowDirections The arrow directions the storyboard is permitted to use.
@param animated Specify YES to animate the presentation of the
storyboard or NO to display it immediately.
*/
- (void)presentStoryboardFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated;
/** Displays the storyboard and anchors it to the specified bar button item.
When presenting the storybaord, this method adds the toolbar that owns the
button to the storyboard's list of passthrough views. Thus, taps in the toolbar
result in the action methods of the corresponding toolbar items being called.
If you want the storyboard to be dismissed when a different toolbar item is
tapped, you must implement that behavior in your action handler methods.
@param item The bar button item on which to anchor the storyboard.
@param arrowDirections The arrow directions the storyboard is permitted to use.
@param animated Specify YES to animate the presentation of the
storyboard or NO to display it immediately.
*/
- (void)presentStoryboardFromBarButtonItem:(UIBarButtonItem *)item permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated;
/** Dismisses the storyboard programmatically.
You can use this method to dismiss the storyboard programmatically in response
to taps inside the storyboard window. Taps outside of the storyboard's contents
automatically dismiss the storyboard.
@param animated Specify YES to animate the dismissal of the storyboard or NO to
dismiss it immediately.
*/
- (void)dismissStoryboardAnimated:(BOOL)animated;
@end
@protocol StoryboardControllerDelegate <NSObject>
/** Called when a storyboard controller needs a view controller for presenting a
storyboard.
This method is required if your application attempts to display a storyboard.
The view controller returned by this method is used as the parent for the
storyboard.
If you return a navigation controller from this method, the inital view
controller of the storyboard controller is pushed onto the navigation stack
using the standard navigation controller animations. If you return any other
type of view controller, the storyboard controller is displayed modally, in
which case, the view controller you return must be capable of presenting a
modal view controller.
@param storyboardController The storyboard controller requesting the parent
view controller.
@return The view controller to user when presenting the storyboard. The return
value must not be nil.
*/
- (UIViewController *)storyboardControllerViewControllerForPresentation:(StoryboardController *)storyboardController;
/** Called when a storyboard controller needs to instantiate a view controller
from its storyboard for the first time.
If this method is not implemented or returns nil, the storyboard view
controller will instantiate the view controller using
instantiateInitialViewController.
@param storyboardController The storyboard controller requesting the identifier
for the view controller to instantiate.
@return A string specifying the identifer for the view controller to
instantiate from the storyboard first.
*/
- (NSString *)storyboardControllerIdentifierForInitialViewController:(StoryboardController *)storyboardController;
@end
@interface UIViewController (StoryboardController)
@property (nonatomic, readonly, strong) StoryboardController *presentedStoryboardController;
- (void)presentStoryboardController:(StoryboardController *)storyboardControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;
- (void)dismissStoryboardControllerAnimated:(BOOL)flag completion:(void (^)(void))completion;
@end
@interface UIStoryboard (StoryboardController)
@property (nonatomic, weak) StoryboardController *controller;
@end
//
// StoryboardController.m
// Ideas
//
// Created by James Moschou on 12/3/13.
// Copyright (c) 2013 James Moschou. All rights reserved.
//
#import <objc/runtime.h>
#import "StoryboardController.h"
@interface StoryboardController ()
@property (nonatomic, readwrite, weak) UIViewController *presentingViewController;
@end
@implementation StoryboardController
- (instancetype)initWithStoryboardName:(NSString *)storyboardNameOrNil bundle:(NSBundle *)storyboardBundleOrNil
{
self = [super init];
if (self) {
_storyboardName = [storyboardNameOrNil copy];
_storyboardBundle = storyboardBundleOrNil;
}
return self;
}
- (UIStoryboard *)storyboard
{
if (!_storyboard) {
[self loadStoryboard];
[self storyboardDidLoad];
NSAssert(_storyboard, @"self.storyboard must not be nil after calling -loadStoryboard.");
}
return _storyboard;
}
- (void)loadStoryboard
{
NSString *name = self.storyboardName ?: NSStringFromClass([self class]);
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:name bundle:self.storyboardBundle];
if (!storyboard && [name hasSuffix:@"Controller"]) {
storyboard = [UIStoryboard storyboardWithName:[name substringToIndex:name.length - @"Controller".length] bundle:self.storyboardBundle];
}
storyboard.controller = self;
self.storyboard = storyboard;
}
- (void)storyboardDidLoad
{
}
@end
@interface UIViewController (StoryboardControllerPrivate)
@property (nonatomic, readwrite, strong) StoryboardController *presentedStoryboardController;
@end
@implementation UIViewController (StoryboardController)
- (void)presentStoryboardController:(StoryboardController *)storyboardControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
NSAssert(!self.presentedViewController, @"Cannot present a storyboard controller while a view controller is already presented.");
NSString *initialViewControllerIdentifier;
UIViewController *initialViewController;
if ([storyboardControllerToPresent.delegate respondsToSelector:@selector(storyboardControllerIdentifierForInitialViewController:)]) {
initialViewControllerIdentifier = [storyboardControllerToPresent.delegate storyboardControllerIdentifierForInitialViewController:storyboardControllerToPresent];
}
if (initialViewControllerIdentifier) {
initialViewController = [storyboardControllerToPresent.storyboard instantiateViewControllerWithIdentifier:initialViewControllerIdentifier];
} else {
initialViewController = [storyboardControllerToPresent.storyboard instantiateInitialViewController];
}
[self presentViewController:initialViewController animated:flag completion:^{
self.presentedStoryboardController = storyboardControllerToPresent;
storyboardControllerToPresent.presentingViewController = self;
if (completion) completion();
}];
}
- (void)dismissStoryboardControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
[self dismissViewControllerAnimated:flag completion:completion];
}
@end
@implementation UIStoryboard (StoryboardController)
static void *StoryboardControllerKey = &StoryboardControllerKey;
- (StoryboardController *)controller
{
return objc_getAssociatedObject(self, StoryboardControllerKey);
}
- (void)setController:(StoryboardController *)storyboardController
{
objc_setAssociatedObject(self, StoryboardControllerKey, storyboardController, OBJC_ASSOCIATION_ASSIGN);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment