iOS 13フルスクリーンでモーダルを表示する


465

iOS 13では、モーダルビューコントローラーが表示されるときの新しい動作があります。

これで、デフォルトでは全画面表示にならず、下にスライドしようとすると、アプリはView Controllerを自動的に閉じます。

この動作を防ぎ、古いフルスクリーンモーダルVCに戻すにはどうすればよいですか?

モーダル動作

ありがとう

回答:


622

iOS 13では、WWDC 2019 のPlatforms State of the Unionで述べられているように、Appleは新しいデフォルトのカードプレゼンテーションを導入しました。全画面表示を強制するには、次のように明示的に指定する必要があります。

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

81
これはデフォルトとして長くは続かないと思います。fullScreenUIの変更を壊さないようにするには、このオプションをデフォルトにする必要があります。
JakubTruhlář19年

17
私はそれを当てにしません。以前は、Appleはデフォルトを変更して、現在のSDKにリンクした後にのみアクティブになることがよくありました。以前のバージョンに対してリンクすると、古い動作が得られると期待しています。
DrMickeyLauer

11
Xcode-10でビルドされたアプリがiOS 13シミュレーターで実行されても、デフォルトでフルスクリーンになることが確認できます。@DrMickeyLauerが言ったように、Xcode 11でビルドすると、アプリは新しい動作にオプトインします。isModalInPresentationスワイプジェスチャーが消えないようにブロックするために使用します。詳細については、私のブログ投稿を参照してください。medium.com
Geoff Hackworth

5
.overFullScreenではなく.fullScreenを使用することをお勧めします。.fullScreenはviewWillAppearおよびviewDidAppearを起動します。.overFullScreenはそれを行いません
PiterPan

5
時間が経過し、.automaticスタイルはデフォルトのスタイルに落ち着きました。これは(ほとんどのビューコントローラの).pageSheetスタイルです。ただし、システムビューコントローラーによっては、それを別のスタイルにマップする場合があります。
JakubTruhlář19年

186

誰かに役立つ情報を追加します。ストーリーボードセグエがある場合、古いスタイルに戻すには、kindプロパティをPresent Modallyに、PresentationプロパティをFull Screenに設定する必要があります。

ここに画像の説明を入力してください


2
インターフェイスビルダーでisModalInPresentationを設定する方法はありますか?
Brad Thomas、

これに同意します。最高レベルの問題のあるビューのプレゼンテーション値を変更します。
デビッド

これは私が持っているので私のために働いたものですperformSegue。親愛なるありがとう!
ブラサンカ

95

起動画面の直後の初期ビューにこの問題がありました。セグエやロジックが定義されていなかったための修正は、次のようにプレゼンテーションを自動からフルスクリーンに切り替えることでした。

fix_storyboard_presentation_default_behavior


2
ストーリーボードで1つずつではなく、すべてのビューに対してプログラムでこれを行う方法はありますか?
The1993

@ The1993あなたはそれに代わるものを見つけましたか?
Shobhit Puri

1
@ShobhitPuri持ってここにOmreyhことにより、第一の溶液を見stackoverflow.com/a/58255416/4323101
The1993

1
うわー、これが私の問題の答えでした。先端をありがとう!同様にこれを調べている他の人にとって、これはバックグラウンドからアプリを再度開いた後の奇妙な動作の修正です。私のアプリケーションでは、バックグラウンドから開くと、スプラッシュスクリーン(初期ビューコントローラー)がカードプレゼンテーションスタイルとしてオーバーレイされ、定義済みのセグエスタイルを使用する代わりに、今後のセグエがクロスフェードします。アプリを閉じても問題ありません(ホームボタンをダブルタップして上にスワイプしてから再度開く)。ただし、追加の起動によってこの奇妙な動作が発生します。再度、感謝します!
ジャスティン

92

これには複数の方法があり、それぞれが1つのプロジェクトに適合することはできるが、別のプロジェクトには適合しないと思うので、ここに置いておこうと思います。

1-現在のオーバーライド

を持っているBaseViewController場合は、present(_ viewControllerToPresent: animated flag: completion:)メソッドをオーバーライドできます。

class BaseViewController: UIViewController {

  // ....

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

  // ....
}

この方法を使用するpresentと、presentメソッドをオーバーライドするだけなので、呼び出しを変更する必要はありません。

2-拡張:

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

使用法:

presentInFullScreen(viewController, animated: true)

3- 1つのUIViewController

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

4-ストーリーボードから

セグエを選択し、プレゼンテーションをに設定しFullScreenます。
ここに画像の説明を入力してください

5-スウィズリング

extension UIViewController {

  static func swizzlePresent() {

    let orginalSelector = #selector(present(_: animated: completion:))
    let swizzledSelector = #selector(swizzledPresent)

    guard let orginalMethod = class_getInstanceMethod(self, orginalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else{return}

    let didAddMethod = class_addMethod(self,
                                       orginalSelector,
                                       method_getImplementation(swizzledMethod),
                                       method_getTypeEncoding(swizzledMethod))

    if didAddMethod {
      class_replaceMethod(self,
                          swizzledSelector,
                          method_getImplementation(orginalMethod),
                          method_getTypeEncoding(orginalMethod))
    } else {
      method_exchangeImplementations(orginalMethod, swizzledMethod)
    }

  }

  @objc
  private func swizzledPresent(_ viewControllerToPresent: UIViewController,
                               animated flag: Bool,
                               completion: (() -> Void)? = nil) {
    if #available(iOS 13.0, *) {
      if viewControllerToPresent.modalPresentationStyle == .automatic {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
      }
    }
    swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
   }
}

使用法:
あなたの中AppDelegateの内側には、application(_ application: didFinishLaunchingWithOptions)この行を追加します。

UIViewController.swizzlePresent()

この方法を使用すると、現在のメソッド実装をランタイムで置き換えるので、現在の呼び出しを変更する必要はありません。
何がスウィズリングしているかを知る必要がある場合は、次のリンクを確認できます。https//nshipster.com/swift-objc-runtime/


私のプロジェクトには多くのviewControllersがありますが、基本クラスがありません。スウィズルしたくないので、コードの変更を最小限に抑えて解決策はありますか
グル

スウィズリングを使用しましたが、条件に.pageSheetを追加しました... if viewControllerToPresent.modalPresentationStyle == .pageSheet || viewControllerToPresent.modalPresentationStyle == .automatic {viewControllerToPresent.modalPresentationStyle = .fullScreen}
クラッシュ

適用したソリューション1を追加するとステータスバーが非表示になる
Ganesh Pawar

4
スウィズリングは、しばらくの間うまくいったソリューションです。ただし、FacebookLogin(現在の5.8)やGoogleSigninなどの一部の外部SDKを使用すると、このメソッドがこれらのフローを壊すことに気付きました。iPadで白い画面が表示されます。これはおそらく、彼らが独自のスウィズリング方式を使用しているという事実によるものです
Tao-Nhan Nguyen

39

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

次にこれを入れて

UIViewController.preventPageSheetPresentation

どこかに

たとえばAppDelegate

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

    UIViewController.preventPageSheetPresentation
    // ...
    return true
}

3
複数のモーダルがある場合、最も簡単なソリューションです。
Raisal Calicut

どこかが意味するのは、どのファイルまたはプロジェクト全体のどこか
Yatendra

どこかで、私はそれをappdelegateに入れます static letが怠惰です、それを読み取って計算させるには、遅延varを1回だけ計算します。スウィズリングが1回呼び出されることを保証するために使用します
Maxime Ashurov

37

Objective-Cユーザー向け

このコードを使用するだけ

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

または、iOS 13.0で追加したい場合は、

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

2
これは以前のiosバージョンでも機能しますが、答えは適切です
カタリン

4
UIModalPresentationFullScreenはiOS 3.2以降で動作します。だから、もし他に条件があれば追加する必要はありません。
flame3

2
なんらかの理由でiOS 13.1.2ではObj-cクラスでのみこれは機能せず、modalPresentationStyleはpageSheetのみを表示しています。これは他の人にも起こりますか?
Sevy11、19年

@ Sevy11私はiOS 13.1.2に更新していませんが、13.1で正常に動作しています
9to5ios

こんにちは@ Sevy11私はiOS 13.1.2を更新しましたが、正常に動作しています。最後に確認してください
9to5ios

36

一発ギャグ:

modalPresentationStyle提示されているnavigationController 設定する必要があります。


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で設定する必要があります。


明確化:navigationControllerからviewControllerを表示する場合、navigationControllerでmodalPresentationStyleを設定する必要はありません。代わりに、表示されるviewControllerに設定されます。ただし、navigationControllerを提示する場合は、埋め込まれたviewControllerではなく、navigationControllerに 'modalPresentationStyle'プロパティを設定する必要があります。このアプローチはiOS 13.3、Xcode 11.3で機能します。Yogesh Bharateの回答を参照してください。
Womble

@Pratik Sodha。これについて何か助けはありますか?stackoverflow.com/questions/61429600/...
デヴィッド・


25

ヒントとして:あなたが現在呼び出した場合ViewControllerの内部に埋め込まれているNavigationControllerあなたは、設定する必要がNavigationController.fullScreen VC及びません。

@davidbatesのようにこれを行うか、(@ pascalbrosのように)プログラムで行うことができます。

シナリオ例:

ここに画像の説明を入力してください

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

21

私は両方をする必要がありました:

  1. プレゼンテーションスタイルを全画面に設定

    全画面表示

  2. トップバーを半透明のナビゲーションバーとして設定する

トップバー


Simulated Metricsのいくつかのオプションがプレゼンテーションスタイルを変更するのに役立つというのは非常に奇妙な仮定です
Alexander Kulabukhov

14

modalPresentationStyle提示する前に変更

vc.modalPresentationStyle = UIModalPresentationFullScreen;

13

これは、単一の行をコーディングせずに簡単な解決策です。

  • ストーリーボードでビューコントローラーを選択
  • 属性インスペクターを選択
  • 下の画像のように、プレゼンテーション「自動」を「フルスクリーン」に設定します

この変更により、iPadアプリは予想どおりに動作します。そうでない場合、新しい画面は画面の中央にポップアップとして表示されます。

ここに画像の説明を入力してください


ここで重要なのは、NavigationControllerでこれを実行したことだと思います。これは図が示すものですが、テキストはこのように言っていません。
アンディワインスタイン

1
また、後続のセグエが「現在のモーダル」ではなく「表示」であることを確認してください
Andy Weinstein

わかりました。タブバーコントローラーを使用して他のビューを制御すると、これはうまくいくようです。ただし、他のすべてのビューを制御するため、実際の「タブバーコントローラー」のプレゼンテーションをフルスクリーンに設定する必要があります。
チャーリー

12

埋め込みナビゲーションコントローラーを備えた画面を備えたUITabControllerがある場合は、下の図に示すように、UITabController プレゼンテーションをFullScreen に設定する必要があります。

ここに画像の説明を入力してください


12

これがObjective-Cのソリューションです

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

vc.modalPresentationStyle = UIModalPresentationFullScreen;

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


9

これは、カテゴリを使用したObjectiveCでの修正バージョンです。このアプローチでは、別のアクションが明示的に設定されるまで、デフォルトのUIModalPresentationStyleFullScreenの動作になります。

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

@implementation UIViewController (Presentation)

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

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

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

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

@end

.hファイルはありますか?
ペドロゴエス

@PedroGóesはい、しかしそれはカテゴリ宣言のみで構成されています: `` `@interface UIViewController(Presentation)@end` ``
Alexander Kulabukhov

ストーリーボードのすべてのコントローラーを処理したくない場合は、これを使用してください!時間節約、ありがとう!
AnthoPak

7

他のすべての答えで十分ですが、私たちのような大規模なプロジェクトで、コードとストーリーボードの両方でナビゲーションが行われている場合、それは非常に困難な作業です。

ここに画像の説明を入力してください

Storyboardを積極的に使用している人向け。これは私のアドバイスです:Regexを使用してください。

次の形式は、フルスクリーンページには適していません。

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

次の形式は、全画面ページに適しています。

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

VS CODEと互換性のある次の正規表現は、すべての古いスタイルのページを新しいスタイルのページに変換します。他の正規表現エンジン/テキストエディタを使用している場合は、特殊文字をエスケープする必要がある場合があります。

正規表現を検索

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

正規表現を置き換える

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

4

最初は、デフォルト値はfullscreenmodalPresentationStyleですが、iOS 13ではに変更されていUIModalPresentationStyle.automaticます。

フルスクリーンのビューコントローラを作成する場合は、をに変更するmodalPresentationStyle必要がありfullScreenます。

詳細についてはUIModalPresentationStyle アップルのドキュメントを参照してください。また、モダリティをどこで使用するかについては、アップルのヒューマンインターフェイスガイドラインを参照してください。


これは、iOS 13.3、Xcode 11.3での正しい答えです。navigationControllerからviewControllerを提示する場合に適しています。.overFullScreenのmodalPresentationStyleも機能します。ただし、navigationControllerを提示する場合は、viewControllerではなく、navigationControllerに「modalPresentationStyle」を設定する必要があります。乾杯。
Womble

3

あなたは簡単にそうすることができますソースコードとしてストーリーボードを開いてを検索しkind="presentation"、kind = presentationのすべてのseagueタグで追加の属性を追加modalPresentationStyle="fullScreen"


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

//スワイプを無効にして無効にする場合は、行を追加します

Obj.isModalInPresentation = true

詳しくはApple Documentを確認してください。


2

私はメソッドswizzling(Swift 4.2)を使用してそれを達成しました:

次のようにUIViewController拡張機能を作成するには

extension UIViewController {

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

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

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

     static func setPresentationStyle_fullScreen() {

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

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

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

AppDelegateのapplication:didFinishLaunchingWithOptions:次の呼び出しでスウィズリングコードを呼び出します。

UIViewController.setPresentationStyle_fullScreen()

1

これは私のために働きました:

yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen


3
これは他のすべての既存の答えに何も追加しません。
rmaddy

1

UIViewController(たとえば、UIViewController + PresentationStyle)のカテゴリを作成します。次のコードを追加します。

 -(UIModalPresentationStyle)modalPresentationStyle{
     return UIModalPresentationStyleFullScreen;
}

これにより、UISearchControllerが壊れ、デバッグが困難なクラッシュが発生します
dklt

@dkltそれは素晴らしい観察です。したがって、プロパティを明示的に設定すると、問題が解決します。UISearchControllerを使用している場合、より簡単なソリューションはありません。
Govind

0

別のアプローチは、アプリに独自のベースビューコントローラーコンポーネントを配置し、次のような基本的なセットアップで指定された必要な初期化子を実装することです。

class MyBaseViewController: UIViewController {

//MARK: Initialisers

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

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

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

//MARK: Private

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

注意:ビューコントローラーが実際にモーダルで表示されるナビゲーションコントローラーに含まれている場合、ナビゲーションコントローラーは同じ方法で問題に対処する必要があります(つまり、カスタムナビゲーションコントローラーコンポーネントを同じ方法でカスタマイズします)

iOS 13.1およびiOS 12.4のXcode 11.1でテスト済み

それが役に立てば幸い


0

フルスクリーンを表示しないビデオでこの問題が発生しました。この日を救ったこの行を追加しました:-)

videoController.modalPresentationStyle = UIModalPresentationFullScreen;

2
これは他のすべての既存の答えに何も追加しません。
rmaddy

0

私のために働いた最も簡単な解決策。

viewController.modalPresentationStyle = .fullScreen

3
これは他のすべての既存の答えに何も追加しません。
rmaddy

0

UINavigationControllerを使用していて、ViewControllerをルートビューコントローラーとして埋め込む場合も、同じ問題が発生します。克服するために次のコードを使用してください。

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

0
class MyViewController: UIViewController {

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

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

self.modalPresentationStyle = .fullScreenすべてのビューコントローラを呼び出すのではなく、UIViewControllerをサブクラス化しMyViewControllerて、どこでも使用できます。


-1

上記の回答と提案は正しいですが、以下は別のバージョンであり、プログラムで使用する効率的な方法です。

#1 UIView拡張を作成

#2メソッドを作成した()

//#1
extension UIViewController {

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

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

  }
}

以下のように呼び出す

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

または

let vc = MyViewController()
sourceView.presentLocal(vc, animated: true, completion: nil)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.