全屏显示iOS 13中的模态


464

在iOS 13中,出现模态视图控制器时有新行为。

现在默认情况下它不是全屏显示,当我尝试向下滑动时,该应用程序会自动关闭View Controller。

如何防止这种行为并回到旧的全屏模式vc?

情态行为

谢谢

Answers:


621

如WWDC 2019期间平台联盟中所述,使用iOS 13,Apple引入了新的默认卡演示文稿。为了强制全屏,您必须使用以下命令明确指定它:

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen //or .overFullScreen for transparency
self.present(vc, animated: true, completion: nil)

81
我会说这不会在默认情况下持续很长时间。该fullScreen选项应为默认选项,以防止破坏UI更改。
雅各布·特鲁拉斯(JakubTruhlář)

17
我不会指望这一点。过去,Apple经常更改默认设置,仅在与当前SDK链接后才激活默认设置。我们希望对以前版本的链接时,你得到旧的行为。
DrMickeyLauer

11
我可以确认在iOS 13模拟器上运行的Xcode-10构建的应用程序仍默认为全屏显示。正如@DrMickeyLauer所说,使用Xcode 11进行构建会使该应用程序采用新的行为。使用isModalInPresentation从驳回阻止滑动手势。请参见我的博客文章了解详情:medium.com/@hacknicity/...
杰夫Hackworth

5
我建议使用.fullScreen而不是.overFullScreen。.fullScreen触发viewWillAppear和viewDidAppear,.overFullScreen不会执行此操作
PiterPan

5
时间已经过去,.automatic样式已定为默认样式,这是(对于大多数视图控制器而言).pageSheet样式。但是,某些系统视图控制器可能会将其映射为其他样式。
雅各布·特鲁拉斯(JakubTruhlář)

186

我添加了对某人可能有用的信息。如果您有任何故事板segue,那么要回到旧样式,需要将kind属性设置为Present Modally并将Presentation属性设置为Full Screen

enter image description here


2
接口构建器中是否可以设置isModalInPresentation?
布拉德·托马斯

同意这一点。更改最高级别违规视图的Presentation值。
大卫,

这是为我工作的,因为我有performSegue。谢谢亲爱的!
Blasanka

95

在启动屏幕之后的初始视图中,我遇到了这个问题。因为我没有定义顺序或逻辑,所以对我的解决方法是将演示文稿从自动切换到全屏,如下所示:

fix_storyboard_presentation_default_behavior


1
有什么方法可以对所有视图进行编程,而不是通过情节提要一一进行吗?
The1993

@ The1993您找到其他替代方法了吗?
Shobhit Puri

1
@ShobhitPuri在这里查看Omreyh的第一个解决方案stackoverflow.com/a/58255416/4323101
The1993

1
哇,这就是我的问题的答案。谢谢你的提示!对于其他也在调查此问题的人来说,这是从后台重新打开应用程序后解决奇怪行为的方法。在我的应用程序中,从后台打开会将我的初始屏幕(初始视图控制器)作为名片演示样式覆盖,然后任何从此处开始的笔迹都会淡入淡出,而不是使用我定义的笔迹样式。如果我关闭了应用程序(双击主屏幕按钮并向上滑动,然后重新打开),那会很好,但是任何其他启动都会导致这种奇怪的行为。再次感谢!
贾斯汀

91

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

1-覆盖存在

如果您有一个BaseViewController,则可以覆盖该present(_ viewControllerToPresent: 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-从情节提要

选择一个序列并将演示文稿设置为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(_ application: didFinishLaunchingWithOptions)添加以下行:

UIViewController.swizzlePresent()

使用这种方式,您不需要在任何当前调用上进行任何更改,因为我们正在运行时替换当前方法的实现。
如果您想知道什么在泛滥,可以查看以下链接:https : //nshipster.com/swift-objc-runtime/


我的项目中有很多viewControllers,但是没有基类,我不希望您在代码最少的更改上有任何解决方案吗
大师

我使用过毛毛雨,但是我添加了.pageSheet作为条件....如果viewControllerToPresent.modalPresentationStyle == .pageSheet || viewControllerToPresent.modalPresentationStyle == .automatic {viewControllerToPresent.modalPresentationStyle = .fullScreen}
崩溃

状态栏隐藏,当我添加应用的解决方案没有1
加尼甚帕瓦尔

4
混乱是一种行之有效的解决方案。但是,当使用诸如FacebookLogin(今天为5.8)和GoogleSignin之类的外部sdks时,我注意到该方法破坏了这些流程:我们在iPad上出现白屏。这可能是因为他们使用自己混写方法的事实
陶仁阮

39

我在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
}

3
如果您有多个模态,这是最简单的解决方案。
拉伊萨尔·卡利卡特

某处意味着,意味着在哪个文件中或在整个项目中的哪个位置
Yatendra '19

在您的某个地方,我把它放在appdelegate中static let是惰性的,您必须阅读它才能进行计算,惰性var仅计算一次,我用它来确保一次调用会
引起混乱

37

对于Objective-C用户

只需使用此代码

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

或者,如果要在iOS 13.0中添加它,请使用

 if (@available(iOS 13.0, *)) {
     [vc setModalPresentationStyle: UIModalPresentationFullScreen];
 } else {
     // Fallback on earlier versions
 }

2
这同样适用于早期的ios版本,但是答案很好
Catalin19

4
UIModalPresentationFullScreen可与iOS 3.2及更高版本一起使用。因此,如果没有其他条件,则无需添加。
Flame3

2
由于某些原因,仅在iOS 13.1.2中,仅在Obj-c类中,此方法不起作用,而modalPresentationStyle仅显示pageSheet。这会发生在其他人身上吗?
Sevy19年

@ Sevy11我尚未更新至iOS 13.1.2,但可以在13.1
9to5ios

嗨@ Sevy11我已经更新了我的iOS 13.1.2及其工作正常,请在您结尾检查
9to5ios '19

36

一线:

modalPresentationStyle需要被上设置navigationController 正被呈现


iOS 13及以下版本的iOS全屏,overCurrentContext并带有和 navigationController

测试代码

let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)

modalPresentationStyle需要在navigationController处设置。


澄清:从navigationController呈现viewController不需要在navigationController上设置modalPresentationStyle。而是在显示的viewController上设置它。但是,如果要显示navigationController,则应该在navigationController而不是嵌入式viewController上设置'modalPresentationStyle'属性。这种方法适用于iOS 13.3,Xcode 11.3。请参见Yogesh Bharate的答案。
Womble

@Pratik Sodha。有什么帮助吗?stackoverflow.com/questions/61429600/...
大卫·


25

提示:如果您调用当前存在于ViewController内嵌在中的NavigationController,则必须将设置NavigationController.fullScreen而不是VC。

您可以像@davidbates一样执行此操作,也可以以编程方式(例如@pascalbros)来执行。

一个示例场景:

在此处输入图片说明

    //BaseNavigationController: UINavigationController {}
    let baseNavigationController = storyboard!.instantiateViewController(withIdentifier: "BaseNavigationController")
    var navigationController = UINavigationController(rootViewController: baseNavigationController)
    navigationController.modalPresentationStyle = .fullScreen
    navigationController.topViewController as? LoginViewController
    self.present(navigationViewController, animated: true, completion: nil)

19

我需要两者都做:

  1. 将演示文稿样式设置为全屏

    全屏

  2. 将顶部栏设置为半透明导航栏

顶栏


这是一个非常奇怪的假设,即“模拟
量度”

14

modalPresentationStyle演示前进行更改

vc.modalPresentationStyle = UIModalPresentationFullScreen;

13

这是一个简单的解决方案,无需编码一行。

  • 在情节提要中选择视图控制器
  • 选择属性检查器
  • 如下图所示,将演示文稿“自动”设置为“全屏”

此更改使iPad应用程序的行为符合预期,否则新屏幕将在弹出窗口的中央显示。

在此处输入图片说明


我认为这里的关键是您在NavigationController上执行了此操作,这是图片显示的内容,但是文本并未这样说。
安迪·温斯坦

1
还要确保随后的对话是“显示”,而不是“模态呈现”
Andy Weinstein

好的,所以当我使用标签栏控制器控制其他视图时,这一选项似乎对我有用。但是,您需要将实际的“制表符栏控制器”的“演示”设置为“全屏”,因为这会控制所有其他视图。
查理

12

如果您的UITabController的屏幕带有嵌入式导航控制器,则必须将UITabController Presentation设置为FullScreen,如下图所示

在此处输入图片说明


12

这是Objective-C的解决方案

UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:@"ViewController"];

vc.modalPresentationStyle = UIModalPresentationFullScreen;

[self presentViewController:vc animated:YES completion:nil];

10

最新适用于iOS 13和Swift 5.x

let vc = ViewController(nibName: "ViewController", bundle: nil)

vc.modalPresentationStyle = .fullScreen

self.present(vc, animated: true, completion: nil)

8

这是我在ObjectiveC中使用类别修复的版本。使用这种方法,您将拥有默认的UIModalPresentationStyleFullScreen行为,直到另外一个显式设置为止。

#import "UIViewController+Presentation.h"
#import "objc/runtime.h"

@implementation UIViewController (Presentation)

- (void)setModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
    [self setPrivateModalPresentationStyle:modalPresentationStyle];
}

-(UIModalPresentationStyle)modalPresentationStyle {
    UIModalPresentationStyle style = [self privateModalPresentationStyle];
    if (style == NSNotFound) {
        return UIModalPresentationFullScreen;
    }
    return style;
}

- (void)setPrivateModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
    NSNumber *styleNumber = [NSNumber numberWithInteger:modalPresentationStyle];
     objc_setAssociatedObject(self, @selector(privateModalPresentationStyle), styleNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIModalPresentationStyle)privateModalPresentationStyle {
    NSNumber *styleNumber = objc_getAssociatedObject(self, @selector(privateModalPresentationStyle));
    if (styleNumber == nil) {
        return NSNotFound;
    }
    return styleNumber.integerValue;
}

@end

您有.h文件吗?
PedroGóes

@PedroGóes是的,但它仅包含类别声明:```@interface的UIViewController(演示)@end```
亚历山大Kulabukhov

7

所有其他答案都足够了,但是对于像我们这样的大型项目,无论是在代码还是在情节提要中进行导航,这都是一项艰巨的任务。

在此处输入图片说明

对于那些正在积极使用Storyboard的人。这是我的建议:使用Regex。

以下格式不适用于全屏页面:

<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>

以下格式适合全屏页面:

<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>

以下与VS CODE兼容的正则表达式会将所有旧样式页面转换为新样式页面。如果您使用其他正则表达式引擎/文本编辑器,则可能需要转义特殊字符。

搜索正则表达式

<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>

替换正则表达式

<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>

4

最初,默认值是fullscreenmodalPresentationStyle,但在iOS 13中,其对UIModalPresentationStyle.automatic

如果要制作全屏视图控制器,则必须将更modalPresentationStyle改为fullScreen

请参阅UIModalPresentationStyle Apple文档以获取更多详细信息,并参阅Apple人机界面指南以了解应在哪里使用哪种模式。


对于要从​​NavigationController呈现viewController的情况,这是从iOS 13.3,Xcode 11.3开始的正确答案。.overFullScreen的modalPresentationStyle也可以使用。但是,如果要显示navigationController,则需要在navigationController而不是viewController上设置“ modalPresentationStyle”。干杯。
Womble

3

您可以轻松做到这一点,将故事板作为源代码打开并搜索kind="presentation",在所有带有kind = presentation的seague标签中添加一个额外的属性modalPresentationStyle="fullScreen"


2
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)

//如果您要禁用滑动以关闭它,请添加行

Obj.isModalInPresentation = true

检查Apple文档以获取更多信息。


2

我通过使用swizzling(Swift 4.2)方法实现的:

要创建UIViewController扩展,如下所示

extension UIViewController {

    @objc private func swizzled_presentstyle(_ viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?) {

        if #available(iOS 13.0, *) {
            if viewControllerToPresent.modalPresentationStyle == .automatic || viewControllerToPresent.modalPresentationStyle == .pageSheet {
                viewControllerToPresent.modalPresentationStyle = .fullScreen
            }
        }

        self.swizzled_presentstyle(viewControllerToPresent, animated: animated, completion: completion)
    }

     static func setPresentationStyle_fullScreen() {

        let instance: UIViewController = UIViewController()
        let aClass: AnyClass! = object_getClass(instance)

        let originalSelector = #selector(UIViewController.present(_:animated:completion:))
        let swizzledSelector = #selector(UIViewController.swizzled_presentstyle(_:animated:completion:))

        let originalMethod = class_getInstanceMethod(aClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)
        if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
        method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

在AppDelegate中,在application:didFinishLaunchingWithOptions中:通过调用以下代码来调用混乱的代码:

UIViewController.setPresentationStyle_fullScreen()

1

这为我工作:

yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen


3
这不会为所有其他现有答案添加任何内容。
rmaddy19年

1

为UIViewController创建一个类别(例如UIViewController + PresentationStyle)。向其添加以下代码。

 -(UIModalPresentationStyle)modalPresentationStyle{
     return UIModalPresentationStyleFullScreen;
}

这将破坏UISearchController +触发一些难以调试的崩溃
dklt

@dklt这是一个很好的观察。因此,显式设置属性将解决此问题。如果使用UISearchController,没有简单的解决方案。
Govind

0

一种替代方法是在您的应用程序中拥有您自己的基本viewcontroller组件,并仅通过基本设置来实现指定的和必需的初始化程序,如下所示:

class MyBaseViewController: UIViewController {

//MARK: Initialisers

/// Alternative initializer which allows you to set the modal presentation syle
/// - Parameter modalStyle: the presentation style to be used
init(with modalStyle:UIModalPresentationStyle) {
    super.init(nibName: nil, bundle: nil)
    self.setup(modalStyle: modalStyle)
}

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    // default modal presentation style as fullscreen
    self.setup(modalStyle: .fullScreen)
}

required init?(coder: NSCoder) {
    super.init(coder: coder)
    // default modal presentation style as fullscreen
    self.setup(modalStyle: .fullScreen)
}

//MARK: Private

/// Setup the view
///
/// - Parameter modalStyle: indicates which modal presentation style to be used
/// - Parameter modalPresentation: default true, it prevent modally presented view to be dismissible with the default swipe gesture
private func setup(modalStyle:UIModalPresentationStyle, modalPresentation:Bool = true){
    if #available(iOS 13, *) {
        self.modalPresentationStyle = modalStyle
        self.isModalInPresentation = modalPresentation
    }
}

注意:如果视图控制器包含在实际以模态形式显示的导航控制器中,则导航控制器应以相同的方式解决此问题(即,以相同的方式自定义自定义导航控制器组件

在iOS 13.1和iOS 12.4的Xcode 11.1上测试

希望能帮助到你


0

我遇到了视频无法再全屏显示的问题。添加了这一行,它节省了一天:-)

videoController.modalPresentationStyle = UIModalPresentationFullScreen;

2
这不会为所有其他现有答案添加任何内容。
rmaddy19年


0

如果您使用UINavigationController并将ViewController嵌入为根视图控制器,那么您也会遇到同样的问题。使用以下代码来克服。

let vc = UIViewController()
let navController = UINavigationController(rootViewController: vc)
navController.modalPresentationStyle = .fullScreen

0
class MyViewController: UIViewController {

    convenience init() {
        self.init(nibName:nil, bundle:nil)
        self.modalPresentationStyle = .fullScreen
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

self.modalPresentationStyle = .fullScreen可以为UIViewController子类化,而不必在每个视图控制器上调用,而可以MyViewController在任何地方使用。


-1

上面的答案和建议是正确的,下面是另一个版本,并且是通过编程有效使用的方式。

#1创建一个UIView扩展

#2创建一个方法()

//#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)

要么

let vc = MyViewController()
sourceView.presentLocal(vc, animated: true, completion: nil)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.