With the UIView animation API and view controller containment the current Cocoa Touch stack is very well suited for automatic transitions between view controllers.<
I don't know of any libraries to do this, but I've abstracted out the transition code with either a category on UIViewController, or by making a base class for my view controllers that has the transition code in it. I keep all the messy transition code in the base class, and in my controller, I just need to add a gesture recognizer and call the base class method from its action method.
-(IBAction)dragInController:(UIPanGestureRecognizer *)sender {
[self dragController:[self.storyboard instantiateViewControllerWithIdentifier:@"GenericVC"] sender:sender];
}
After Edit:
Here is one of my attempts. This is the code in the DragIntoBaseVC which is the controller that another controller needs to inherit from to have a view dragged into it using the above code. This only handles the drag in (from the right only), not the drag out (still working on that one, and how to make this one more generic with respect to direction). A lot of this code is in there to handle rotations. It works in any orientation (except upside down), and works on both iPhone and iPad. I'm doing the animations by animating the layout constraints rather than setting frames, since this seems to be the way Apple is heading (I suspect they'll depreciate the old struts and springs system in the future).
#import "DragIntoBaseVC.h"
@interface DragIntoBaseVC ()
@property (strong,nonatomic) NSLayoutConstraint *leftCon;
@property (strong,nonatomic) UIViewController *incomingVC;
@property (nonatomic) NSInteger w;
@end
@implementation DragIntoBaseVC
static int first = 1;
-(void)dragController:(UIViewController *) incomingVC sender:(UIPanGestureRecognizer *) sender {
if (first) {
self.incomingVC = incomingVC;
UIView *inView = incomingVC.view;
[inView setTranslatesAutoresizingMaskIntoConstraints:NO];
inView.transform = self.view.transform;
[self.view.window addSubview:inView];
self.w = self.view.bounds.size.width;
NSLayoutConstraint *con2;
switch ([UIDevice currentDevice].orientation) {
case 0:
case 1:
self.leftCon = [NSLayoutConstraint constraintWithItem:inView attribute:NSLayoutAttributeLeft relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.w];
con2 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:inView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
break;
case 3:
self.leftCon = [NSLayoutConstraint constraintWithItem:inView attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:self.w];
con2 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeft relatedBy:0 toItem:inView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
break;
case 4:
self.leftCon = [NSLayoutConstraint constraintWithItem:inView attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:-self.w];
con2 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:0 toItem:inView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
break;
default:
break;
}
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeWidth relatedBy:0 toItem:inView attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:0 toItem:inView attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
NSArray *constraints = @[self.leftCon,con2,con3,con4];
[self.view.window addConstraints:constraints];
first = 0;
}
CGPoint translate = [sender translationInView:self.view];
if ([UIDevice currentDevice].orientation == 0 || [UIDevice currentDevice].orientation == 1 || [UIDevice currentDevice].orientation == 3) { // for portrait or landscapeRight
if (sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged) {
self.leftCon.constant += translate.x;
[sender setTranslation:CGPointZero inView:self.view];
}else if (sender.state == UIGestureRecognizerStateEnded){
if (self.leftCon.constant < self.w/2) {
[self.view removeGestureRecognizer:sender];
[self finishTransition];
}else{
[self abortTransition:1];
}
}
}else{ // for landscapeLeft
if (sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged) {
self.leftCon.constant -= translate.x;
[sender setTranslation:CGPointZero inView:self.view];
}else if (sender.state == UIGestureRecognizerStateEnded){
if (-self.leftCon.constant < self.w/2) {
[self.view removeGestureRecognizer:sender];
[self finishTransition];
}else{
[self abortTransition:-1];
}
}
}
}
-(void)finishTransition {
self.leftCon.constant = 0;
[UIView animateWithDuration:.3 animations:^{
[self.view.window layoutSubviews];
} completion:^(BOOL finished) {
self.view.window.rootViewController = self.incomingVC;
}];
}
-(void)abortTransition:(int) sign {
self.leftCon.constant = self.w * sign;
[UIView animateWithDuration:.3 animations:^{
[self.view.window layoutSubviews];
} completion:^(BOOL finished) {
[self.incomingVC.view removeFromSuperview]; // this line and the next reset the system back to the inital state.
first = 1;
}];
}