I have an app with a root view controller and two additional view controllers. For design reasons, those are supposed to appear to be either to the left or to the right of the root VC. This spatial relationship should be represented in the way they are presented to the user when they appear on screen. That is, through the push-animation.
When pushing a new view controller, it enters the screen from the right so that works fine or one of my two VC's. For the other, I have created a custom segue based on this example that will make the VC appear from left to right.
This works but the animation itself is not seamless. It doesn't look as if the view is push onto the root VC from the left - it looks as if there's a black background that is visible before the view appears.
I was wondering whether anyone would know how to get an animation that's a perfectly mirrored version of the default "push" (i.e. from right to left) animation?
Check out this article. It does just what you want, except for the transition itself. In a nutshell:
1) Create an object to perform the animation
@interface Animator : NSObject <UIViewControllerAnimatedTransitioning>
@end
2) Become your navigation controller's delegate, and answer an instance of that animator on this nav controller delegate method:
// in your vc.m
@interface ViewController () <UINavigationControllerDelegate> // add this
// in view did load
self.navigationController.delegate = self;
// implement this to pass your animator as the thing to be in charge of the transition animation
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController*)fromVC toViewController:(UIViewController*)toVC {
if (operation == UINavigationControllerOperationPush) {
return [[Animator alloc] init];
}
return nil;
}
3) In your animator, answer a duration:
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.25;
}
4) All of that is from the article. The only thing left to do is implement the left to right slide. (I changed the article's code to do your left to right slide):
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect fromVCFrame = fromViewController.view.frame;
CGFloat width = toViewController.view.frame.size.width;
[[transitionContext containerView] addSubview:toViewController.view];
toViewController.view.frame = CGRectOffset(fromVCFrame, -width, 0);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.frame = CGRectOffset(fromVCFrame, width, 0);
toViewController.view.frame = fromVCFrame;
} completion:^(BOOL finished) {
fromViewController.view.frame = fromVCFrame;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
I tried all this out in a little project and it works pretty slick.
Updated for Swift
Class Definition
class LeftRightAnimationController: NSObject, UIViewControllerAnimatedTransitioning
Set Navigation Delegate
class NavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
//Custom animation controller for settings table vc
let leftRightAnimationController = LeftRightAnimationController()
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if toVC.isKind(of: SettingsTableViewController.self){
self.leftRightAnimationController.originFrame = fromVC.view.frame
return self.leftRightAnimationController
}
return nil
}
}
Animator Duration
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.75
}`
Implement Left to Right Slide
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
let fromVCFrame = fromVC.view.frame
let width = toVC.view.frame.size.width
transitionContext.containerView.addSubview(toVC.view)
toVC.view.frame = fromVCFrame.offsetBy(dx: width, dy: 0)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
fromVC.view.frame = fromVCFrame.offsetBy(dx: -width, dy: 0)
toVC.view.frame = fromVCFrame
}) { (finished) in
fromVC.view.frame = fromVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With