在iOS 13中,模态视图控制器在呈现时有一个新的行为。

现在它不是全屏默认情况下,当我向下滑动时,应用会自动解除视图控制器。

我怎么能防止这种行为,并回到旧的全屏模态vc?

谢谢


当前回答

以上的答案和建议是正确的,下面是另一个版本,和有效的方法使用编程。

创建一个UIView扩展

创建一个方法()

//#1
extension UIViewController {

//#2
func presentLocal(_ viewControllerToPresent: UIViewController, animated flag: 
Bool, completion: (() -> Void)? = nil) {

//Reusing below 2 lines :-)
viewControllerToPresent.modalPresentationStyle = .overCurrentContext
self.present(viewControllerToPresent, animated: flag, completion: completion)

  }
}

调用如下所示

let vc = MyViewController()
let nc = UINavigationController(rootViewController: vc)
sourceView.presentLocal(nc, animated: true, completion: nil)

OR

let vc = MyViewController()
sourceView.presentLocal(vc, animated: true, completion: nil)

其他回答

有多种方法可以做到这一点,我认为每一种方法都适合一个项目,但不适合另一个项目,所以我想我将它们保留在这里,也许其他人会处理不同的情况。

1-覆盖当前

如果你有一个BaseViewController,你可以重写present(_ viewcontrollertoppresent: animated flag: completion:)方法。

class BaseViewController: UIViewController {

  // ....

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

  // ....
}

使用这种方法,您不需要对任何present调用做任何更改,因为我们只是覆盖了present方法。

2-扩展:

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

用法:

presentInFullScreen(viewController, animated: true)

3-对于一个UIViewController

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

4-从故事板

选择一个segue并将演示设置为FullScreen。

5 -狂饮

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)
   }
}

用法: 在你的AppDelegate内部应用(_ application: didFinishLaunchingWithOptions)中添加这一行:

UIViewController.swizzlePresent()

使用这种方式,您不需要对任何present调用做任何更改,因为我们正在运行时替换present方法实现。 如果你想知道什么是swizzling,你可以查看这个链接: https://nshipster.com/swift-objc-runtime/

我在ios 13中使用了swizzling

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)
    }
}

然后放这个

UIViewController.preventPageSheetPresentation

的某个地方

例如在AppDelegate中

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {

    UIViewController.preventPageSheetPresentation
    // ...
    return true
}

我在启动屏幕后的初始视图中遇到了这个问题。修复我,因为我没有一个segue或逻辑定义是切换演示从自动到全屏如下所示:

override modalPresentationStyle将修复使用或不使用编码器创建的UIViewControllers的样式。

优点:

你放置它的唯一位置。 不需要知道应该设置哪个init或awake方法

劣势:

你不能从外部改变它,像接口构建器或配置从代码

解决方案:

override var modalPresentationStyle: UIModalPresentationStyle {
    get { .fullScreen }
    set { }
}

我添加的信息可能对某人有用。如果你有任何故事板segue,要回到旧的风格,你需要将kind属性设置为modal Present,并将Presentation属性设置为全屏。