[英]Presenting modal in iOS 13 fullscreen
在 iOS 13 中,正如 WWDC 2019 期间的平台国情咨文所述,Apple 引入了新的默认卡片展示。 为了强制全屏,您必须明确指定它:
let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen //or .overFullScreen for transparency
self.present(vc, animated: true, completion: nil)
有多种方法可以做到这一点,我认为每种方法都适合一个项目,但不适合另一个项目,所以我想我会把它们留在这里,也许其他人会遇到不同的情况。
如果你有一个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
方法。
extension UIViewController {
func presentInFullScreen(_ viewController: UIViewController,
animated: Bool,
completion: (() -> Void)? = nil) {
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: animated, completion: completion)
}
}
用法:
presentInFullScreen(viewController, animated: true)
let viewController = UIViewController()
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: true, completion: nil)
选择一个 segue 并将演示文稿设置为FullScreen
。
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()
使用这种方式,您无需对任何当前调用进行任何更改,因为我们正在运行时替换当前方法实现。
如果您需要知道什么是 swizzling,可以查看此链接: https : //nshipster.com/swift-objc-runtime/
对于 Objective-C 用户
只需使用此代码
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
或者,如果您想在 iOS 13.0 中添加它,请使用
if (@available(iOS 13.0, *)) {
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
} else {
// Fallback on earlier versions
}
一个班轮:
modalPresentationStyle
需要在正在呈现的导航modalPresentationStyle
上设置。
iOS 13 及以下 iOS 版本 fullScreen,带有
overCurrentContext
和navigationController
测试代码
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)
modalPresentationStyle需要在navigationController 中设置。
我为 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
}
提示:如果您将present 调用到嵌入在NavigationController
的ViewController
,您必须将NavigationController
设置为.fullScreen
而不是VC。
您可以像@davidbates 那样执行此操作,也可以以编程方式(如@pascalbros)执行此操作。
这同样适用于UITabViewController
NavigationController
的示例场景:
//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)
这对我有用
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboardID_cameraview1") as! CameraViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)`
适用于 iOS 13 和 Swift 5.x 的最新版本
let vc = ViewController(nibName: "ViewController", bundle: nil)
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
这是Objective-C的解决方案
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:@"ViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
这是我使用类别在 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
所有其他答案都足够了,但对于像我们这样的大型项目以及在代码和故事板中进行导航的情况,这是一项艰巨的任务。
对于那些积极使用 Storyboard 的人。 这是我的建议:使用正则表达式。
以下格式不适用于全屏页面:
<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"/>
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)
// 如果您想禁用滑动以关闭它,请添加行
Obj.isModalInPresentation = true
查看Apple 文档了解更多信息。
你可以很容易地打开你的故事板作为源代码并搜索kind="presentation"
,在所有带有 kind =presentation 的海格标签中添加一个额外的属性modalPresentationStyle="fullScreen"
我通过使用方法 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: 中调用 swizzling 代码:
UIViewController.setPresentationStyle_fullScreen()
将navigationController.modalPresentationStyle
设置为.fullScreen
在这里已经重复了一千多次,但让我向您展示另一个阻止程序,它导致UIViewController
/ UINavigationController
没有fullscreen
显示,即使所有属性都设置正确。
就我而言,罪魁祸首隐藏在这一行中
navigationController?.presentationController?.delegate = self
显然,在设置UIAdaptivePresentationControllerDelegate
您需要在可选委托方法中指定演示样式
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
presentationStyle
}
为 UIViewController 创建一个类别(比如 UIViewController+PresentationStyle)。 将以下代码添加到其中。
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
另一种方法是在您的应用程序中拥有您自己的基本视图控制器组件,并仅通过基本设置实现指定和必需的初始化程序,如下所示:
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 上测试
希望能帮助到你
覆盖 modalPresentationStyle将修复使用或不使用编码器创建的UIViewControllers的样式。
优点:
坏处:
解决方案:
override var modalPresentationStyle: UIModalPresentationStyle {
get { .fullScreen }
set { }
}
上面的回答和建议是对的,下面是另一个版本,以编程方式使用的有效方法。
#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)
class MyViewController: UIViewController {
convenience init() {
self.init(nibName:nil, bundle:nil)
self.modalPresentationStyle = .fullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
而不是为每个视图控制器调用self.modalPresentationStyle = .fullScreen
,您可以self.modalPresentationStyle = .fullScreen
UIViewController 并在任何地方使用MyViewController
。
在Objective-C中,我找到了两种方法。
我注意到两个枚举modalPresentationStyles都小于零
typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
UIModalPresentationFullScreen = 0,
UIModalPresentationPageSheet API_AVAILABLE(ios(3.2)) API_UNAVAILABLE(tvos),
UIModalPresentationFormSheet API_AVAILABLE(ios(3.2)) API_UNAVAILABLE(tvos),
UIModalPresentationCurrentContext API_AVAILABLE(ios(3.2)),
UIModalPresentationCustom API_AVAILABLE(ios(7.0)),
UIModalPresentationOverFullScreen API_AVAILABLE(ios(8.0)),
UIModalPresentationOverCurrentContext API_AVAILABLE(ios(8.0)),
UIModalPresentationPopover API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos),
UIModalPresentationBlurOverFullScreen API_AVAILABLE(tvos(11.0)) API_UNAVAILABLE(ios) API_UNAVAILABLE(watchos),
UIModalPresentationNone API_AVAILABLE(ios(7.0)) = -1,
UIModalPresentationAutomatic API_AVAILABLE(ios(13.0)) = -2,
};
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
if ([self isKindOfClass:[UINavigationController class]]) {
//default is UIModalPresentationPageSheet
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
else if ([self isKindOfClass:[UITabBarController class]]) {
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
else if (viewControllerToPresent.modalPresentationStyle < 0){
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
[super presentViewController:viewControllerToPresent animated:flag completion:completion];
}
迅速
override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if self as? UINavigationController != nil {
//UINavigationController default is UIModalPresentationPageSheet
viewControllerToPresent.modalPresentationStyle = .fullScreen
}
else if self as? UITabBarController != nil {
viewControllerToPresent.modalPresentationStyle = .fullScreen
}
else if viewControllerToPresent.modalPresentationStyle.rawValue < 0 {
viewControllerToPresent.modalPresentationStyle = .fullScreen
}
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.