"From View Controller" Disappears Using Uiviewcontrollercontexttransitioning

From View Controller disappears using UIViewControllerContextTransitioning

I was having the same problem here – looks like a bug in iOS 8. I've filed a radar.

I used Reveal to inspect the view hierarchy after the screen goes black. The key UIWindow is completely empty – no view hierarchy at all!

Reveal'd

I played around a bit and it looks like there is an easy workaround, for simple cases. You can just re-add the toViewController's view as a subview of the key window's:

transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)

I've checked and the key window's rootViewController is still correctly set, so that's fine. I'm not sure what would happen if you presented your controller from within an already presented modal controller, so for more complex cases, you'll have to experiment around.

UIViewController interactive transition - Presented view disappears when interactive dismiss is canceled

Thanks to a comment by @Dare, I realized all that was needed was a small update to the dismiss animation completion block:

// before - broken

transitionContext.completeTransition(true)

// after - WORKING!

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)

How do I keep the source view controller from disappearing after a push segue?

I suggest you create a custom transition between view controllers.

I just wrote and tested this class:

class AnimationController: NSObject, UIViewControllerAnimatedTransitioning
{
var pushing = true

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let duration = transitionDuration(using: transitionContext)

let toVc = transitionContext.viewController(forKey: .to)!
let toView = transitionContext.view(forKey: .to)!

let fromView = transitionContext.view(forKey: .from)!

let container = transitionContext.containerView

if pushing {
container.addSubview(fromView)
container.addSubview(toView)
}

var finalFrame = transitionContext.finalFrame(for: toVc)
if pushing {
finalFrame.origin.x = finalFrame.width
toView.frame = finalFrame
finalFrame.origin.x = 0
} else {
finalFrame.origin.x = finalFrame.width
}

UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
if self.pushing {
toView.frame = finalFrame
} else {
fromView.frame = finalFrame
}
}) { (_) in
transitionContext.completeTransition(true)
if self.pushing {
container.insertSubview(fromView, belowSubview: toView)
} else {
fromView.removeFromSuperview()
}
}
}
}

In your UINavigationController class do the following:

class NavigationController: UINavigationController {

let animationController = AnimationController()

override func viewDidLoad() {
super.viewDidLoad()

delegate = self
}
}

And this extension:

extension NavigationController: UINavigationControllerDelegate
{
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

animationController.pushing = operation == .push

return animationController
}
}

However, this makes you lose the interactive dismiss gesture (Swiping from the left of the screen to dismiss) So you would need to fix that yourself.

Container view disappearing on completeTransition:

The container view disappearing on dismissal is correct behavior. Your mistake is adding the fromView to it.

You are incorrectly distinguishing whether this is presentation or dismissal and what you should do in each case. Simply use the two view controllers fromViewController and toViewController to tell them apart; on dismissal, the roles are reversed. On dismissal, do not add anything to the content view; the original presenter is still present and will be revealed by the removal of the container view.

So, on presentation, add only the toView to the container view. On dismissal, do not add anything to the container view. It's as simple as that.

Presented view controller disappears after animation using custom UIViewController animations

This seems to be an iOS8 bug. I found a solution but it is ghetto. After the transition when a view should be on-screen but isn't, it needs to be added back to the window like this:

BOOL canceled = [transitionContext transitionWasCancelled];
[transitionContext completeTransition:!canceled];
if (!canceled)
{
[[UIApplication sharedApplication].keyWindow addSubview: toViewController.view];
}

You might need to play around with which view you add back to the window, whether to do it in canceled or !canceled, and perhaps making sure to only do it on dismissal and not presentation.

Sources: Container view disappearing on completeTransition:
http://joystate.wordpress.com/2014/09/02/ios8-and-custom-uiviewcontrollers-transitions/

Does a view controller get removed entirely when using a custom transition?

You said:

is my view controller still get removed after the transition finishes or is it still there but off the screen?

There are two completely separate issues here.

First, there is a question of the view controller hierarchy. When you present a new view controller, the old view controller is always kept in the view controller hierarchy so that when you dismiss back to it, it will still be there. However, when you dismiss, the dismissed view controller will be removed from the view controller hierarchy and (unless you do something unusual, like keeping your own strong reference to it somewhere) it will be deallocated.

Second, there is a separate question of the view hierarchy. When presenting, the UIPresentationController dictates whether the presenting view controller's view remains in the view hierarchy or not. By default, it keeps it in the view hierarchy, but generally if doing a modal, full-screen "present", you'd specify a UIPresentationController subclass that tells it to remove the presenting view controller's view when the transition is done.


For example, when doing a custom modal "present" transition where the presented view controller's view is opaque and covers the whole screen, then your UIViewControllerTransitioningDelegate would not only supply the animation controllers, but also specify a presentation controller:

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return YourAnimationController(...)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return YourAnimationController(...)
}

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}

And that presentation controller might be fairly minimal, only telling it to remove the presenter's view:

class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}

UIViewController custom transition stuck on iOS13

Ok, it was easy, even though, it's a breaking API change of Apple.

viewController.modalPresentationStyle = .fullScreen

Now I have to go through my whole project and check all modal presentations if they still look as I need them to.



Related Topics



Leave a reply



Submit