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 dismiss the View Controller automatically.
How can I prevent this behaviour and get back to the old fullscreen modal vc?
Thanks
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)
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.
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:
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/
For Objective-C users
Just Use this code
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
Or if you want to add it particular in iOS 13.0 then use
if (#available(iOS 13.0, *)) {
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
} else {
// Fallback on earlier versions
}
As a hint: If you call present to a ViewController which is embedded inside a NavigationController you have to set the NavigationController to .fullScreen and not the VC.
You can do this like #davidbates or you do it programmatically (like #pascalbros).
The same applies to the UITabViewController
An example scenario for 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)
One Liner:
modalPresentationStyle is required to be set on the navigationController which is being presented.
iOS 13 and below iOS version fullScreen with overCurrentContext and
navigationController
Tested Code
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)
modalPresentationStyle require to set at navigationController.
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
}
This worked for me
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboardID_cameraview1") as! CameraViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)`
Latest for iOS 13 and Swift 5.x
let vc = ViewController(nibName: "ViewController", bundle: nil)
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
I needed to do both:
Set presentation style as Full screen
Set Top bar as Translucent Navigation Bar
Quick solution. There are already really great answers above. I am also adding my quick 2 points input, which is presented in the screenshot.
If you are not using Navigation Controller then from Right Menu Inspector set the Presentation to Full Screen
If you are using Navigation Controller then by default it will present full screen, you have to do nothing.
Here is an easy solution without coding a single line.
Select View Controller in Storyboard
Select attribute Inspector
Set presentation "Automatic" to "FullScreen" as per below image
This change makes iPad app behavior as expected otherwise the new screen is displaying in the center of the screen as a popup.
Here is the solution for Objective-C
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:#"ViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
If you have a UITabController with Screens with Embeded Navigation Controllers, you have to set the UITabController Presentation to FullScreen as shown in pic below
Here's my version of fix in ObjectiveC using Categories. With this approach you'll have default UIModalPresentationStyleFullScreen behaviour until another one explicitly set.
#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
All the other answers are sufficient but for a large project like ours and where navigations are being made both in code and storyboard, it is quite a daunting task.
For those who are actively using Storyboard. This is my advice: use Regex.
The following format is not good for full screen pages:
<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>
The following format is good for full screen pages:
<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>
The following regex compatible with VS CODE will convert all Old Style pages to new style pages. You may need to escape special chars if you're using other regex engines/text editors.
Search Regex
<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>
Replace Regex
<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>
Setting navigationController.modalPresentationStyle to .fullScreen has been repeated here more than a thousand times but let me present you another blocker which was causing that UIViewController / UINavigationController was not showing in fullscreen even though all the properties were set properly.
In my case the culprit was hidden in this line
navigationController?.presentationController?.delegate = self
Apparently when setting UIAdaptivePresentationControllerDelegate you need to specify the presentation style within the optional delegate method
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
presentationStyle
}
Initially, the default value is fullscreen for modalPresentationStyle, but in iOS 13 its changes to the UIModalPresentationStyle.automatic.
If you want to make the full-screen view controller you have to change the modalPresentationStyle to fullScreen.
Refer UIModalPresentationStyle apple documentation for more details and refer apple human interface guidelines for where should use which modality.
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)
// if you want to disable swipe to dismiss on it, add line
Obj.isModalInPresentation = true
Check Apple Document for More info.
You can easily do so
Open your storyboard as source code and search for kind="presentation", in all the seague tag with kind = presentation add a extra attribute modalPresentationStyle="fullScreen"
I achieved it by using method swizzling(Swift 4.2):
To create an UIViewController extension as follows
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)
}
}
}
and in AppDelegate, in application:didFinishLaunchingWithOptions: invoke the swizzling code by calling:
UIViewController.setPresentationStyle_fullScreen()
Create a category for UIViewController (say UIViewController+PresentationStyle). Add the following code to it.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
an alternative approach is to have your own base viewcontroller component in your app, and just implementing the designated and required initialisers with a basic setup, something like the following:
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
}
}
NOTE: If your view controller is contained in a navigation controller which is actually presented modally, then the navigation controller should approach the problem in the same way (meaning, having your custom navigation controller component customised in the same way
Tested on Xcode 11.1 on iOS 13.1 and iOS 12.4
Hope it helps
override modalPresentationStyle will fix the style for UIViewControllers created with or without a coder.
Advantages:
Single place where you set it.
No need to know which init or awake method it should be set
Disadvantage:
You can't change it from outside like Interface builder or configuration from code
Solution:
override var modalPresentationStyle: UIModalPresentationStyle {
get { .fullScreen }
set { }
}
The above answers and suggestions are right, below is another version, and efficient way using programmatically.
#1 Created a UIView Extension
#2 Created a Method ()
//#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)
}
}
Invoking as below
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)
class MyViewController: UIViewController {
convenience init() {
self.init(nibName:nil, bundle:nil)
self.modalPresentationStyle = .fullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Rather than call self.modalPresentationStyle = .fullScreen for every view controller, you can subclass UIViewController and just use MyViewController everywhere.
Related
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 dismiss the View Controller automatically.
How can I prevent this behaviour and get back to the old fullscreen modal vc?
Thanks
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)
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.
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:
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/
For Objective-C users
Just Use this code
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
Or if you want to add it particular in iOS 13.0 then use
if (#available(iOS 13.0, *)) {
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
} else {
// Fallback on earlier versions
}
As a hint: If you call present to a ViewController which is embedded inside a NavigationController you have to set the NavigationController to .fullScreen and not the VC.
You can do this like #davidbates or you do it programmatically (like #pascalbros).
The same applies to the UITabViewController
An example scenario for 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)
One Liner:
modalPresentationStyle is required to be set on the navigationController which is being presented.
iOS 13 and below iOS version fullScreen with overCurrentContext and
navigationController
Tested Code
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)
modalPresentationStyle require to set at navigationController.
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
}
This worked for me
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboardID_cameraview1") as! CameraViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)`
Latest for iOS 13 and Swift 5.x
let vc = ViewController(nibName: "ViewController", bundle: nil)
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
I needed to do both:
Set presentation style as Full screen
Set Top bar as Translucent Navigation Bar
Quick solution. There are already really great answers above. I am also adding my quick 2 points input, which is presented in the screenshot.
If you are not using Navigation Controller then from Right Menu Inspector set the Presentation to Full Screen
If you are using Navigation Controller then by default it will present full screen, you have to do nothing.
Here is an easy solution without coding a single line.
Select View Controller in Storyboard
Select attribute Inspector
Set presentation "Automatic" to "FullScreen" as per below image
This change makes iPad app behavior as expected otherwise the new screen is displaying in the center of the screen as a popup.
Here is the solution for Objective-C
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:#"ViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
If you have a UITabController with Screens with Embeded Navigation Controllers, you have to set the UITabController Presentation to FullScreen as shown in pic below
Here's my version of fix in ObjectiveC using Categories. With this approach you'll have default UIModalPresentationStyleFullScreen behaviour until another one explicitly set.
#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
All the other answers are sufficient but for a large project like ours and where navigations are being made both in code and storyboard, it is quite a daunting task.
For those who are actively using Storyboard. This is my advice: use Regex.
The following format is not good for full screen pages:
<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>
The following format is good for full screen pages:
<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>
The following regex compatible with VS CODE will convert all Old Style pages to new style pages. You may need to escape special chars if you're using other regex engines/text editors.
Search Regex
<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>
Replace Regex
<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>
Setting navigationController.modalPresentationStyle to .fullScreen has been repeated here more than a thousand times but let me present you another blocker which was causing that UIViewController / UINavigationController was not showing in fullscreen even though all the properties were set properly.
In my case the culprit was hidden in this line
navigationController?.presentationController?.delegate = self
Apparently when setting UIAdaptivePresentationControllerDelegate you need to specify the presentation style within the optional delegate method
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
presentationStyle
}
Initially, the default value is fullscreen for modalPresentationStyle, but in iOS 13 its changes to the UIModalPresentationStyle.automatic.
If you want to make the full-screen view controller you have to change the modalPresentationStyle to fullScreen.
Refer UIModalPresentationStyle apple documentation for more details and refer apple human interface guidelines for where should use which modality.
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)
// if you want to disable swipe to dismiss on it, add line
Obj.isModalInPresentation = true
Check Apple Document for More info.
You can easily do so
Open your storyboard as source code and search for kind="presentation", in all the seague tag with kind = presentation add a extra attribute modalPresentationStyle="fullScreen"
I achieved it by using method swizzling(Swift 4.2):
To create an UIViewController extension as follows
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)
}
}
}
and in AppDelegate, in application:didFinishLaunchingWithOptions: invoke the swizzling code by calling:
UIViewController.setPresentationStyle_fullScreen()
Create a category for UIViewController (say UIViewController+PresentationStyle). Add the following code to it.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
an alternative approach is to have your own base viewcontroller component in your app, and just implementing the designated and required initialisers with a basic setup, something like the following:
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
}
}
NOTE: If your view controller is contained in a navigation controller which is actually presented modally, then the navigation controller should approach the problem in the same way (meaning, having your custom navigation controller component customised in the same way
Tested on Xcode 11.1 on iOS 13.1 and iOS 12.4
Hope it helps
override modalPresentationStyle will fix the style for UIViewControllers created with or without a coder.
Advantages:
Single place where you set it.
No need to know which init or awake method it should be set
Disadvantage:
You can't change it from outside like Interface builder or configuration from code
Solution:
override var modalPresentationStyle: UIModalPresentationStyle {
get { .fullScreen }
set { }
}
The above answers and suggestions are right, below is another version, and efficient way using programmatically.
#1 Created a UIView Extension
#2 Created a Method ()
//#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)
}
}
Invoking as below
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)
class MyViewController: UIViewController {
convenience init() {
self.init(nibName:nil, bundle:nil)
self.modalPresentationStyle = .fullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Rather than call self.modalPresentationStyle = .fullScreen for every view controller, you can subclass UIViewController and just use MyViewController everywhere.
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 dismiss the View Controller automatically.
How can I prevent this behaviour and get back to the old fullscreen modal vc?
Thanks
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)
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.
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:
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/
For Objective-C users
Just Use this code
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
Or if you want to add it particular in iOS 13.0 then use
if (#available(iOS 13.0, *)) {
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
} else {
// Fallback on earlier versions
}
As a hint: If you call present to a ViewController which is embedded inside a NavigationController you have to set the NavigationController to .fullScreen and not the VC.
You can do this like #davidbates or you do it programmatically (like #pascalbros).
The same applies to the UITabViewController
An example scenario for 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)
One Liner:
modalPresentationStyle is required to be set on the navigationController which is being presented.
iOS 13 and below iOS version fullScreen with overCurrentContext and
navigationController
Tested Code
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)
modalPresentationStyle require to set at navigationController.
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
}
This worked for me
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboardID_cameraview1") as! CameraViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)`
Latest for iOS 13 and Swift 5.x
let vc = ViewController(nibName: "ViewController", bundle: nil)
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
I needed to do both:
Set presentation style as Full screen
Set Top bar as Translucent Navigation Bar
Quick solution. There are already really great answers above. I am also adding my quick 2 points input, which is presented in the screenshot.
If you are not using Navigation Controller then from Right Menu Inspector set the Presentation to Full Screen
If you are using Navigation Controller then by default it will present full screen, you have to do nothing.
Here is an easy solution without coding a single line.
Select View Controller in Storyboard
Select attribute Inspector
Set presentation "Automatic" to "FullScreen" as per below image
This change makes iPad app behavior as expected otherwise the new screen is displaying in the center of the screen as a popup.
Here is the solution for Objective-C
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:#"ViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
If you have a UITabController with Screens with Embeded Navigation Controllers, you have to set the UITabController Presentation to FullScreen as shown in pic below
Here's my version of fix in ObjectiveC using Categories. With this approach you'll have default UIModalPresentationStyleFullScreen behaviour until another one explicitly set.
#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
All the other answers are sufficient but for a large project like ours and where navigations are being made both in code and storyboard, it is quite a daunting task.
For those who are actively using Storyboard. This is my advice: use Regex.
The following format is not good for full screen pages:
<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>
The following format is good for full screen pages:
<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>
The following regex compatible with VS CODE will convert all Old Style pages to new style pages. You may need to escape special chars if you're using other regex engines/text editors.
Search Regex
<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>
Replace Regex
<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>
Setting navigationController.modalPresentationStyle to .fullScreen has been repeated here more than a thousand times but let me present you another blocker which was causing that UIViewController / UINavigationController was not showing in fullscreen even though all the properties were set properly.
In my case the culprit was hidden in this line
navigationController?.presentationController?.delegate = self
Apparently when setting UIAdaptivePresentationControllerDelegate you need to specify the presentation style within the optional delegate method
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
presentationStyle
}
Initially, the default value is fullscreen for modalPresentationStyle, but in iOS 13 its changes to the UIModalPresentationStyle.automatic.
If you want to make the full-screen view controller you have to change the modalPresentationStyle to fullScreen.
Refer UIModalPresentationStyle apple documentation for more details and refer apple human interface guidelines for where should use which modality.
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)
// if you want to disable swipe to dismiss on it, add line
Obj.isModalInPresentation = true
Check Apple Document for More info.
You can easily do so
Open your storyboard as source code and search for kind="presentation", in all the seague tag with kind = presentation add a extra attribute modalPresentationStyle="fullScreen"
I achieved it by using method swizzling(Swift 4.2):
To create an UIViewController extension as follows
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)
}
}
}
and in AppDelegate, in application:didFinishLaunchingWithOptions: invoke the swizzling code by calling:
UIViewController.setPresentationStyle_fullScreen()
Create a category for UIViewController (say UIViewController+PresentationStyle). Add the following code to it.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
an alternative approach is to have your own base viewcontroller component in your app, and just implementing the designated and required initialisers with a basic setup, something like the following:
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
}
}
NOTE: If your view controller is contained in a navigation controller which is actually presented modally, then the navigation controller should approach the problem in the same way (meaning, having your custom navigation controller component customised in the same way
Tested on Xcode 11.1 on iOS 13.1 and iOS 12.4
Hope it helps
override modalPresentationStyle will fix the style for UIViewControllers created with or without a coder.
Advantages:
Single place where you set it.
No need to know which init or awake method it should be set
Disadvantage:
You can't change it from outside like Interface builder or configuration from code
Solution:
override var modalPresentationStyle: UIModalPresentationStyle {
get { .fullScreen }
set { }
}
The above answers and suggestions are right, below is another version, and efficient way using programmatically.
#1 Created a UIView Extension
#2 Created a Method ()
//#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)
}
}
Invoking as below
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)
class MyViewController: UIViewController {
convenience init() {
self.init(nibName:nil, bundle:nil)
self.modalPresentationStyle = .fullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Rather than call self.modalPresentationStyle = .fullScreen for every view controller, you can subclass UIViewController and just use MyViewController everywhere.
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 dismiss the View Controller automatically.
How can I prevent this behaviour and get back to the old fullscreen modal vc?
Thanks
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)
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.
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:
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/
For Objective-C users
Just Use this code
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
Or if you want to add it particular in iOS 13.0 then use
if (#available(iOS 13.0, *)) {
[vc setModalPresentationStyle: UIModalPresentationFullScreen];
} else {
// Fallback on earlier versions
}
As a hint: If you call present to a ViewController which is embedded inside a NavigationController you have to set the NavigationController to .fullScreen and not the VC.
You can do this like #davidbates or you do it programmatically (like #pascalbros).
The same applies to the UITabViewController
An example scenario for 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)
One Liner:
modalPresentationStyle is required to be set on the navigationController which is being presented.
iOS 13 and below iOS version fullScreen with overCurrentContext and
navigationController
Tested Code
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)
modalPresentationStyle require to set at navigationController.
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
}
This worked for me
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboardID_cameraview1") as! CameraViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)`
Latest for iOS 13 and Swift 5.x
let vc = ViewController(nibName: "ViewController", bundle: nil)
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
I needed to do both:
Set presentation style as Full screen
Set Top bar as Translucent Navigation Bar
Quick solution. There are already really great answers above. I am also adding my quick 2 points input, which is presented in the screenshot.
If you are not using Navigation Controller then from Right Menu Inspector set the Presentation to Full Screen
If you are using Navigation Controller then by default it will present full screen, you have to do nothing.
Here is an easy solution without coding a single line.
Select View Controller in Storyboard
Select attribute Inspector
Set presentation "Automatic" to "FullScreen" as per below image
This change makes iPad app behavior as expected otherwise the new screen is displaying in the center of the screen as a popup.
Here is the solution for Objective-C
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:#"ViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
If you have a UITabController with Screens with Embeded Navigation Controllers, you have to set the UITabController Presentation to FullScreen as shown in pic below
Here's my version of fix in ObjectiveC using Categories. With this approach you'll have default UIModalPresentationStyleFullScreen behaviour until another one explicitly set.
#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
All the other answers are sufficient but for a large project like ours and where navigations are being made both in code and storyboard, it is quite a daunting task.
For those who are actively using Storyboard. This is my advice: use Regex.
The following format is not good for full screen pages:
<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>
The following format is good for full screen pages:
<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>
The following regex compatible with VS CODE will convert all Old Style pages to new style pages. You may need to escape special chars if you're using other regex engines/text editors.
Search Regex
<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>
Replace Regex
<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>
Setting navigationController.modalPresentationStyle to .fullScreen has been repeated here more than a thousand times but let me present you another blocker which was causing that UIViewController / UINavigationController was not showing in fullscreen even though all the properties were set properly.
In my case the culprit was hidden in this line
navigationController?.presentationController?.delegate = self
Apparently when setting UIAdaptivePresentationControllerDelegate you need to specify the presentation style within the optional delegate method
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
presentationStyle
}
Initially, the default value is fullscreen for modalPresentationStyle, but in iOS 13 its changes to the UIModalPresentationStyle.automatic.
If you want to make the full-screen view controller you have to change the modalPresentationStyle to fullScreen.
Refer UIModalPresentationStyle apple documentation for more details and refer apple human interface guidelines for where should use which modality.
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)
// if you want to disable swipe to dismiss on it, add line
Obj.isModalInPresentation = true
Check Apple Document for More info.
You can easily do so
Open your storyboard as source code and search for kind="presentation", in all the seague tag with kind = presentation add a extra attribute modalPresentationStyle="fullScreen"
I achieved it by using method swizzling(Swift 4.2):
To create an UIViewController extension as follows
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)
}
}
}
and in AppDelegate, in application:didFinishLaunchingWithOptions: invoke the swizzling code by calling:
UIViewController.setPresentationStyle_fullScreen()
Create a category for UIViewController (say UIViewController+PresentationStyle). Add the following code to it.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
an alternative approach is to have your own base viewcontroller component in your app, and just implementing the designated and required initialisers with a basic setup, something like the following:
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
}
}
NOTE: If your view controller is contained in a navigation controller which is actually presented modally, then the navigation controller should approach the problem in the same way (meaning, having your custom navigation controller component customised in the same way
Tested on Xcode 11.1 on iOS 13.1 and iOS 12.4
Hope it helps
override modalPresentationStyle will fix the style for UIViewControllers created with or without a coder.
Advantages:
Single place where you set it.
No need to know which init or awake method it should be set
Disadvantage:
You can't change it from outside like Interface builder or configuration from code
Solution:
override var modalPresentationStyle: UIModalPresentationStyle {
get { .fullScreen }
set { }
}
The above answers and suggestions are right, below is another version, and efficient way using programmatically.
#1 Created a UIView Extension
#2 Created a Method ()
//#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)
}
}
Invoking as below
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)
class MyViewController: UIViewController {
convenience init() {
self.init(nibName:nil, bundle:nil)
self.modalPresentationStyle = .fullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Rather than call self.modalPresentationStyle = .fullScreen for every view controller, you can subclass UIViewController and just use MyViewController everywhere.
I know how to present a popover from a bar button item as is described in this answer (for both iPhone and iPad).
I would like to add a popover for an arbitrary anchor point. The other SO answers that I saw were for bar button items or in Objective-C.
I just learned how to do this, so I am adding my own answer below.
Updated for Swift 3
In the storyboard, add a view controller that you would like to be the popover. Set the Storyboard ID to be "popoverId".
Also add a button to your main view controller and hook up the IBAction to the following code.
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBAction func buttonTap(sender: UIButton) {
// get a reference to the view controller for the popover
let popController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "popoverId")
// set the presentation style
popController.modalPresentationStyle = UIModalPresentationStyle.popover
// set up the popover presentation controller
popController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up
popController.popoverPresentationController?.delegate = self
popController.popoverPresentationController?.sourceView = sender // button
popController.popoverPresentationController?.sourceRect = sender.bounds
// present the popover
self.present(popController, animated: true, completion: nil)
}
// UIPopoverPresentationControllerDelegate method
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
// Force popover style
return UIModalPresentationStyle.none
}
}
Setting the sourceView and sourceRect is what allows you to choose an arbitrary point to display the popover.
That's it. Now it should like something like this when the button is tapped.
Thanks to this article for help.
Solution for Swift 3.1 :
Add to your ViewController UIPopoverPresentationControllerDelegate delegate :
class OriginalViewController: UIViewController, UIPopoverPresentationControllerDelegate
Add a button to your ViewController and on tap on your button, call this code :
let controller = MyPopViewController()
controller.modalPresentationStyle = UIModalPresentationStyle.popover
let popController = controller.popoverPresentationController
popController?.permittedArrowDirections = .any
popController?.delegate = self
popController?.sourceRect = (self.myButton?.bounds)!
popController?.sourceView = self.myButton
self.present(controller, animated: true, completion: nil)
Update to func syntax above:
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentatinStyle {
return .none
}
For some reason the old syntax is still allowed but is not active and will not implement popup or anchor correctly.
I'm trying to programmatically present a view via an adaptive popover (e.g. in a popover on an iPad, full screen on an iPhone). In order to be able to dismiss the presented view controller on the iPhone, I've tried wrapping it in a navigation controller as in https://stackoverflow.com/a/29956631/5061277 or the nice example here: https://github.com/shinobicontrols/iOS8-day-by-day/tree/master/21-alerts-and-popovers/AppAlert, which looks like:
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBAction func handlePopoverPressed(sender: UIView) {
let popoverVC = storyboard?.instantiateViewControllerWithIdentifier("codePopover") as! UIViewController
popoverVC.modalPresentationStyle = .Popover
// Present it before configuring it
presentViewController(popoverVC, animated: true, completion: nil)
// Now the popoverPresentationController has been created
if let popoverController = popoverVC.popoverPresentationController {
popoverController.sourceView = sender
popoverController.sourceRect = sender.bounds
popoverController.permittedArrowDirections = .Any
popoverController.delegate = self
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
// This line _IS_ reached in the debugger
NSLog("Delagate asked for presentation style");
return .FullScreen
}
func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
// This line _IS_NOT_ reached in the debugger
NSLog("Delegate asked for view controller");
return UINavigationController(rootViewController: controller.presentedViewController)
}
}
While the adaptivePresentationStyleForPresentationController delegate method is called, the presentationController viewControllerForAdaptivePresentationStyle delegate method is not called. As a result there is no way to dismiss the presented controller on an iPhone.
I've tried XCode 6.4 and 7.0b2 on iOS 8.1 through 8.4, both in the simulator and on a device, and in no case is my delegate asked for viewControllerForAdaptivePresentationStyle. Why? Is there a build setting I should be looking at, or could having XCode 7 installed be changing something? This exact code is presented as working in the links above.
You need to present it after configuring it. Alternatively use a segue to make it much easier.
You need to add this method:
-(UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection{
return UIModalPresentationOverFullScreen;
}
The trick is that you must set the
presentation controller’s delegate before calling present(_:animated:completion:);
otherwise, the adaptive presentation delegate methods won’t be called:
let vc = MyViewController()
vc.modalPresentationStyle = .popover
if let pop = vc.popoverPresentationController {
pop.delegate = self // *
}
self.present(vc, animated: true)