问题
In iOS 13 Beta 1 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 good fullscreen modal vc?
Thanks
回答1:
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)
回答2:
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.
回答3:
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:
回答4:
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/
回答5:
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
}
回答6:
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).
An example scenario:
//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)
回答7:
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
}
回答8:
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
回答9:
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.
回答10:
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"/>
回答11:
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];
回答12:
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.
回答13:
This worked for me:
yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
回答14:
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.
回答15:
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
回答16:
I had this problem with a video not presenting fullscreen anymore. Added this line, which saved the day :-)
videoController.modalPresentationStyle = UIModalPresentationFullScreen;
回答17:
The simplest solution that worked for me.
viewController.modalPresentationStyle = .fullScreen
回答18:
If you are using a UINavigationController and embed a ViewController as a root view controller, then also you would rise up with same issue. Use following code to overcome.
let vc = UIViewController()
let navController = UINavigationController(rootViewController: vc)
navController.modalPresentationStyle = .fullScreen
回答19:
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.
回答20:
I've had to switch some of my navigation controllers back to fullscreen because of a problem I can't figure out.
If I have two set to automatic, one that calls the other, the second controller appears with the right button against the edge of the screen.
The first controller appears like this:
If I then segue to another NavigationController
with a ViewController
, it looks like this:
Why, in the second ViewController
, is the right button against the edge and not the margin? Also note the reduced height.
If I change the first navigationcontroller to fullscreen, the second one appears correctly.
回答21:
Create a category for UIViewController (say UIViewController+PresentationStyle). Add the following code to it.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
回答22:
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)
来源:https://stackoverflow.com/questions/56435510/presenting-modal-in-ios-13-fullscreen