How to present view controller from right to left in iOS using Swift
You can also use custom segue.
Swift 5
class SegueFromRight: UIStoryboardSegue {
override func perform() {
let src = self.source
let dst = self.destination
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)
UIView.animate(withDuration: 0.25,
delay: 0.0,
options: UIView.AnimationOptions.curveEaseInOut,
animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
},
completion: { finished in
src.present(dst, animated: false, completion: nil)
})
}
}
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
)
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)
}
}
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.5
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
transition.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.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
transition.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
.