ios - サイズ - View ControllerにないときにUIAlertControllerを表示するにはどうすればよいですか?



uialertcontroller 重複 (20)

@agilityvisionの答えはとても良いです。 私は迅速なプロジェクトに慣れているので、Swift 3.0を使用して彼の答えに対する私の意見を共有すると思った

fileprivate class MyUIAlertController: UIAlertController {

  typealias Handler = () -> Void

  struct AssociatedKeys {
    static var alertWindowKey = "alertWindowKey"
  }

  dynamic var _alertWindow: UIWindow?

  var alertWindow: UIWindow? {
    return objc_getAssociatedObject(self, &AssociatedKeys.alertWindowKey) as? UIWindow
  }


  func setAlert(inWindow window: UIWindow) {
    objc_setAssociatedObject(self, &AssociatedKeys.alertWindowKey, _alertWindow, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }

  func show(completion: Handler? = nil) {
    show(animated: true, completion: completion)
  }

  func show(animated: Bool, completion: Handler? =  nil) {
    _alertWindow = UIWindow(frame: UIScreen.main.bounds)
    _alertWindow?.rootViewController = UIViewController()

    if let delegate: UIApplicationDelegate = UIApplication.shared.delegate, let window = delegate.window {
      _alertWindow?.tintColor = window?.tintColor

    }

    let topWindow = UIApplication.shared.windows.last
    _alertWindow?.windowLevel = topWindow?.windowLevel ?? 0 + 1
    _alertWindow?.makeKeyAndVisible()
    _alertWindow?.rootViewController?.present(self, animated: animated, completion: completion)
  }

  fileprivate override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    _alertWindow?.isHidden = true
    _alertWindow = nil
  }
}

https://src-bin.com

シナリオ:ユーザーがView Controllerのボタンをタップします。 View Controllerは、ナビゲーションスタックの最上部にあることは明らかです。 タップは、別のクラスで呼び出されるユーティリティクラスメソッドを呼び出します。 そこで悪いことが起こったので、制御がView Controllerに戻る前にアラートをすぐに表示したいと思います。

+ (void)myUtilityMethod {
    // do stuff
    // something bad happened, display an alert.
}

これは UIAlertView で可能 UIAlertView (ただし、おそらく適切ではありません)。

この場合、 UIAlertController どのように提示しますか?


Answer #1

Andの UIViewController ような - (void)presentErrorMessage; 方法で カテゴリを実装しようとすることができ、 そのメソッド内でUIAlertControllerを実装し、それを表示し self ます。 クライアントコードよりも次のようになります。

[myViewController presentErrorMessage];

これにより、不要なパラメータや、ビューがウィンドウ階層にないことに関する警告を回避できます。


Answer #2

Kevin Sliechは素晴らしいソリューションを提供しました。

メインのUIViewControllerサブクラスで以下のコードを使用します。

私が行った小さな変更の1つは、最適なプレゼンテーションコントローラーがプレーンなUIViewControllerではないかどうかを確認することでした。 そうでない場合は、プレーンVCを提供するVCでなければなりません。 したがって、代わりに提示されているVCを返します。

- (UIViewController *)bestPresentationController
{
    UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController;

    if (![bestPresentationController isMemberOfClass:[UIViewController class]])
    {
        bestPresentationController = bestPresentationController.presentedViewController;
    }    

    return bestPresentationController;
}

私のテストでこれまでのところうまくいくようです。

ケビンありがとう!


Answer #3

iOS 13の場合、 mythicalcoder と bobbyrehm による回答に bobbyrehm ます。

iOS 13では、アラートを表示する独自のウィンドウを作成する場合、そのウィンドウへの強い参照を保持する必要があります。そうしないと、参照がスコープを終了するとすぐにウィンドウが割り当て解除されるため、アラートは表示されません。

さらに、ウィンドウを削除してその下のメインウィンドウでのユーザーの操作を継続できるようにするには、アラートが閉じられた後に参照を再びnilに設定する必要があります。

UIViewController ウィンドウメモリ管理ロジックをカプセル化 する サブクラスを 作成できます 。

class WindowAlertPresentationController: UIViewController {

    // MARK: - Properties

    private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
    private let alert: UIAlertController

    // MARK: - Initialization

    init(alert: UIAlertController) {

        self.alert = alert
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {

        fatalError("This initializer is not supported")
    }

    // MARK: - Presentation

    func present(animated: Bool, completion: (() -> Void)?) {

        window?.rootViewController = self
        window?.windowLevel = UIWindow.Level.alert + 1
        window?.makeKeyAndVisible()
        present(alert, animated: animated, completion: completion)
    }

    // MARK: - Overrides

    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {

        super.dismiss(animated: flag) {
            self.window = nil
            completion?()
        }
    }
}

これをそのまま使用できます。または、で便利なメソッドが必要な場合 UIAlertController は、拡張機能でスローでき ます 。

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {

        let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
        windowAlertPresentationController.present(animated: animated, completion: completion)
    }
}

Answer #4

上記のすべてを試しましたが、成功しませんでした。 Swift 3.0に使用した方法:

extension UIAlertController {
    func show() {
        present(animated: true, completion: nil)
    }

    func present(animated: Bool, completion: (() -> Void)?) {
        if var topController = UIApplication.shared.keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            topController.present(self, animated: animated, completion: completion)
        }
    }
}

Answer #5

与えられた素晴らしい答えに加えて( agilityvision 、 adib 、 malhal )。 古き良きUIAlertViews(アラートウィンドウの重複を避ける)のようにキューイング動作に到達するには、このブロックを使用してウィンドウレベルの可用性を監視します。

@interface UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block;

@end

@implementation UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block {
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    if (keyWindow.windowLevel == level) {
        // window level is occupied, listen for windows to hide
        id observer;
        observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification *note) {
            [[NSNotificationCenter defaultCenter] removeObserver:observer];
            [self notifyWindowLevelIsAvailable:level withBlock:block]; // recursive retry
        }];

    } else {
        block(); // window level is available
    }
}

@end

完全な例:

[UIWindow notifyWindowLevelIsAvailable:UIWindowLevelAlert withBlock:^{
    UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    alertWindow.windowLevel = UIWindowLevelAlert;
    alertWindow.rootViewController = [UIViewController new];
    [alertWindow makeKeyAndVisible];

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        alertWindow.hidden = YES;
    }]];

    [alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}];

これにより、アラートウィンドウの重複を回避できます。 同じ方法を使用して、任意の数のウィンドウレイヤーのキュービューコントローラーを分離および配置できます。


Answer #6

動作するようです:

static UIViewController *viewControllerForView(UIView *view) {
    UIResponder *responder = view;
    do {
        responder = [responder nextResponder];
    }
    while (responder && ![responder isKindOfClass:[UIViewController class]]);
    return (UIViewController *)responder;
}

-(void)showActionSheet {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]];
    [viewControllerForView(self) presentViewController:alertController animated:YES completion:nil];
}

Answer #7

現在のビューまたはコントローラーをパラメーターとして送信できます。

+ (void)myUtilityMethod:(id)controller {
    // do stuff
    // something bad happened, display an alert.
}

Answer #8

@agilityvisionの回答はSwift4 / iOS11に翻訳されました。 ローカライズされた文字列は使用していませんが、簡単に変更できます。

import UIKit

/** An alert controller that can be called without a view controller.
 Creates a blank view controller and presents itself over that
 **/
class AlertPlusViewController: UIAlertController {

    private var alertWindow: UIWindow?

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

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.alertWindow?.isHidden = true
        alertWindow = nil
    }

    func show() {
        self.showAnimated(animated: true)
    }

    func showAnimated(animated _: Bool) {

        let blankViewController = UIViewController()
        blankViewController.view.backgroundColor = UIColor.clear

        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = blankViewController
        window.backgroundColor = UIColor.clear
        window.windowLevel = UIWindowLevelAlert + 1
        window.makeKeyAndVisible()
        self.alertWindow = window

        blankViewController.present(self, animated: true, completion: nil)
    }

    func presentOkayAlertWithTitle(title: String?, message: String?) {

        let alertController = AlertPlusViewController(title: title, message: message, preferredStyle: .alert)
        let okayAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
        alertController.addAction(okayAction)
        alertController.show()
    }

    func presentOkayAlertWithError(error: NSError?) {
        let title = "Error"
        let message = error?.localizedDescription
        presentOkayAlertWithTitle(title: title, message: message)
    }
}

Answer #9

Aviel Grossの回答のような拡張機能を作成します。 ここに、Objective-C拡張があります。

ここにヘッダーファイル* .hがあります

//  UIAlertController+Showable.h

#import <UIKit/UIKit.h>

@interface UIAlertController (Showable)

- (void)show;

- (void)presentAnimated:(BOOL)animated
             completion:(void (^)(void))completion;

- (void)presentFromController:(UIViewController *)viewController
                     animated:(BOOL)animated
                   completion:(void (^)(void))completion;

@end

実装:* .m

//  UIAlertController+Showable.m

#import "UIAlertController+Showable.h"

@implementation UIAlertController (Showable)

- (void)show
{
    [self presentAnimated:YES completion:nil];
}

- (void)presentAnimated:(BOOL)animated
             completion:(void (^)(void))completion
{
    UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
    if (rootVC != nil) {
        [self presentFromController:rootVC animated:animated completion:completion];
    }
}

- (void)presentFromController:(UIViewController *)viewController
                     animated:(BOOL)animated
                   completion:(void (^)(void))completion
{

    if ([viewController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visibleVC = ((UINavigationController *)viewController).visibleViewController;
        [self presentFromController:visibleVC animated:animated completion:completion];
    } else if ([viewController isKindOfClass:[UITabBarController class]]) {
        UIViewController *selectedVC = ((UITabBarController *)viewController).selectedViewController;
        [self presentFromController:selectedVC animated:animated completion:completion];
    } else {
        [viewController presentViewController:self animated:animated completion:completion];
    }
}

@end

実装ファイルでこの拡張機能を次のように使用しています。

#import "UIAlertController+Showable.h"

UIAlertController* alert = [UIAlertController
    alertControllerWithTitle:@"Title here"
                     message:@"Detail message here"
              preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction* defaultAction = [UIAlertAction
    actionWithTitle:@"OK"
              style:UIAlertActionStyleDefault
            handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];

// Add more actions if needed

[alert show];

Answer #10

Swift 2.2では次のことができます。

let alertController: UIAlertController = ...
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

Swift 3.0:

let alertController: UIAlertController = ...
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)

Answer #11

WWDCで、私はラボの1つに立ち寄り、Appleエンジニアに同じ質問をし UIAlertController 。「 UIAlertController を表示するためのベストプラクティスは何ですか?」 そして、彼は彼らがこの質問をたくさん受けていたと言った、そして我々は彼らがそれについてセッションをするべきだったと冗談を言った。 彼は内部的にAppleは透明な UIViewController UIWindow を作成し、 UIAlertController 上に UIAlertController を提示していると UIAlertController ました。 基本的にディラン・ベターマンの答えには何が含まれています。

しかし、 UIAlertController サブクラスを使用したくありませんでした。アプリ全体でコードを変更する必要があるからです。 そのため、関連オブジェクトの助けを借りて、Objective-Cで show メソッドを提供する UIAlertController カテゴリを作成しました。

関連するコードは次のとおりです。

#import "UIAlertController+Window.h"
#import <objc/runtime.h>

@interface UIAlertController (Window)

- (void)show;
- (void)show:(BOOL)animated;

@end

@interface UIAlertController (Private)

@property (nonatomic, strong) UIWindow *alertWindow;

@end

@implementation UIAlertController (Private)

@dynamic alertWindow;

- (void)setAlertWindow:(UIWindow *)alertWindow {
    objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIWindow *)alertWindow {
    return objc_getAssociatedObject(self, @selector(alertWindow));
}

@end

@implementation UIAlertController (Window)

- (void)show {
    [self show:YES];
}

- (void)show:(BOOL)animated {
    self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.alertWindow.rootViewController = [[UIViewController alloc] init];

    id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
    // Applications that does not load with UIMainStoryboardFile might not have a window property:
    if ([delegate respondsToSelector:@selector(window)]) {
        // we inherit the main window's tintColor
        self.alertWindow.tintColor = delegate.window.tintColor;
    }

    // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
    self.alertWindow.windowLevel = topWindow.windowLevel + 1;

    [self.alertWindow makeKeyAndVisible];
    [self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    // precaution to ensure window gets destroyed
    self.alertWindow.hidden = YES;
    self.alertWindow = nil;
}

@end

以下に使用例を示します。

// need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow
// would not disappear after the Alert was dismissed
__block UITextField *localTextField;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    NSLog(@"do something with text:%@", localTextField.text);
// do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle
}]];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
    localTextField = textField;
}];
[alert show];

UIWindowUIAlertController が解除されると、 UIAlertController 保持している唯一のオブジェクトであるため、作成された UIAlertController は破棄されます。 ただし、 UIAlertController をプロパティに割り当てるか、アクションブロックの1つでアラートにアクセスして保持カウントを増加させると、 UIWindow は画面に残り、UIがロックされます。 UITextField にアクセスする必要がある場合に回避するには、上記のサンプル使用コードを参照してください。

テストプロジェクト FFGlobalAlertController GitHubリポジトリを作成しました


Answer #12

Zevの答えに加えて(そしてObjective-Cに戻ると)、ルートビューコントローラーがセグエまたは他の何かを介して他のVCを提示する状況に陥る可能性があります。 ルートVCでpresentViewControllerを呼び出すと、これが処理されます。

[[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController presentViewController:alertController animated:YES completion:^{}];

これにより、ルートVCが別のVCに引き継がれていたという問題が解決され、アラートコントローラーを表示する代わりに、上記のような警告が発行されました。

Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!

私はそれをテストしていませんが、ルートVCがたまたまナビゲーションコントローラーである場合、これも必要になるかもしれません。


Answer #13

これは、Swift 4でテストおよび動作する拡張機能としての mythicalcoderの回答 です。

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
        let alertWindow = UIWindow(frame: UIScreen.main.bounds)
        alertWindow.rootViewController = UIViewController()
        alertWindow.windowLevel = UIWindowLevelAlert + 1;
        alertWindow.makeKeyAndVisible()
        alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
    }

}

使用例:

let alertController = UIAlertController(title: "<Alert Title>", message: "<Alert Message>", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
alertController.presentInOwnWindow(animated: true, completion: {
    print("completed")
})

Answer #14

これらの2つのスレッドは重複としてフラグが付けられていないので、私の answer クロスポストしてください...

UIViewController がレスポンダーチェーンの一部になったので、次のようなことができます。

if let vc = self.nextResponder()?.targetForAction(#selector(UIViewController.presentViewController(_:animated:completion:)), withSender: self) as? UIViewController {

    let alert = UIAlertController(title: "A snappy title", message: "Something bad happened", preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))

    vc.presentViewController(alert, animated: true, completion: nil)
}

Answer #15

数か月前に 同様の質問 を投稿し、ようやく問題を解決したと思います。 コードを表示するだけの場合は、投稿の下部にあるリンクをたどってください。

解決策は、追加のUIWindowを使用することです。

UIAlertControllerを表示する場合:

  1. ウィンドウをキーにして可視ウィンドウにする( window.makeKeyAndVisible()
  2. 単純なUIViewControllerインスタンスを新しいウィンドウのrootViewControllerとして使用するだけです。 ( window.rootViewController = UIViewController()
  3. UIAlertControllerをウィンドウのrootViewControllerに提示します

注意点がいくつかあります:

  • UIWindowを強く参照する必要があります。 強く参照されていない場合は表示されません(リリースされているため)。 プロパティを使用することをお勧めしますが、 関連するオブジェクト でも成功しました。
  • ウィンドウが他のすべてのもの(システムUIAlertControllersを含む)の上に表示されるようにするには、windowLevelを設定します。 ( window.windowLevel = UIWindowLevelAlert + 1

最後に、あなたがそれを見たいだけなら、私は完成した実装を持っています。

https://github.com/dbettermann/DBAlertController


Answer #16

agilityvisionの答えを 改善するには、透明なルートビューコントローラーを備えたウィンドウを作成し、そこからアラートビューを表示する必要があります。

ただし 、アラートコントローラーにアクションがある限り 、ウィンドウへの参照を保持する必要はありません 。 アクションハンドラーブロックの最後のステップとして、クリーンアップタスクの一部としてウィンドウを非表示にするだけです。 ハンドラーブロック内のウィンドウへの参照を持つことにより、アラートコントローラーが閉じられると壊れる一時的な循環参照が作成されます。

UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;

UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:... message:... preferredStyle:UIAlertControllerStyleAlert];

[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK",@"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    ... // do your stuff

    // very important to hide the window afterwards.
    // this also keeps a reference to the window until the action is invoked.
    window.hidden = YES;
}]];

[window makeKeyAndVisible];
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];

Answer #17

UINavigationController および/または UITabBarController すべてのケースに UINavigationController 汎用的な UIAlertController extension 。 現時点で画面にモーダルVCがある場合にも機能します。

使用法:

//option 1:
myAlertController.show()
//option 2:
myAlertController.present(animated: true) {
    //completion code...
}

これは拡張子です:

//Uses Swift1.2 syntax with the new if-let
// so it won't compile on a lower version.
extension UIAlertController {

    func show() {
        present(animated: true, completion: nil)
    }

    func present(#animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
            presentFromController(rootVC, animated: animated, completion: completion)
        }
    }

    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if  let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController {
                presentFromController(visibleVC, animated: animated, completion: completion)
        } else {
          if  let tabVC = controller as? UITabBarController,
              let selectedVC = tabVC.selectedViewController {
                presentFromController(selectedVC, animated: animated, completion: completion)
          } else {
              controller.presentViewController(self, animated: animated, completion: completion)
          }
        }
    }
}

Answer #18

スイフト4+

私は何年も何の問題もなく使用しています。 まず、 UIWindow を拡張してvisibleViewControllerを見つけます。 :カスタムコレクション*クラス(サイドメニューなど)を使用する場合、この場合のハンドラーを次の拡張機能に追加する必要があります。 一番上のView Controllerを取得した後、 UIAlertController ように UIAlertView 簡単に表示できます。

extension UIAlertController {

  func show(animated: Bool = true, completion: (() -> Void)? = nil) {
    if let visibleViewController = UIApplication.shared.keyWindow?.visibleViewController {
      visibleViewController.present(self, animated: animated, completion: completion)
    }
  }

}

extension UIWindow {

  var visibleViewController: UIViewController? {
    guard let rootViewController = rootViewController else {
      return nil
    }
    return visibleViewController(for: rootViewController)
  }

  private func visibleViewController(for controller: UIViewController) -> UIViewController {
    var nextOnStackViewController: UIViewController? = nil
    if let presented = controller.presentedViewController {
      nextOnStackViewController = presented
    } else if let navigationController = controller as? UINavigationController,
      let visible = navigationController.visibleViewController {
      nextOnStackViewController = visible
    } else if let tabBarController = controller as? UITabBarController,
      let visible = (tabBarController.selectedViewController ??
        tabBarController.presentedViewController) {
      nextOnStackViewController = visible
    }

    if let nextOnStackViewController = nextOnStackViewController {
      return visibleViewController(for: nextOnStackViewController)
    } else {
      return controller
    }
  }

}

Answer #19

迅速

let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
//...
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
    rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
    rootViewController = tabBarController.selectedViewController
}
//...
rootViewController?.present(alertController, animated: true, completion: nil)

Objective-C

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
//...
id rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if([rootViewController isKindOfClass:[UINavigationController class]])
{
    rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
}
if([rootViewController isKindOfClass:[UITabBarController class]])
{
    rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
}
//...
[rootViewController presentViewController:alertController animated:YES completion:nil];




uialertcontroller