I made a custom segue that moves the current view to the left and moves the next view in from the right at the same time. When I used it on the first segueway it works perfectly, but when used in the next segueway only the destination view moves. The code for the segue looks like the following:
class CustomSlideSegue: UIStoryboardSegue {
override func perform() {
let firstVCView = self.sourceViewController.view as UIView!
let secondVCView = self.destinationViewController.view as UIView!
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
secondVCView.frame = CGRectMake(screenWidth, 0, screenWidth, screenHeight)
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
UIView.animateWithDuration(0.6, animations: { () -> Void in
firstVCView.transform = CGAffineTransformMakeTranslation(-screenWidth, 0)
secondVCView.transform = CGAffineTransformMakeTranslation(-screenWidth, 0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
}
}
}
And here is the code from the view controller button action method where I start the segue:
performSegueWithIdentifier("customSlideSegue", sender: self)
Here is a video showing the segues:
Can anyone see the problem? Or how should I go about debugging this? Thankful for replies!
the problem was that the second time you call the segue the sourceviewcontroller's view's transform already has a value of -screenWidth and thus it doesn't get pushed out of the screen. change your code to the following:
override func perform() {
let firstVCView = self.sourceViewController.view as UIView!
let secondVCView = self.destinationViewController.view as UIView!
let screenWidth = UIScreen.mainScreen().bounds.size.width
secondVCView.transform = CGAffineTransformMakeTranslation(screenWidth, 0)
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
UIView.animateWithDuration(0.6, animations: { () -> Void in
firstVCView.transform = CGAffineTransformMakeTranslation(-screenWidth, 0)
secondVCView.transform = CGAffineTransformIdentity
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: nil)
}
}
Related
I am using a UISplitViewController, with a MasterViewController and DetailViewController, without UINavigationControllers.
Currenly the segue animation for master->detail, triggered by
performSegueWithIdentifier("showDetail", sender: self)
consists in the DetailViewController showing up from the bottom upwards.
How can I make that animation showing left-wards?
I've recently needed to have more control of how the segues are being performed, so I made my custom segue classes which all perform the transition in different directions. Here's one of the implementations:
Swift 2.x
override func perform() {
//credits to http://www.appcoda.com/custom-segue-animations/
let firstClassView = self.sourceViewController.view
let secondClassView = self.destinationViewController.view
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
secondClassView.frame = CGRectMake(screenWidth, 0, screenWidth, screenHeight)
if let window = UIApplication.sharedApplication().keyWindow {
window.insertSubview(secondClassView, aboveSubview: firstClassView)
UIView.animateWithDuration(0.4, animations: { () -> Void in
firstClassView.frame = CGRectOffset(firstClassView.frame, -screenWidth, 0)
secondClassView.frame = CGRectOffset(secondClassView.frame, -screenWidth, 0)
}) {(Finished) -> Void in
self.sourceViewController.navigationController?.pushViewController(self.destinationViewController, animated: false)
}
}
}
This one will have a "right to left" transition. You can modify this function for your needs by simply changing the initial and ending positions of the source and destination view controller.
Also don't forget that you need to mark your segue as "custom segue", and to assign the new class to it.
UPDATE: Added Swift 3 version
Swift 3
override func perform() {
//credits to http://www.appcoda.com/custom-segue-animations/
let firstClassView = self.source.view
let secondClassView = self.destination.view
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
secondClassView?.frame = CGRect(x: screenWidth, y: 0, width: screenWidth, height: screenHeight)
if let window = UIApplication.shared.keyWindow {
window.insertSubview(secondClassView!, aboveSubview: firstClassView!)
UIView.animate(withDuration: 0.4, animations: { () -> Void in
firstClassView?.frame = (firstClassView?.frame.offsetBy(dx: -screenWidth, dy: 0))!
secondClassView?.frame = (secondClassView?.frame.offsetBy(dx: -screenWidth, dy: 0))!
}, completion: {(Finished) -> Void in
self.source.navigationController?.pushViewController(self.destination, animated: false)
})
}
}
Embed your view controllers in UINavigationControllers.
Per the SplitViewController template:
On smaller devices it's going to have to use the Master's navigationController.
Furthermore this question has been answered here and here and here
More from the View Controller Programming Guide:
There are two ways to display a view controller onscreen: embed it in
a container view controller or present it. Container view controllers
provide an app’s primary navigation….
In storyboards it's not that difficult to embed something in a navigation controller. Click on the view controller you want to embed, then Editor->embed in->navigation controller.
--Swift 3.0--
Armin's solution adapted for swift 3.
New -> File -> Cocoa Touch Class -> Class: ... (Subclass: UIStoryboardSegue).
import UIKit
class SlideHorSegue: UIStoryboardSegue {
override func perform() {
//credits to http://www.appcoda.com/custom-segue-animations/
let firstClassView = self.source.view
let secondClassView = self.destination.view
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
secondClassView?.frame = CGRect(x: screenWidth, y: 0, width: screenWidth, height: screenHeight)
if let window = UIApplication.shared.keyWindow {
window.insertSubview(secondClassView!, aboveSubview: firstClassView!)
UIView.animate(withDuration: 0.4, animations: { () -> Void in
firstClassView?.frame = (firstClassView?.frame)!.offsetBy(dx: -screenWidth, dy: 0)
secondClassView?.frame = (secondClassView?.frame)!.offsetBy(dx: -screenWidth, dy: 0)
}) {(Finished) -> Void in
self.source.navigationController?.pushViewController(self.destination, animated: false)
}
}
}
}
In storyboard: mark your segue as "custom segue", and to assign the new class to it.
Note: If you have a UIScrollView in your detailesVC, this won't work.
It sounds as though the new view controller is presenting modally. If you embed the detailViewController into a UINavigationController and push the new controller it will animate from right to left and should show a back button too by default.
When you have a compact-width screen,"Show Detail" segue fall back to modal segue automatically.So your DetailViewController will appear vertically as the default modal segue.
You can use a UIViewControllerTransitioningDelegate to custom the animation of a modal segue.
Here is an example to achieve the horizontal animation:
1. set the transitioningDelegate and it's delegate method
class MasterViewController: UITableViewController,UIViewControllerTransitioningDelegate {
//prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
let detailVC = segue.destinationViewController as! DetailViewController
detailVC.transitioningDelegate = self
// detailVC.detailItem = object//Configure detailVC
}
}
//UIViewControllerTransitioningDelegate
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return LeftTransition()
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let leftTransiton = LeftTransition()
leftTransiton.dismiss = true
return leftTransiton
}
}
2: a custom UIViewControllerAnimatedTransitioning : LeftTransition
import UIKit
class LeftTransition: NSObject ,UIViewControllerAnimatedTransitioning {
var dismiss = false
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 2.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning){
// Get the two view controllers
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
var originRect = containerView.bounds
originRect.origin = CGPointMake(CGRectGetWidth(originRect), 0)
containerView.addSubview(fromVC.view)
containerView.addSubview(toVC.view)
if dismiss{
containerView.bringSubviewToFront(fromVC.view)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
fromVC.view.frame = originRect
}, completion: { (_ ) -> Void in
fromVC.view.removeFromSuperview()
transitionContext.completeTransition(true )
})
}else{
toVC.view.frame = originRect
UIView.animateWithDuration(transitionDuration(transitionContext),
animations: { () -> Void in
toVC.view.center = containerView.center
}) { (_) -> Void in
fromVC.view.removeFromSuperview()
transitionContext.completeTransition(true )
}
}
}
}
I want to navigate tru ViewControllers with animations like in PageViewController if you choose Scroll style. So I want to navigate tru them using left/right swipes.
How to do it without UIPageViewController.
Is there a way to develop reusable class which could be used as main transition style in my app ?
I appreciate any help
If you want the same effect as UIPageViewController you should just use it. That's what it's there for. If you have a real reason for not using it then you can google any of the many tutorials for implementing a container view controller.
Well, I managed to do it using Custom Storyboard Segues
Here is the code :
Custom segue :
import UIKit
class CustomScrollPagingSegue: UIStoryboardSegue {
override func perform() {
let firstVCView = self.sourceViewController.view as UIView!
let secondVCView = self.destinationViewController.view as UIView!
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
secondVCView.frame = CGRectMake(screenWidth, 0.0, screenWidth, screenHeight)
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
UIView.animateWithDuration(0.5, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, -screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, -screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
}
}
}
and Custom Unwind Segue
import UIKit
class CustomScrollPagingSegueUnwind: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let secondVCView = self.sourceViewController.view as UIView!
let firstVCView = self.destinationViewController.view as UIView!
let screenWidth = UIScreen.mainScreen().bounds.size.width
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(firstVCView, aboveSubview: secondVCView)
// Animate the transition.
UIView.animateWithDuration(0.5, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
}
}
}
I think having 100% same result is possible with interactive transitions, but I don't know how to implement them.
I have a storyboard like that :
Article View is presented from segue and animation :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "showArticleFromArticles" {
let ViewToShow = segue.destinationViewController as! ArticleView
ViewToShow.articleToShow = ArticleToShow2
ViewToShow.transitioningDelegate = self
}
}
My animation :
class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerInteractiveTransitioning, UIViewControllerTransitioningDelegate, UIViewControllerContextTransitioning {
weak var transitionContext: UIViewControllerContextTransitioning?
var sourceViewController: UIViewController! {
didSet {
print("set")
print(sourceViewController)
enterPanGesture = UIScreenEdgePanGestureRecognizer()
enterPanGesture.addTarget(self, action:"panned:")
enterPanGesture.edges = UIRectEdge.Left
let newSource = sourceViewController as! ArticleView
newSource.WebView.addGestureRecognizer(enterPanGesture)
}
}
let duration = 1.0
var presenting = true
var originFrame = CGRectNull
private var didStartedTransition = false
private var animated = false
private var interactive = false
private var AnimationStyle = UIModalPresentationStyle(rawValue: 1)
private var didFinishedTransition = false
private var percentTransition: CGFloat = 0.0
private var enterPanGesture: UIScreenEdgePanGestureRecognizer!
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// get reference to our fromView, toView and the container view that we should perform the transition in
let container = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
// set up from 2D transforms that we'll use in the animation
let offScreenRight = CGAffineTransformMakeTranslation(container!.frame.width, 0)
let offScreenLeft = CGAffineTransformMakeTranslation(container!.frame.width, 0)
// start the toView to the right of the screen
toView.transform = offScreenRight
// add the both views to our view controller
container!.addSubview(toView)
container!.addSubview(fromView)
// get the duration of the animation
// DON'T just type '0.5s' -- the reason why won't make sense until the next post
// but for now it's important to just follow this approach
let duration = self.transitionDuration(transitionContext)
// perform the animation!
// for this example, just slid both fromView and toView to the left at the same time
// meaning fromView is pushed off the screen and toView slides into view
// we also use the block animation usingSpringWithDamping for a little bounce
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.8, options: UIViewAnimationOptions.TransitionFlipFromRight, animations: {
fromView.transform = offScreenLeft
toView.transform = CGAffineTransformIdentity
}, completion: { finished in
// tell our transitionContext object that we've finished animating
transitionContext.completeTransition(true)
})
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return duration
}
func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning) {
interactive = true
// get reference to our fromView, toView and the container view that we should perform the transition in
let container = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
// set up from 2D transforms that we'll use in the animation
let offScreenRight = CGAffineTransformMakeTranslation(container!.frame.width, 0)
let offScreenLeft = CGAffineTransformMakeTranslation(container!.frame.width, 0)
// start the toView to the right of the screen
toView.transform = offScreenRight
// add the both views to our view controller
container!.addSubview(toView)
container!.addSubview(fromView)
// get the duration of the animation
// DON'T just type '0.5s' -- the reason why won't make sense until the next post
// but for now it's important to just follow this approach
let duration = self.transitionDuration(transitionContext)
// perform the animation!
// for this example, just slid both fromView and toView to the left at the same time
// meaning fromView is pushed off the screen and toView slides into view
// we also use the block animation usingSpringWithDamping for a little bounce
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.8, options: UIViewAnimationOptions.TransitionFlipFromRight, animations: {
fromView.transform = offScreenLeft
toView.transform = CGAffineTransformIdentity
}, completion: { finished in
// tell our transitionContext object that we've finished animating
transitionContext.completeTransition(true)
})
}
func containerView() -> UIView? {
return sourceViewController?.view
}
func viewControllerForKey(key: String) -> UIViewController? {
return sourceViewController?.storyboard!.instantiateViewControllerWithIdentifier(key)
}
func viewForKey(key: String) -> UIView? {
return sourceViewController?.storyboard!.instantiateViewControllerWithIdentifier(key).view
}
func initialFrameForViewController(vc: UIViewController) -> CGRect {
return vc.view.frame
}
func finalFrameForViewController(vc: UIViewController) -> CGRect {
return vc.view.frame
}
func isAnimated() -> Bool {
return animated
}
func isInteractive() -> Bool {
return interactive
}
func presentationStyle() -> UIModalPresentationStyle {
return AnimationStyle!
}
func completeTransition(didComplete: Bool) {
didFinishedTransition = didComplete
}
func updateInteractiveTransition(percentComplete: CGFloat) {
percentTransition = percentComplete
}
func finishInteractiveTransition() {
completeTransition(true)
}
func cancelInteractiveTransition() {
completeTransition(true)
}
func transitionWasCancelled() -> Bool {
return didFinishedTransition
}
func targetTransform() -> CGAffineTransform {
return CGAffineTransform()
}
func panned(pan: UIPanGestureRecognizer) {
//print(pan.translationInView(sourceViewController!.view))
switch pan.state {
case .Began:
animated = true
didStartedTransition = true
didFinishedTransition = false
sourceViewController?.dismissViewControllerAnimated(true, completion: nil)
if transitionContext != nil {
startInteractiveTransition(transitionContext!)
}
break
case .Changed:
percentTransition = CGFloat(pan.translationInView(sourceViewController!.view).x / sourceViewController!.view.frame.width)
print(percentTransition)
updateInteractiveTransition(percentTransition)
break
case .Ended, .Failed, .Cancelled:
animated = false
didStartedTransition = false
didFinishedTransition = true
finishInteractiveTransition()
break
case .Possible:
break
}
}
}
From Article View, I call dismiss view like that :
#IBAction func Quit(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
and :
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.presenting = false
return transition
}
And i add the PanGesture like that :
let transition = TransitionManager()
self.transition.sourceViewController = self
But Pan Gesture just dismiss the view, and Interactive is not available
Because i call :
self.dismissViewControllerAnimated(true, completion: nil)
during UIPanGestureRecognizer.began
How can I do this ?
I am using Xcode 7, Swift 2, iOS 9
Thanks !
I found the solution :
i should just use
startInteractiveTransition
to instantiate some things
and use :
func updateInteractiveTransition(percentComplete: CGFloat) {
if self.reverse {
print(percentComplete)
self.tovc.view.frame.origin.x = (self.fromvc.view.frame.maxX * (percentComplete)) - self.fromvc.view.frame.maxX
}
}
to custom my transition.
Easy to use, Just inherent Your UIViewController with InteractiveViewController and you are done
InteractiveViewController
call method showInteractive() from your controller to show as Interactive.
When swipe , i want navigate between pages with smoothly ( change according to finger moves ) not to navigate with a given time
class FirstCustomSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
var firstVCView = self.sourceViewController.view as UIView!
var secondVCView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(screenWidth, 0.0, screenWidth, screenHeight)
// Access the app's key window and insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(0.4, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, -screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, -screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController,
animated: false,
completion: nil)
}
} }
I had to deal with it recently. look at my github project, maybe it will hepl you.
If a nutshell. You should create class adopts
UIViewControllerAnimatedTransitioning
and implement 2 methods. One for animation's duration, another for your custom animation (moving, fade, and so on).
For example:
class ModalPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
// ...
let duration = transitionDuration(transitionContext)
UIView.animateWithDuration(duration, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.2, options: UIViewAnimationOptions.CurveLinear, animations: {
toVC.view.frame = // new postion
}) { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
}
Then in your ViewController to specify a new ViewControllerTransitioning
extension ViewController: UIViewControllerTransitioningDelegate {
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
swipeInteractionController.wireToViewController(presented)
return modalPresentAnimationController
}
But, if you want to add "change according to finger moves" you should create class adopts
UIPercentDrivenInteractiveTransition
And in it to use gestureRecognizer.
I have two views I would like to make a swipe style transition accross and I have done that when there is a segue to act on but the I don't have one here so am not sure how to apply my animation class. All I have in my class is:
let stb = UIStoryboard(name: "Walkthrough", bundle: nil)
let walkthrough = stb.instantiateViewControllerWithIdentifier("walk") as! BWWalkthroughViewController
self.presentViewController(walkthrough, animated: true, completion: nil)
I want to apply apply the following custom segue:
class CustomSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
var firstVCView = self.sourceViewController.view as UIView!
var secondVCView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(0.0, screenHeight, screenWidth, screenHeight)
// Access the app's key window and insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(0.2, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, -screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, -screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController,
animated: false,
completion:nil)
}
}
}
I cannot get it to work any pointers please?
While the first answer should be "use Storyboard Segues", you can solve custom transitions this way:
Generic Approach
Modify your CustomSegue to adopt both UIStoryboardSegue and UIViewControllerAnimatedTransitioning protocols.
Refactor CustomSegue so that the animation can be used by both protocols.
Setup a delegate to the navigation controller, which can be itself, to supply the custom transitions to push & pop
let animationControllerForOperation create and return an instance of CustomSegue, with an identifier of your choice.
Overview
// Adopt both protocols
class CustomSegue: UIStoryboardSegue, UIViewControllerAnimatedTransitioning {
func animate(firstVCView:UIView,
secondVCView:UIView,
containerView:UIView,
transitionContext: UIViewControllerContextTransitioning?) {
// factored transition code goes here
}) { (Finished) -> Void in
if let context = transitionContext {
// UIViewControllerAnimatedTransitioning
} else {
// UIStoryboardSegue
}
}
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
// return timing
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// Perform animate using transitionContext information
// (UIViewControllerAnimatedTransitioning)
}
override func perform() {
// Perform animate using segue (self) variables
// (UIStoryboardSegue)
}
}
Below is the complete code example. It has been tested. Notice that I also fixed a few animation bugs from the original question, and added a fade-in effect to emphasize the animation.
CustomSegue Class
// Animation for both a Segue and a Transition
class CustomSegue: UIStoryboardSegue, UIViewControllerAnimatedTransitioning {
func animate(firstVCView:UIView,
secondVCView:UIView,
containerView:UIView,
transitionContext: UIViewControllerContextTransitioning?) {
// Get the screen width and height.
let offset = secondVCView.bounds.width
// Specify the initial position of the destination view.
secondVCView.frame = CGRectOffset(secondVCView.frame, offset, 0.0)
firstVCView.superview!.addSubview(secondVCView)
secondVCView.alpha = 0;
// Animate the transition.
UIView.animateWithDuration(self.transitionDuration(transitionContext!),
animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, -offset, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, -offset, 0.0)
secondVCView.alpha = 1; // emphasis
}) { (Finished) -> Void in
if let context = transitionContext {
context.completeTransition(!context.transitionWasCancelled())
} else {
self.sourceViewController.presentViewController(
self.destinationViewController as! UIViewController,
animated: false,
completion:nil)
}
}
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return 4 // four seconds
}
// Perform Transition (UIViewControllerAnimatedTransitioning)
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
self.animate(transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!.view,
secondVCView: transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!.view,
containerView: transitionContext.containerView(),
transitionContext: transitionContext)
}
// Perform Segue (UIStoryboardSegue)
override func perform() {
self.animate(self.sourceViewController.view!!,
secondVCView: self.destinationViewController.view!!,
containerView: self.sourceViewController.view!!.superview!,
transitionContext:nil)
}
}
Host ViewController Class
class ViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .Push:
return CustomSegue(identifier: "Abc", source: fromVC, destination: toVC)
default:
return nil
}
}
}