In iOS 9, why is SFSafariViewController is being pushed instead of presented modally?

10,995

Solution 1

Here's a simple way to obtain a vertical modal presentation of a SFSafariViewController:

let safari = SFSafariViewController(URL: url)
safari.modalPresentationStyle = .overFullScreen
presentViewController(safari, animated: true, completion: nil)

Solution 2

I just had the same issue. Also, the done button work even if you don't set up the delegate. Not sure why it happens. However, I found a workaround: wrapping the safari controller in a navigation controller and hiding the navigation bar.

func openURL(url:NSURL) {

    if #available(iOS 9.0, *) {
        let safariController = SFSafariViewController(url: url)
        safariController.delegate = self
        let navigationController = UINavigationController(rootViewController: safariController)
        navigationController.setNavigationBarHidden(true, animated: false)
        self.present(navigationController, animated: true, completion: nil)
    } else {
        UIApplication.sharedApplication().openURL(url)
    }
}

Solution 3

To use the default modal transition style, you can simply set the transitioning delegate equal to self.

let svc = SFSafariViewController(url: url)
svc.transitioningDelegate = self //use default modal presentation instead of push
present(svc, animated: true, completion: nil)

You'll need to adopt the UIViewControllerTransitioningDelegate protocol in your view controller, but there are no required functions to implement.

This was mentioned in Session 225 at WWDC, What's New in Safari View Controller.

Solution 4

Objective-C version of iGerms answer:

-(void)openURL:(NSURL *)url {
   SFSafariViewController *safariController = [[SFSafariViewController alloc]initWithURL:url];
   safariController.delegate = self;
   UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:safariController];
   [navigationController setNavigationBarHidden:YES animated:NO];
   [self presentViewController:navigationController animated:YES completion:nil];
}
Share:
10,995
abc123
Author by

abc123

Updated on July 24, 2022

Comments

  • abc123
    abc123 almost 2 years

    I'm presenting a SFSafariViewController by calling presentViewController:animated:completion: on a UIViewController instance.

    The result is that it gets pushed on (slides in from the right), as if I called pushViewController:animated: on a UINavigationController instance. I've verified that this is all happening on the main queue. And the presenting view controller is not a modal itself (which shouldn't matter anyways, but just in case, we can rule that out).

    If I substitute the SFSafariViewController with a UIViewController, it works as expected, it presents modally.

    weakSelf.oAuthViewController = [[SFSafariViewController alloc] initWithURL:url];
    [viewController presentViewController:weakSelf.oAuthViewController animated:YES completion:nil];
    

    Any idea why or how to work around this?

  • Ivan Ičin
    Ivan Ičin almost 8 years
    While it works better when navigating back, this is not modal.
  • zidanex
    zidanex about 6 years
    iGerms solution is working, but this one is more elegant. Thanks!
  • tadija
    tadija almost 6 years
    I found that .overCurrentContext works better since it also prevents a bug on iPhone X when status bar is hidden (transparent status bar area).
  • John Scalo
    John Scalo about 5 years
    This is the only method that works if you're using a custom transitioning delegate. If presenting the SFSafariViewController directly, then the transitionContext's from view will always be nil, even with other modalPresentationStyles.