Kann festgestellt werden, ob ViewController als modal dargestellt wird?

117

Ist es möglich, innerhalb der ViewController-Klasse zu überprüfen, ob sie als modaler View Controller dargestellt wird?

lukewar
quelle

Antworten:

96

Da modalViewControllerdies in iOS 6 veraltet ist, ist hier eine Version, die für iOS 5+ funktioniert und ohne Warnungen kompiliert wird.

Ziel c:

- (BOOL)isModal {
    return self.presentingViewController.presentedViewController == self
      || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
      || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
}

Schnell:

var isModal: Bool {
    return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController)
        || self.tabBarController?.presentingViewController is UITabBarController
}

Hutspitze zu Felipes Antwort.

Gabriele Petronella
quelle
2
Guter Fang, ich musste es nur nach langer Zeit wieder verwenden und bemerkte, dass die Abwertung passiert ist ... Ich habe meine Antwort so bearbeitet, dass die Leute hier nach dem richtigen Code suchen, wenn sie iOS 6+ verwenden, danke
Felipe Sabino
10
Funktioniert nicht, wenn der übergeordnete Ansichtscontroller ein Modal ist, auf den unser Ansichtscontroller übertragen wird.
Sinn-Angelegenheiten
2
Es gibt einen Fehler, wir sollten prüfen, ob beide Seiten gleich Null sind, da nil == nilzurückgegeben YESwird und es nicht das gewünschte Ergebnis ist.
CocoaBob
1
@GabrielePetronella Stört es Sie, wenn ich die Antwort aktualisiere, um auch eine Swift-Implementierung der Methode einzuschließen?
Michael Wasserfall
1
@ MichaelWaterfall, das wäre sehr dankbar, danke
Gabriele Petronella
77

Wenn Sie nach iOS 6+ suchen, ist diese Antwort veraltet und Sie sollten die Antwort von Gabriele Petronella überprüfen


Es gibt keine gute Möglichkeit, dies als Eigenschaft oder Methode von UIKit zu tun. Sie können verschiedene Aspekte Ihres Controllers überprüfen, um sicherzustellen, dass er als modal dargestellt wird.

Um zu überprüfen, ob der aktuelleself Controller (wie im folgenden Code dargestellt) modal dargestellt wird oder nicht, habe ich den folgenden Balken entweder in einer UIViewControllerKategorie oder (wenn Ihr Projekt keine anderen UIKit-Controller verwenden muss, als UITableViewControllerzum Beispiel) in einem Basis-Controller, von dem meine anderen Controller erben

-(BOOL)isModal {

     BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
            //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
            ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
            //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
            [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);

    //iOS 5+
    if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {

        isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
             //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
             (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
             //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
             [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);

    }

    return isModal;        

}

BEARBEITEN: Ich habe die letzte Überprüfung hinzugefügt, um festzustellen, ob ein UITabBarController verwendet wird, und Sie präsentieren einen anderen UITabBarController als modal.

BEARBEITEN 2: iOS 5+ Check hinzugefügt, wo UIViewControllernicht parentViewControllermehr geantwortet wird, sondern presentingViewControllerstattdessen.

EDIT 3: Ich habe einen Kern dafür erstellt, nur für den Fall, dass https://gist.github.com/3174081

Felipe Sabino
quelle
Beachten Sie, dass die modalViewControllerEigenschaft ab iOS 6 veraltet ist. In der Dokumentation wird empfohlen, sie presentedViewControllerstattdessen zu verwenden .
Bart Jacobs
@ BartJacobs guter Punkt! Ich habe mir diese Antwort nach der Veröffentlichung von iOS6 nicht angesehen, daher ist sie möglicherweise nicht auf dem neuesten Stand. Ich werde später in der Woche versuchen, einige Tests durchzuführen, um es zu aktualisieren, tks!
Felipe Sabino
NSLog(@"%@", self.navigationController.parentViewController)Drucke (null)- können Sie bitte erklären, warum? Mein ViewController ist über navController im Storyboard mit dem Modal View Controller verbunden.
Roman
@oyatek Kannst du Pastebin oder ähnliches verwenden und Code anzeigen?
Felipe Sabino
@Feilpe Ich habe das Problem gefunden - .parentViewControllerist veraltet, .presentingViewControllermuss stattdessen verwendet werden.
Roman
35

In iOS5 + können Sie es, wie Sie in der UIViewController-Klassenreferenz sehen können, über die Eigenschaft "PresentingViewController" abrufen .

PresentingViewController Der View-Controller, der diesen View-Controller präsentiert hat. (schreibgeschützt)

@property (nichtatomar, schreibgeschützt) UIViewController * Präsentation der ViewController-
Diskussion

Wenn der Ansichtscontroller, der diese Nachricht empfangen hat, von einem anderen Ansichtscontroller angezeigt wird, enthält diese Eigenschaft den Ansichtscontroller, der sie anzeigt. Wenn der Ansichtscontroller nicht angezeigt wird, aber einer seiner Vorfahren angezeigt wird, enthält diese Eigenschaft den Ansichtscontroller, der den nächsten Vorfahren darstellt. Wenn weder der Ansichtscontroller noch einer seiner Vorfahren angezeigt wird, ist diese Eigenschaft gleich Null.

Verfügbarkeit
Verfügbar in iOS 5.0 und höher.
In
UIViewController.h deklariert

Raj
quelle
3
Funktioniert perfekt, benutze if (self.presentingViewController) {// Dies ist ein modaler viewContoller} else {// Dies ist ein normaler ViewController}
mashdup
2
IMHO, das ist die einzig richtige Antwort hier. Überprüfen Sie einfach, ob ein vorhanden ist presentingViewController. Es funktioniert auch in Containersicht-Controllern, da es automatisch die Vorfahren durchläuft.
Daniel Rinser
17

Ist dies nicht der Fall , können Sie eine Eigenschaft für this ( presentedAsModal) in Ihrer UIViewController-Unterklasse definieren und festlegen, YESbevor Sie den ViewController als modale Ansicht präsentieren.

childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];

Sie können diesen Wert in Ihrer viewWillAppearÜberschreibung überprüfen .

Ich glaube, es gibt keine offizielle Eigenschaft, die angibt, wie die Ansicht dargestellt wird, aber nichts hindert Sie daran, Ihre eigene zu erstellen.

hpique
quelle
Recht und das habe ich getan, aber ich suchte nach einer anderen netten Lösung. Vielen Dank.
Lukewar
Diese Lösung funktioniert nicht, wenn Sie eine UINavigationControllerals modal präsentieren ... es sei denn, Sie erstellen einen benutzerdefinierten Navigationscontroller, um diese Eigenschaft hinzuzufügen. Und danach müssen Sie innerhalb der Controller self.navigationControllerjedes Mal, wenn Sie überprüfen müssen, ob der Controller als modal dargestellt wird, in diese benutzerdefinierte Klasse
Felipe Sabino
8

Petronellas Antwort funktioniert nicht, wenn self.navigationController modal dargestellt wird, aber self nicht gleich self.navigationController.viewControllers [0] ist. In diesem Fall wird self gedrückt.

Hier erfahren Sie, wie Sie das Problem beheben können.

return self.presentingViewController.presentedViewController == self
            || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController && self == self.navigationController.viewControllers[0])
            || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];

Und in Swift:

return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController && self.navigationController?.viewControllers[0] == self)
        || self.tabBarController?.presentingViewController is UITabBarController
Semih Cihan
quelle
6

Das sollte funktionieren.

if(self.parentViewController.modalViewController == self)…
kubi
quelle
Das funktioniert leider nicht. Es war mein erster Versuch. Aber zurückgegeben modalViewController ins nil :(.
lukewar
Wenn Sie nur 'self.parentViewController' erhalten, wird das richtige übergeordnete Objekt zurückgegeben?
Kubi
4
Das Problem könnte sein, dass sich Ihre UIViewController-Unterklasse in einem UINavigationController oder einem UITabBarController (oder beiden) befindet. In diesem Fall müssen Sie möglicherweise etwas mehr in der Ansichtshierarchie suchen, um das übergeordnete Element herauszufinden, das als modaler Ansichtscontroller dargestellt wurde.
Hpique
@hgpc Ich brauchte diesen Check in meinem Projekt, also habe ich nur eine Antwort hinzugefügt, um nach beiden UINavigationControllerund UITabBarControllerFällen zu suchen . Es funktioniert bisher ziemlich gut
Felipe Sabino
4

Beste Weg zu überprüfen

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Sunny Shah
quelle
2

Wenn Sie nicht zwischen modalen Vollbildansichten und nicht modalen Ansichten unterscheiden müssen, was in meinem Projekt der Fall ist (ich hatte ein Problem, das nur bei Formularblättern und Seitenblättern auftritt), können Sie den modalPresentationStyle verwenden Eigentum von UIViewController:

switch (self.modalPresentationStyle) {
    case 0: NSLog(@"full screen, or not modal"); break;
    case 1: NSLog(@"page sheet"); break;
    case 2: NSLog(@"form sheet"); break;
}
Arlomedia
quelle
2

In Swift :

func isUIViewControllerPresentedAsModal() -> Bool {
    if((self.presentingViewController) != nil) {
        return true
    }

    if(self.presentingViewController?.presentedViewController == self) {
        return true
    }

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
        return true
    }

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
        return true
    }

    return false
}
König-Zauberer
quelle
Bei diesem Anwendungsfall liegt ein Problem vor. Wenn ich mich in einem Root-View-Controller eines UINavigationControllers befinde, wird ohne modale Darstellung immer noch true zurückgegeben.
mariusLAN
1
Die erste if-Anweisung deckt alles ab, was in der zweiten if-Anweisung enthalten ist, wodurch die zweite Anweisung überflüssig wird. Ich bin mir nicht sicher, was die Absicht hier ist.
Isoiphone
1

In meinem Projekt habe ich einen Ansichts-Controller (Detail), der entweder modal (beim Hinzufügen eines neuen Elements) oder per Push (beim Bearbeiten eines vorhandenen Elements) vom Master-Ansichts-Controller dargestellt werden kann. Wenn der Benutzer auf [Fertig] tippt, ruft der Detailansichts-Controller die Methode des Master-Ansichts-Controllers auf, um zu benachrichtigen, dass er zum Schließen bereit ist. Der Master muss festlegen, wie das Detail dargestellt wird, um zu wissen, wie es geschlossen werden kann. So mache ich das:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}
Olex
quelle
0

Ein Hack wie dieser könnte funktionieren.

UIViewController* child = self;
UIViewController* parent = child.parentViewController;
while (parent && parent.modalViewController != child) {
    child = parent;
    parent = child.parentViewController;
}
if (parent) {
    // A view controller in the hierarchy was presented as a modal view controller
}

Ich denke jedoch, dass meine vorherige Antwort eine sauberere Lösung ist.

hpique
quelle
0

Was für mich funktioniert hat, ist Folgendes:

// this is the trick: set parent view controller as application's window root view controller
UIApplication.sharedApplication.delegate.window.rootViewController = viewController;

// assert no modal view is presented
XCTAssertNil(viewController.presentedViewController);

// simulate button tap which shows modal view controller
[viewController.deleteButton sendActionsForControlEvents:UIControlEventTouchUpInside];

// assert that modal view controller is presented
XCTAssertEqualObjects(viewController.presentedViewController.class, MyModalViewController.class);

Soweit ich es getestet habe, funktioniert dies für iOS7 und iOS8. Ich habe iOS6 jedoch nicht ausprobiert.

mixtly87
quelle
0

Ich habe mich ein bisschen umgesehen, um die richtige Antwort auf diese Frage zu finden, und ich konnte keine finden, die alle möglichen Szenarien abdeckte. Ich habe diese wenigen Codezeilen geschrieben, die den Job zu erledigen scheinen. Sie können einige Inline-Kommentare finden, um herauszufinden, was überprüft wurde.

- (BOOL)isModal {
    BOOL modal = NO;
    if ([self presentingViewController]) { //Some view Controller is presenting the current stack
        UIViewController *presented = [[self presentingViewController] presentedViewController]; // What's been presented
        if ([presented respondsToSelector:@selector(viewControllers)]) { // There's a stack
            NSArray *viewControllers = [presented performSelector:@selector(viewControllers)];
            modal = [viewControllers firstObject] == self; // Current VC is presented modally if it's the first in the stack
        }
        else {
            modal = presented == self; // Don't think this is actually needed. set modal = YES should do the job tho.
        }
    }
    return modal;
}

Ich hoffe das hilft.

DennyLou
quelle
0

Hier ist meine modifizierte Version von @ GabrielePetronella isModal, die mit enthaltenen View-Controllern funktioniert, indem sie zuerst die parentViewController-Hierarchie aufruft. Ziehen Sie den Code auch in mehrere Zeilen heraus, damit klar ist, was er tut.

var isModal: Bool {
    // If we are a child view controller, we need to check our parent's presentation
    // rather than our own.  So walk up the chain until we don't see any parentViewControllers
    var potentiallyPresentedViewController : UIViewController = self
    while (potentiallyPresentedViewController.parentViewController != nil) {
        potentiallyPresentedViewController = potentiallyPresentedViewController.parentViewController!
    }

    if self.presentingViewController?.presentedViewController == potentiallyPresentedViewController {
        return true
    }

    if let navigationController = potentiallyPresentedViewController.navigationController {
        if navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        }
    }

    return potentiallyPresentedViewController.tabBarController?.presentingViewController is UITabBarController
}
Ryan
quelle