web-dev-qa-db-ja.com

現在のビューコントローラーを知らずにモーダルコントローラーを提示しますか?

表示されているViewControllerビューが何であるかを知らなくても、View Controllerをモーダルに表示する方法はありますか?基本的には、任意の時点でアラートビューを表示するようなものです。

私は次のようなことができるようになりたいです:

MyViewController *myVC = [[MyViewController alloc] init];
[myVC showModally];

アプリのどこからでもこれを呼び出して、一番上に表示できるようにしたいと思います。現在のViewControllerが何であるかは気にしたくありません。

これを使用してログインプロンプトを表示する予定です。アラートビューを使用したくありません。また、アプリ全体でログインプレゼンテーションコードを使用したくありません。

これについて何か考えはありますか?それとも、これを達成するためのより良い方法はありますか?独自のメカニズムを実装して、ウィンドウの上にビューを配置する必要がありますか?

21
nebs

さて、あなたはチェーンをたどることができます。

_[UIApplication sharedApplication].delegate.window.rootViewController_から開始します。

各ViewControllerで、次の一連のテストを実行します。

_[viewController isKindOfClass:[UINavigationController class]]_の場合は、[(UINavigationController *)viewController topViewController]に進みます。

_[viewController isKindOfClass:[UITabBarController class]]_の場合は、[(UITabBarController *)viewController selectedViewController]に進みます。

_[viewController presentedViewController]_の場合は、_[viewController presentedViewController]_に進みます。

29
Jeffery Thomas

Swift(MartinMoizardの要点に触発された)の私の解決策

extension UIViewController {
    func presentViewControllerFromVisibleViewController(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
        if let navigationController = self as? UINavigationController {
            navigationController.topViewController?.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else if let tabBarController = self as? UITabBarController {
            tabBarController.selectedViewController?.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else if let presentedViewController = presentedViewController {
            presentedViewController.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else {
            present(viewControllerToPresent, animated: flag, completion: completion)
        }
    }
}
20
allaire

このソリューションは、最上位のView Controllerを提供するため、提示する前に特別な条件を処理できます。たとえば、最上位のViewControllerが特定のViewControllerでない場合にのみ、ViewControllerを表示したい場合があります。

_extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}
_

これにより、最上位のView Controllerが何であるかを知らなくても、どこからでもViewControllerを提示できます。

_UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil)
_

または、最上位のViewControllerが特定のViewControllerでない場合にのみ、ViewControllerを提示します

_if let topVC = UIApplication.topMostViewController, !(topVC is FullScreenAlertVC) {
    topVC.present(viewController, animated: true, completion: nil)
}
_

注意すべき点の1つは、現在表示されているUIAlertControllerがある場合、_UIApplication.topMostViewController_はUIAlertControllerを返すことです。 UIAlertControllerの上に表示すると、動作がおかしくなるため、避ける必要があります。そのため、提示する前に!(UIApplication.topMostViewController is UIAlertController)を手動で確認するか、_else if_の場合にnilを返すように_self is UIAlertController_ケースを追加する必要があります。

_extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else if self is UIAlertController {
            return nil
        } else {
            return self
        }
    }
}
_
11
NSExceptional

このコードをアプリデリゲートに実装することができます。

AppDelegate.m

-(void)presentViewControllerFromVisibleController:(UIViewController *)toPresent
{
    UIViewController *vc = self.window.rootViewController;
    [vc presentViewController:toPresent animated:YES];
}

AppDelegate.h

-(void)presentViewControllerFromVisibleViewController:(UIViewController *)toPresent;

どこからでも

#import "AppDelegate.h"
...
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
[delegate presentViewControllerFromVisibleViewController:myViewControllerToPresent];

デリゲートでは、rootViewControllerwindowを取得しています。これは常に表示されます-それはすべての「親」コントローラーです。

7
Undo

どのViewControllerが表示されているかを必ずしも知る必要はないと思います。アプリケーションのkeyWindowにアクセスして、モーダルビューコントローラーのビューをビューのリストの一番上に追加できます。次に、それをUIAlertViewのように機能させることができます。

インターフェースファイル: MyModalViewController.h

#import <UIKit/UIKit.h>

@interface MyModalViewController : UIViewController
- (void) show;
@end

実装ファイル: MyModalViewController.m

#import "MyModalViewController.h"


@implementation MyModalViewController

- (void) show {
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    //  Configure the frame of your modal's view.
    [window addSubview: self.view];
}

@end
2
Jason Barker