How to present view controller from left to right in iOS? - ios

When adding a new controller to the navigation stack:
self.navigationController!.pushViewController(PushedViewController(), animated: true)
it appears from the right:
How can I change the direction of animation to make it appear from the left?

Swift 5.1: Segue from different directions
Here is a simple extension for different segue directions. (Tested in Swift 5)
It looks like you want to use segueFromLeft() I added some other examples as well.
extension CATransition {
//New viewController will appear from bottom of screen.
func segueFromBottom() -> CATransition {
self.duration = 0.375 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromTop
return self
}
//New viewController will appear from top of screen.
func segueFromTop() -> CATransition {
self.duration = 0.375 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromBottom
return self
}
//New viewController will appear from left side of screen.
func segueFromLeft() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromLeft
return self
}
//New viewController will pop from right side of screen.
func popFromRight() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.reveal
self.subtype = CATransitionSubtype.fromRight
return self
}
//New viewController will appear from left side of screen.
func popFromLeft() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.reveal
self.subtype = CATransitionSubtype.fromLeft
return self
}
}
And here is how you implement the above extension:
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(YourViewController(), animated: false)
}

let obj = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")as! ViewController
let transition:CATransition = CATransition()
transition.duration = 0.3
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
transition.type = .push
transition.subtype = .fromLeft
self.navigationController?.view.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(obj, animated: true)
Whene you use popToViewController that Time
transition.subtype = kCATransitionFromRight

This may help you
let nextVc = self.storyboard?.instantiateViewController(withIdentifier: "nextVc")
let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromLeft
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
view.window!.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(nextVc!, animated: false)

Ok, here's a drop-in solution for you. Add file named LeftToRightTransitionProxy.swift with the next content
import UIKit
final class LeftToRightTransitionProxy: NSObject {
func setup(with controller: UINavigationController) {
controller.delegate = self
}
}
extension LeftToRightTransitionProxy: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return AnimationController(direction: .forward)
} else {
return AnimationController(direction: .backward)
}
}
}
private final class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
enum Direction {
case forward, backward
}
let direction: Direction
init(direction: Direction) {
self.direction = direction
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toView = transitionContext.view(forKey: .to),
let fromView = transitionContext.view(forKey: .from) else {
return
}
let container = transitionContext.containerView
container.addSubview(toView)
let initialX: CGFloat
switch direction {
case .forward: initialX = -fromView.bounds.width
case .backward: initialX = fromView.bounds.width
}
toView.frame = CGRect(origin: CGPoint(x: initialX, y: 0), size: toView.bounds.size)
let animation: () -> Void = {
toView.frame = CGRect(origin: .zero, size: toView.bounds.size)
}
let completion: (Bool) -> Void = { _ in
let success = !transitionContext.transitionWasCancelled
if !success {
toView.removeFromSuperview()
}
transitionContext.completeTransition(success)
}
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
animations: animation,
completion: completion
)
}
}
And here's how you can use it:
final class ViewController: UIViewController {
let animationProxy = LeftToRightTransitionProxy()
override func viewDidLoad() {
super.viewDidLoad()
animationProxy.setup(with: navigationController!)
}
}
This solution provides animation for both forward and backward (push and pop) directions.
This can be controlled in navigationController(_:animationControllerFor:from:to:) method of your LeftToRightTransitionProxy class (just return nil to remove animation).
If you need this behaviour for specific subclass of UIViewController put appropriate checks in navigationController(_:animationControllerFor:from:to:) method:
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push && toVC is DetailViewController {
return AnimationController(direction: .forward)
} else if operation == .pop && toVC is ViewController {
return AnimationController(direction: .backward)
}
return nil
}

I used Hero as a solution.
import Hero
Then in that place where you’re going to show new UIViewController turn the default animation:
Hero.shared.defaultAnimation = HeroDefaultAnimationType.cover(direction: .right)
Also specify that your UINavigationController is going to use the Hero library:
self.navigationController?.hero.isEnabled = true
After that you’ll get the expected result even if you’re using the standard pushViewController function:
self.navigationController?.pushViewController(vc, animated: true)

You'll need to write your own transition procedure to achieve your needs.
DOCS from Apple:
https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning
Article:
https://medium.com/#ludvigeriksson/custom-interactive-uinavigationcontroller-transition-animations-in-swift-4-a4b5e0cefb1e

If you want to learn how to do custom transitions (i.e. presenting from right to left) then this is a pretty good tutorial for setting them up.
The key things you need to do are set up a transitioning delegate, a custom presentation controller, and a custom animation controller.

you could use a third party library, you can search them in github.comor cocoacontrols.com as navigation Drawer
In my case I use this
https://github.com/CosmicMind/Material#NavigationDrawer
others
https://www.cocoacontrols.com/search?q=Drawer
https://github.com/dekatotoro/SlideMenuControllerSwift
https://github.com/jonkykong/SideMenu

Related

How to open side menu from tabbar

I am trying to achieve some thing like following side menu open from tabbar item click.
I used the following class for Transition Animation ...
class SlideInTransition: NSObject, UIViewControllerAnimatedTransitioning {
var isPresenting = false
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width * 0.3
let finalHeight = toViewController.view.bounds.height
if isPresenting {
// Add dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
// Add menu view controller to container
containerView.addSubview(toViewController.view)
// Init frame off the screen
toViewController.view.frame = CGRect(x: -finalWidth, y: 0, width: finalWidth, height: finalHeight)
}
// Move on screen
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: finalWidth, y: 0)
}
// Move back off screen
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
// Animation of the transition
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
}
}
}
and use it in my code as follow
guard let menuViewController = storyboard?.instantiateViewController(withIdentifier: "MenuVC") as? MenuVC else { return }
menuViewController.modalPresentationStyle = .overCurrentContext
menuViewController.transitioningDelegate = self as? UIViewControllerTransitioningDelegate
menuViewController.tabBarItem.image = UIImage(named: "ico_menu")
menuViewController.tabBarItem.selectedImage = UIImage(named: "ico_menu")
viewControllers = [orderVC,serverdVC,canceledVC,menuViewController]
extension TabbarVC: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transiton.isPresenting = true
return transiton
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transiton.isPresenting = false
return transiton
}
}
but animation doe't work at all ... I want to open it like side menu over current context ..
How can i achieve some thing like that ...
TabBar is not made to handle animate transition for just a single child View controller. If you apply a custom transition, it will be applied in all of its tabs (child view controllers). Plus last time i checked, airbnb's app doesn't behave like that when opening the user profile. :)
What you can do, though, is have a separate menu button at the top of your navigation view controller or wherever and call the slide in from there:
func slideInView() {
let vcToShow = MenuViewController()
vcToShow.modalPresentationStyle = .overCurrentContext
vcToShow.transitioningDelegate = self as? UIViewControllerTransitioningDelegate
present(vcToShow, animated: true, completion: nil)
}
Or if you insist on having the menu a part of the tabs, then you can do this.
Hope this helps. :)

UINavigationController - how to change animation for single UIViewController

How can I have custom animation only for single UIViewController push/pop and default animation for other UIViewControllers in same UINavigationController?
I use UINavigationController and push/pop to navigate between different UIViewControllers. I created custom animation and set custom UINavigationControllerDelegate to UINavigationController.delegate. It animates fine but it changes animation style for all future push/pop in this UINavigationController.
I know I can change animation for single UIViewController using it's transitioningDelegate but it works only if it's displayed by present() and not push.
Is it possible to change animation only for single UIViewController in UINavigationController?
You can check for specific view controller from navigation controller like:
UINavigationcontroller.ViewControllers[number of that view controller]
and apply that animation on it.
Sub class UINavigationController and use CoreAnimation's transition to change push/pop animations like this.
enum TransitionType {
case fade
case movein
case push
case reveal
}
enum TransitionSubtype {
case right
case left
case top
case bottom
}
class MyNavigationController: UINavigationController {
fileprivate func getTransition(by type: TransitionType) -> String? {
var transition: String?
switch type {
case .fade:
transition = kCATransitionFade
break
case .push:
transition = kCATransitionPush
break
case .movein:
transition = kCATransitionMoveIn
break
case .reveal:
transition = kCATransitionReveal
break
default:
transition = nil
break
}
return transition
}
fileprivate func getSubTransition(by type: TransitionSubtype) -> String? {
var transition: String?
switch type {
case .right:
transition = kCATransitionFromRight
break
case .left:
transition = kCATransitionFromLeft
break
case .top:
transition = kCATransitionFromTop
break
case .bottom:
transition = kCATransitionFromBottom
break
default:
transition = nil
break
}
return transition
}
func display(viewController: UIViewController, animated: Bool, animationType: TransitionType = .push, animationSubtype: TransitionSubtype = .left) -> Bool {
guard let type = getTransition(by: animationType) else {return false}
guard let subtype = getSubTransition(by: animationSubtype) else {return false}
if animated {
let transition = CATransition()
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = type
transition.subtype = subtype
self.view.layer.add(transition, forKey: nil)
}
_ = self.pushViewController(viewController, animated: false)
return true
}
func hide(animated: Bool, animationType: TransitionType = .push, animationSubtype: TransitionSubtype = .left) -> Bool {
guard let type = getTransition(by: animationType) else {return false}
guard let subtype = getSubTransition(by: animationSubtype) else {return false}
if animated {
let transition = CATransition()
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = type
transition.subtype = subtype
self.view.layer.add(transition, forKey: nil)
}
_ = self.popViewController(animated: false)
return true
}
}

Swift : UIPercentDrivenInteractiveTransition on cancel?

This is my first iOS development and so I am using this tiny project to learn how the system works and how the language (swift) works too.
I am trying to make a drawer menu similar to android app and a certain number of iOS app.
I found this tutorial that explains well how to do it and how it works : here
Now since I am using a NavigationController with show I have to modify the way it is done.
I swapped the UIViewControllerTransitioningDelegate to a UINavigationControllerDelegate so I can override the navigationController function.
This means I can get the drawer out and dismiss it. It works well with a button or with the gesture.
My problem is the following : If I don't finish to drag the drawer far enough for it to reach the threshold and finishing the animation, it will be cancel and hidden. This is all well and good but when that happens there is no call to a dismiss function meaning that the snapshot I put in place in the PresentMenuAnimator is still in front of all the layers and I am stuck there even though I can interact with what's behind it.
How can I catch a dismiss or a cancel with the NavigationController ? Is that possible ?
Interactor :
import UIKit
class Interactor:UIPercentDrivenInteractiveTransition {
var hasStarted: Bool = false;
var shouldFinish: Bool = false;
}
MenuHelper :
import Foundation
import UIKit
enum Direction {
case Up
case Down
case Left
case Right
}
struct MenuHelper {
static let menuWith:CGFloat = 0.8;
static let percentThreshold:CGFloat = 0.6;
static let snapshotNumber = 12345;
static func calculateProgress(translationInView:CGPoint, viewBounds:CGRect, direction: Direction) -> CGFloat {
let pointOnAxis:CGFloat;
let axisLength:CGFloat;
switch direction {
case .Up, .Down :
pointOnAxis = translationInView.y;
axisLength = viewBounds.height;
case .Left, .Right :
pointOnAxis = translationInView.x;
axisLength = viewBounds.width;
}
let movementOnAxis = pointOnAxis/axisLength;
let positiveMovementOnAxis:Float;
let positiveMovementOnAxisPercent:Float;
switch direction {
case .Right, .Down:
positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0);
positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0);
return CGFloat(positiveMovementOnAxisPercent);
case .Left, .Up :
positiveMovementOnAxis = fminf(Float(movementOnAxis), 0.0);
positiveMovementOnAxisPercent = fmaxf(positiveMovementOnAxis, -1.0);
return CGFloat(-positiveMovementOnAxisPercent);
}
}
static func mapGestureStateToInteractor(gestureState:UIGestureRecognizerState, progress:CGFloat, interactor: Interactor?, triggerSegue: () -> Void ) {
guard let interactor = interactor else {return };
switch gestureState {
case .began :
interactor.hasStarted = true;
interactor.shouldFinish = false;
triggerSegue();
case .changed :
interactor.shouldFinish = progress > percentThreshold;
interactor.update(progress);
case .cancelled :
interactor.hasStarted = false;
interactor.shouldFinish = false;
interactor.cancel();
case .ended :
interactor.hasStarted = false;
interactor.shouldFinish
? interactor.finish()
: interactor.cancel();
interactor.shouldFinish = false;
default :
break;
}
}
}
MenuNavigationController :
import Foundation
import UIKit
class MenuNavigationController: UINavigationController, UINavigationControllerDelegate {
let interactor = Interactor()
override func viewDidLoad() {
super.viewDidLoad();
self.delegate = self;
}
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if((toVC as? MenuViewController) != nil) {
return PresentMenuAnimator();
}
else {
return DismissMenuAnimator();
}
}
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil;
}
}
PresentMenuAnimator :
import UIKit
class PresentMenuAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6;
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
else {return};
let containerView = transitionContext.containerView;
containerView.insertSubview(toVC.view, aboveSubview: fromVC.view);
let snapshot = fromVC.view.snapshotView(afterScreenUpdates: false);
snapshot?.tag = MenuHelper.snapshotNumber;
snapshot?.isUserInteractionEnabled = false;
snapshot?.layer.shadowOpacity = 0.7;
containerView.insertSubview(snapshot!, aboveSubview: toVC.view);
fromVC.view.isHidden = true;
UIView.animate(withDuration: transitionDuration(using: transitionContext),
animations: {snapshot?.center.x+=UIScreen.main.bounds.width*MenuHelper.menuWith;},
completion: {_ in
fromVC.view.isHidden = false;
transitionContext.completeTransition(!transitionContext.transitionWasCancelled);}
);
}
}
DismissMenuAnimator :
import UIKit
class DismissMenuAnimator : NSObject {
}
extension DismissMenuAnimator : UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6;
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
else {
return
}
let containerView = transitionContext.containerView;
let snapshot = containerView.viewWithTag(MenuHelper.snapshotNumber)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
animations: {
snapshot?.frame = CGRect(origin: CGPoint.zero, size: UIScreen.main.bounds.size)
},
completion: { _ in
let didTransitionComplete = !transitionContext.transitionWasCancelled
if didTransitionComplete {
containerView.insertSubview(toVC.view, aboveSubview: fromVC.view)
snapshot?.removeFromSuperview()
}
transitionContext.completeTransition(didTransitionComplete)
}
)
}
}
It is possible to know whether the animation was cancelled, and it can be caught in the func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) method from UINavigationControllerDelegate.
Here's a snippet of code on how to do so:
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
navigationController.transitionCoordinator?.notifyWhenInteractionEnds { context in
if context.isCancelled {
// The interactive back transition was cancelled
}
}
}
This method could be put in your MenuNavigationController, in which you could persist your PresentMenuAnimator and tell it that the transition was cancelled, and in there remove the snapshot that's hanging around.
To fix the problem I added a verification in PresentMenuAnimator to check if it the animation was canceled.
If it was then remove the snapshot in the UIView.Animate.

iOS 10 - black screen after custom animation

I've a custom animation that works correctly except that, at the end of dismiss animation, there is a black screen.
The code of the transition is:
class FolderAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 5.0
var presenting = true
var originFrame = CGRect.zero
var selectedFolderCell: FolderCollectionViewCell?
func transitionDuration(_ transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()
let toViewC = transitionContext.viewController(forKey: UITransitionContextToViewControllerKey)!
let fromViewC = transitionContext.viewController(forKey: UITransitionContextFromViewControllerKey)!
let folderViewC = presenting ? fromViewC as! ViewController : transitionContext.viewController(forKey: UITransitionContextToViewControllerKey) as! ViewController
let projectViewC = presenting ? toViewC as! ProjectViewController : transitionContext.viewController(forKey: UITransitionContextFromViewControllerKey) as! ProjectViewController
let cellView = presenting ? (folderViewC.folderCollectionView.cellForItem(at: (folderViewC.folderCollectionView.indexPathsForSelectedItems()?.first!)!) as! FolderCollectionViewCell).folderView : projectViewC.containerView
let cellSnapshot: UIView = presenting ? cellView!.snapshotView(afterScreenUpdates: false)! : cellView!.snapshotView(afterScreenUpdates: false)!
let cellFrame = containerView.convert(cellView!.frame, from: cellView!.superview)
cellSnapshot.frame = cellFrame
cellView!.isHidden = true
toViewC.view.frame = transitionContext.finalFrame(for: toViewC)
toViewC.view.layoutIfNeeded()
toViewC.view.alpha = 0
presenting ? (projectViewC.containerView.isHidden = true) : (self.selectedFolderCell!.folderView.isHidden = true)
containerView.addSubview(toViewC.view)
containerView.addSubview(cellSnapshot)
UIView.animate(withDuration: duration, animations: {
toViewC.view.alpha = 1.0
let finalFrame = self.presenting ? projectViewC.containerView.frame : self.originFrame
cellSnapshot.frame = finalFrame
}) { (_) in
self.presenting ? (projectViewC.containerView.isHidden = false) : (self.selectedFolderCell?.isHidden = false)
cellSnapshot.removeFromSuperview()
transitionContext.completeTransition(true)
}
}
}
And the code of the first view controller that call the animation:
func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
presentAnimator.presenting = true
presentAnimator.originFrame = openingFrame!
presentAnimator.selectedFolderCell = selectedCell!
return presentAnimator
}
func animationController(forDismissedController dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
presentAnimator.presenting = false
return presentAnimator
}
Use UIViewPropertyAnimator.runningPropertyAnimator instead UIView.animate
There was a bug in the first beta of Xcode 8. It was resolved on the second beta.

UIViewControllerAnimatedTransitioning does not work properly

I want to make a transitioning animation on navigationController's pushes and pops. But it works really weird. Like 1/5 times the fromViewController fades out as it should but toViewController does not fade in, it just appears. Constraints go weird sometimes too. If I use standard transitioning it is all OK. Here is my transitioning class:
class TMFadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
toViewController?.beginAppearanceTransition(true, animated: true)
fromViewController?.beginAppearanceTransition(false, animated: true)
transitionContext.containerView()!.addSubview(toViewController!.view)
toViewController!.view.alpha = 0.0
UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: {
toViewController!.view.alpha = 1.0
fromViewController!.view.alpha = 0.0
}) { (finished) in
toViewController?.endAppearanceTransition()
fromViewController?.endAppearanceTransition()
transitionContext.completeTransition(finished)
}
}
}
Strangely enough the problems were solved by putting toViewController?.view.frame = fromViewController!.view.frame just after the toViewController and fromViewController were defined. (Thanks to A'sa Dickens for findinding it out). Here is the final code
class TMFadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
toViewController?.view.frame = fromViewController!.view.frame
toViewController?.beginAppearanceTransition(true, animated: true)
fromViewController?.beginAppearanceTransition(false, animated: true)
transitionContext.containerView()!.addSubview(toViewController!.view)
toViewController!.view.alpha = 0.0
UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: {
toViewController!.view.alpha = 1.0
fromViewController!.view.alpha = 0.0
}) { (finished) in
toViewController?.endAppearanceTransition()
fromViewController?.endAppearanceTransition()
transitionContext.completeTransition(finished)
}
}
}

Resources