Presenting modal in iOS 13 fullscreen

后端 未结 30 2071
囚心锁ツ
囚心锁ツ 2020-11-21 06:48

In iOS 13 there is a new behaviour for modal view controller when being presented.

Now it\'s not fullscreen by default and when I try to slide down, the app just dis

相关标签:
30条回答
  • 2020-11-21 07:02

    There are multiple ways to do that, and I think each one could fit for one project but not another, so I thought I'll keep them here maybe someone else will run to a different case.

    1- Override present

    If you have a BaseViewController you can override the present(_ viewControllerToPresent: animated flag: completion:) method.

    class BaseViewController: UIViewController {
    
      // ....
    
      override func present(_ viewControllerToPresent: UIViewController,
                            animated flag: Bool,
                            completion: (() -> Void)? = nil) {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
        super.present(viewControllerToPresent, animated: flag, completion: completion)
      }
    
      // ....
    }
    

    Using this way you don't need to do any change on any present call, as we just overrode the present method.

    2- An extension:

    extension UIViewController {
      func presentInFullScreen(_ viewController: UIViewController,
                               animated: Bool,
                               completion: (() -> Void)? = nil) {
        viewController.modalPresentationStyle = .fullScreen
        present(viewController, animated: animated, completion: completion)
      }
    }
    

    Usage:

    presentInFullScreen(viewController, animated: true)
    
    

    3- For one UIViewController

    let viewController = UIViewController()
    viewController.modalPresentationStyle = .fullScreen
    present(viewController, animated: true, completion: nil)
    

    4- From Storyboard

    Select a segue and set the presentation to FullScreen.

    5- Swizzling

    extension UIViewController {
    
      static func swizzlePresent() {
    
        let orginalSelector = #selector(present(_: animated: completion:))
        let swizzledSelector = #selector(swizzledPresent)
    
        guard let orginalMethod = class_getInstanceMethod(self, orginalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else{return}
    
        let didAddMethod = class_addMethod(self,
                                           orginalSelector,
                                           method_getImplementation(swizzledMethod),
                                           method_getTypeEncoding(swizzledMethod))
    
        if didAddMethod {
          class_replaceMethod(self,
                              swizzledSelector,
                              method_getImplementation(orginalMethod),
                              method_getTypeEncoding(orginalMethod))
        } else {
          method_exchangeImplementations(orginalMethod, swizzledMethod)
        }
    
      }
    
      @objc
      private func swizzledPresent(_ viewControllerToPresent: UIViewController,
                                   animated flag: Bool,
                                   completion: (() -> Void)? = nil) {
        if #available(iOS 13.0, *) {
          if viewControllerToPresent.modalPresentationStyle == .automatic {
            viewControllerToPresent.modalPresentationStyle = .fullScreen
          }
        }
        swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
       }
    }
    

    Usage:
    In your AppDelegate inside application(_ application: didFinishLaunchingWithOptions) add this line:

    UIViewController.swizzlePresent()
    

    Using this way you don't need to do any change on any present call, as we are replacing the present method implementation in runtime.
    If you need to know what is swizzling you can check this link: https://nshipster.com/swift-objc-runtime/

    0 讨论(0)
  • 2020-11-21 07:03

    I had this issue on the initial view right after the launch screen. The fix for me since I didn't have a segue or logic defined was to switch the presentation from automatic to fullscreen as shown here:

    0 讨论(0)
  • 2020-11-21 07:04

    With iOS 13, as stated in the Platforms State of the Union during the WWDC 2019, Apple introduced a new default card presentation. In order to force the fullscreen you have to specify it explicitly with:

    let vc = UIViewController()
    vc.modalPresentationStyle = .fullScreen //or .overFullScreen for transparency
    self.present(vc, animated: true, completion: nil)
    
    0 讨论(0)
  • 2020-11-21 07:04

    Change modalPresentationStyle before presenting

    vc.modalPresentationStyle = UIModalPresentationFullScreen;
    
    0 讨论(0)
  • 2020-11-21 07:06

    I add an information that could be useful for someone. If you have any storyboard segue, to go back to the old style, you need to set the kind property to Present Modally and the Presentation property to Full Screen.

    0 讨论(0)
  • 2020-11-21 07:06

    I used swizzling for ios 13

    import Foundation
    import UIKit
    
    private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
        if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
           let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
    
    extension UIViewController {
    
        static let preventPageSheetPresentation: Void = {
            if #available(iOS 13, *) {
                _swizzling(forClass: UIViewController.self,
                           originalSelector: #selector(present(_: animated: completion:)),
                           swizzledSelector: #selector(_swizzledPresent(_: animated: completion:)))
            }
        }()
    
        @available(iOS 13.0, *)
        @objc private func _swizzledPresent(_ viewControllerToPresent: UIViewController,
                                            animated flag: Bool,
                                            completion: (() -> Void)? = nil) {
            if viewControllerToPresent.modalPresentationStyle == .pageSheet
                       || viewControllerToPresent.modalPresentationStyle == .automatic {
                viewControllerToPresent.modalPresentationStyle = .fullScreen
            }
            _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
        }
    }
    

    then put this

    UIViewController.preventPageSheetPresentation
    

    somewhere

    for example in AppDelegate

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
    
        UIViewController.preventPageSheetPresentation
        // ...
        return true
    }
    
    0 讨论(0)
提交回复
热议问题