2013-04-12 5 views
18

Gibt es eine Möglichkeit, einen View Controller modal zu präsentieren, ohne zu wissen, was die View-Controller Ansicht ist? Grundsätzlich so, als würden Sie zu jedem Zeitpunkt eine Alarmansicht anzeigen.Einen modalen Controller vorstellen, ohne den aktuellen View Controller zu kennen?

Ich möchte in der Lage sein, etwas zu tun:

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

ich dies in der App von überall in der Lage sein möchten, rufen, und es oben angezeigt haben. Ich möchte mich nicht darum kümmern, was der aktuelle View-Controller ist.

Ich plane, dies zu verwenden, um eine Login-Eingabeaufforderung anzuzeigen. Ich möchte keine Warnmeldungsansicht verwenden und möchte auch keinen Login-Code in der gesamten App haben.

Irgendwelche Gedanken dazu? Oder gibt es vielleicht einen besseren Weg, dies zu erreichen? Sollte ich einfach meinen eigenen Mechanismus implementieren und einfach nur einen Blick auf das Fenster werfen?

+0

tun Sie Storyboards verwenden? –

+0

@SpaceDust nope – nebs

Antwort

27

Nun, Sie können die Kette folgen.

Start um [UIApplication sharedApplication].delegate.window.rootViewController.

Führen Sie bei jedem View Controller die folgende Testreihe durch.

Wenn [viewController isKindOfClass:[UINavigationController class]], dann weiter mit [(UINavigationController *)viewController topViewController].

Wenn [viewController isKindOfClass:[UITabBarController class]], dann weiter mit [(UITabBarController *)viewController selectedViewController].

Wenn [viewController presentedViewController], dann weiter mit [viewController presentedViewController].

+3

Dies ist die korrekteste Antwort. –

+1

Ich habe eine rekursive Methode mit Ihrer Idee erstellt: https://gist.github.com/MartinMoizard/6537467. Funktioniert wie ein Charme :) – MartinMoizard

+0

Dies ist die richtige Antwort. –

7

Sie können diesen Code in AppDelegate implementiert haben:

AppDelegate.m

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

AppDelegate.h

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

Von Überall dort, wo

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

In Ihrem Delegaten erhalten Sie die rootViewController der window. Dies wird immer sichtbar sein - es ist der "übergeordnete" Controller von allem.

+7

Ich denke, das wird normalerweise funktionieren, aber es ist nicht korrekt, dass der Root-View-Controller immer sichtbar ist. Es könnte einen modalen Ansicht-Controller auf dem Bildschirm geben, und ich bin nicht sicher, was passieren würde, wenn Sie diesen Code unter diesem Umstand ausführen würden. – rdelmar

+0

@rdelmar In diesem Fall ist Ihr Root-VC immer noch der Stamm, der den modalen VC präsentiert. – Undo

+4

Ja, es ist immer noch die Wurzel, aber es wird nicht funktionieren - wenn ein Modal View-Controller auf dem Bildschirm ist, und Sie versuchen, eine andere aus der Root-VC zu präsentieren, erhalten Sie eine Warnung, und der Controller wird nicht vorgestellt (Versuch Darstellen ... auf dessen Ansicht nicht in der Fensterhierarchie ist!). – rdelmar

2

Ich glaube nicht, dass Sie unbedingt wissen müssen, welcher View Controller sichtbar ist. Sie können die keyWindow der Anwendung aufrufen und die Ansicht Ihres modalen Ansichtscontrollers an den Anfang der Liste der Ansichten hinzufügen. Dann können Sie es wie die UIAlertView arbeiten lassen.

Schnittstelle Datei: MyModalViewController.h

#import <UIKit/UIKit.h> 

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

Implementierungsdatei: 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 
19

Meine Lösung in Swift (vom Kern MartinMoizard inspiriert)

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

Diese Lösung bietet Ihnen die obersten View-Controller, so dass Sie besondere Bedingungen umgehen können, bevor daraus präsentieren. Zum Beispiel möchten Sie vielleicht Ihren View-Controller nur dann darstellen, wenn der oberste View-Controller kein spezieller View-Controller ist.

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

Mit diesem können Sie Ihre View-Controller präsentieren von überall, ohne wissen zu müssen, was der obersten View-Controller

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

ist oder Ihre View-Controller präsentieren nur dann, wenn der oberste View-Controller nicht ist ein bestimmte view-Controller

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

Eines ist zu beachten, dass, wenn es ein UIAlertController ist zur Zeit angezeigt wird, UIApplication.topMostViewController einezurück. Präsentieren auf einem UIAlertController hat seltsames Verhalten und sollte vermieden werden. Als solche sollten Sie entweder manuell überprüfen, dass !(UIApplication.topMostViewController is UIAlertController) vor der Vorlage, oder fügen Sie ein else if Fall nil zurück, wenn 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 
     } 
    } 
}