Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController custom transition stuck on iOS13

I've implemented a custom transition between two view controllers in my iOS app and it worked fine with iOS 10, 11, and 12.

Now I want make it ready for iOS 13 using Xcode 11 beta 6 and iOS 13 beta 8, but the transition is stuck.

The custom transition should move the first view controller up and out of the screen and the second one from bottom up. But now it falls back to iOS13 default presentation style pageSheet, just scales the first view controller down a little bit and adds a dimmed overlay. But the second view doesn't appear.

I've found that in the method animatePresentation(context: UIViewControllerContextTransitioning) the context doesn't return a 'from' view, so context.view(forKey: .from) returns nil.

What am I supposed to do without a 'from' view?

FlyUpTransition.swift

class FlyUpTransition: NSObject, UIViewControllerAnimatedTransitioning {

    var mode: Mode = .present

    enum Mode {
        case present
        case dismiss
    }

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        switch mode {
        case .present:
            animatePresentation(context: transitionContext)
        case .dismiss:
            animateDismissal(context: transitionContext)
        }
    }

    func animatePresentation(context: UIViewControllerContextTransitioning) {
        guard let fromView = context.view(forKey: .from), let toView = context.view(forKey: .to) else { return }
        ...
    }

    func animateDismissal(context: UIViewControllerContextTransitioning) {
        guard let fromView = context.view(forKey: .from), let toView = context.view(forKey: .to) else { return }
        ...
    }
}
like image 930
Lukas Würzburger Avatar asked Sep 05 '19 09:09

Lukas Würzburger


2 Answers

TL;DR

This is a bug in iOS, but you can use context.viewController(forKey:.from).view as a workaround.

Full Details

This appears to be a bug in iOS 13.0. As of iOS 13.1 beta 3, it's still there as well. http://www.openradar.me/radar?id=4999313432248320

The transition context's view(forKey:) method is incorrectly returning nil under certain circumstances. It appears this is happening for view(forKey: .from) when the presenting view controller is being presented non-modally. When dismissing a view controller that was originally presented from a non-modal view controller, the result of view(forKey: .to) is also nil.

I've observed this not only on the new sheet-style presentation on iPhone, but also in normal form sheets and popovers on iPad.

Another manifestation of this issue seems to be that the finalFrame(for:) method returns an incorrect CGRect when asked what the final frame for this view controller should be. In my testing, it is returning a full-screen rect, even though the correct view is smaller.

A workaround is to use the root view controller of view controller returned by viewController(forKey:) method, although the documentation explicitly discourages that: "The view returned by this method may or may not be the root view of the corresponding view controller."

like image 192
Chris Vasselli Avatar answered Nov 15 '22 23:11

Chris Vasselli


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.

like image 12
Lukas Würzburger Avatar answered Nov 16 '22 01:11

Lukas Würzburger