How to present view controller from right to left in iOS using Swift
It doesn't matter if it is xib
or storyboard
that you are using. Normally, the right to left transition is used when you push a view controller into presentor's UINavigiationController
.
UPDATE
Added timing function kCAMediaTimingFunctionEaseInEaseOut
Sample project with Swift 4 implementation added to GitHub
Swift 3 & 4.2
let transition = CATransition()transition.duration = 0.5transition.type = CATransitionType.pushtransition.subtype = CATransitionSubtype.fromRighttransition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)view.window!.layer.add(transition, forKey: kCATransition)present(dashboardWorkout, animated: false, completion: nil)
ObjC
CATransition *transition = [[CATransition alloc] init];transition.duration = 0.5;transition.type = kCATransitionPush;transition.subtype = kCATransitionFromRight;[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];[self.view.window.layer addAnimation:transition forKey:kCATransition];[self presentViewController:dashboardWorkout animated:false completion:nil];
Swift 2.x
let transition = CATransition()transition.duration = 0.5transition.type = kCATransitionPushtransition.subtype = kCATransitionFromRighttransition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)view.window!.layer.addAnimation(transition, forKey: kCATransition)presentViewController(dashboardWorkout, animated: false, completion: nil)
Seems like the animated
parameter in the presentViewController
method doesn't really matter in this case of custom transition. It can be of any value, either true
or false
.
Complete code for present/dismiss, Swift 3
extension UIViewController { func presentDetail(_ viewControllerToPresent: UIViewController) { let transition = CATransition() transition.duration = 0.25 transition.type = kCATransitionPush transition.subtype = kCATransitionFromRight self.view.window!.layer.add(transition, forKey: kCATransition) present(viewControllerToPresent, animated: false) } func dismissDetail() { let transition = CATransition() transition.duration = 0.25 transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft self.view.window!.layer.add(transition, forKey: kCATransition) dismiss(animated: false) }}
Read up all answers and can't see correct solution. The right way do to so is to make custom UIViewControllerAnimatedTransitioning for presented VC delegate.
So it assumes to make more steps, but the result is more customizable and haven't some side effects, like moving from view together with presented view.
So, assume you have some ViewController, and there is a method for presenting
var presentTransition: UIViewControllerAnimatedTransitioning?var dismissTransition: UIViewControllerAnimatedTransitioning? func showSettings(animated: Bool) { let vc = ... create new vc to present presentTransition = RightToLeftTransition() dismissTransition = LeftToRightTransition() vc.modalPresentationStyle = .custom vc.transitioningDelegate = self present(vc, animated: true, completion: { [weak self] in self?.presentTransition = nil })}
presentTransition
and dismissTransition
is used for animating your view controllers. So you adopt your ViewController to UIViewControllerTransitioningDelegate
:
extension ViewController: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return presentTransition } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return dismissTransition }}
So the last step is to create your custom transition:
class RightToLeftTransition: NSObject, UIViewControllerAnimatedTransitioning { let duration: TimeInterval = 0.25 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let container = transitionContext.containerView let toView = transitionContext.view(forKey: .to)! container.addSubview(toView) toView.frame.origin = CGPoint(x: toView.frame.width, y: 0) UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: { toView.frame.origin = CGPoint(x: 0, y: 0) }, completion: { _ in transitionContext.completeTransition(true) }) }}class LeftToRightTransition: NSObject, UIViewControllerAnimatedTransitioning { let duration: TimeInterval = 0.25 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let container = transitionContext.containerView let fromView = transitionContext.view(forKey: .from)! container.addSubview(fromView) fromView.frame.origin = .zero UIView.animate(withDuration: duration, delay: 0, options: .curveEaseIn, animations: { fromView.frame.origin = CGPoint(x: fromView.frame.width, y: 0) }, completion: { _ in fromView.removeFromSuperview() transitionContext.completeTransition(true) }) }}
In that code view controller is presented over current context, you can make your customizations from that point. Also you may see custom UIPresentationController
is useful as well (pass in using UIViewControllerTransitioningDelegate
)