Wie kann überprüft werden, ob ein View Controller modal dargestellt oder auf einen Navigationsstapel verschoben wird?

126

Wie kann ich in meinem View Controller-Code unterscheiden zwischen:

  • modal präsentiert
  • auf Navigationsstapel geschoben

Beide presentingViewControllerund isMovingToParentViewControllersind YESin beiden Fällen also nicht sehr hilfreich.

Was die Sache kompliziert macht, ist, dass mein übergeordneter Ansichts-Controller manchmal modal ist, auf den der zu überprüfende Ansichts-Controller gedrückt wird.

Es stellt sich heraus, dass mein Problem darin besteht, dass ich meine HtmlViewControllerin eine einbettete, UINavigationControllerdie dann vorgestellt wird. Deshalb haben meine eigenen Versuche und die guten Antworten unten nicht funktioniert.

HtmlViewController*     termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;

modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
                   animated:YES
                 completion:nil];

Ich denke, ich sollte meinem View Controller besser sagen, wann er modal ist, anstatt zu versuchen, ihn zu bestimmen.

Sinn-Angelegenheiten
quelle

Antworten:

125

Mit einem Körnchen Salz nehmen, nicht getestet.

- (BOOL)isModal {
     if([self presentingViewController])
         return YES;
     if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
         return YES;
     if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
         return YES;

    return NO;
 }
ColdLogic
quelle
12
Ich habe dies in einem anderen SO-Beitrag gefunden. Funktioniert jedoch nicht, wenn das übergeordnete Element des Push-View-Controllers modal ist. Welches ist die Situation, die ich habe.
Bedeutung-Angelegenheiten
2
Wie ich schrieb, presentingViewControllerist immer YESin meinem Fall; hilft nicht.
Sinn-Angelegenheiten
3
presentingViewControllerGibt YESfür Push-VC zurück, wenn ein UITabBarControllerWesen als Root festgelegt ist. Also in meinem Fall nicht geeignet.
Yevhen Dubinin
5
Dies funktioniert nicht, wenn Sie einen Ansichts-Controller präsentieren und dann einen anderen drücken.
Lee
3
"Dies funktioniert nicht, wenn Sie einen View-Controller präsentieren, dann wird ein anderer gepusht." Dies ist nicht die Absicht, der Push-View-Controller wird nicht präsentiert.
Colin Swelin
87

In Swift :

Fügen Sie ein Flag hinzu, um zu testen, ob es sich um ein Modal des Klassentyps handelt:

// MARK: - UIViewController implementation

extension UIViewController {

    var isModal: Bool {

        let presentingIsModal = presentingViewController != nil
        let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
        let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController

        return presentingIsModal || presentingIsNavigation || presentingIsTabBar
    }
}
König-Zauberer
quelle
4
Sollte besser in einem var sein, wievar isModal: Bool {}
Malinois
1
@ Malinois wird geändert
YannSteph
Was macht der letzte falseParameter in der returnAnweisung?
Damjandd
Sie müssen Änderungen vornehmen, damit PresentingsIsNavigation = NavigationController? .presentingViewController? .presentedViewController == NavigationController && NavigationController! = nil
famfamfam
78

Sie haben eine Methode übersehen : isBeingPresented.

isBeingPresented ist wahr, wenn der Ansichts-Controller angezeigt wird, und falsch, wenn er gedrückt wird.

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

    if ([self isBeingPresented]) {
        // being presented
    } else if ([self isMovingToParentViewController]) {
        // being pushed
    } else {
        // simply showing again because another VC was dismissed
    }
}
rmaddy
quelle
2
Ich habe dies auch vor dem Posten versucht, und es funktioniert nicht, isBeingPresentedist NO. Aber ich sehe jetzt den Grund, warum ich meinen vorgestellten View Controller in a einbette UINavigationController, und das ist der, den ich dränge.
Sinn-Angelegenheiten
1
Sie können keinen Navigationscontroller drücken. Vielleicht haben Sie gemeint, dass Sie den Navigationscontroller präsentieren.
rmaddy
3
@jowie Wird verwendet p, nicht pobeim Drucken eines primitiven Werts. podient zum Drucken von Objekten.
rmaddy
37
Dokumentation für isBeingPresented- Diese Methode gibt nur dann JA zurück, wenn sie aus den Methoden viewWillAppear: und viewDidAppear: aufgerufen wird.
Funkt7
4
@Terrence Es scheint, dass die neueste Dokumentation diese Informationen nicht zeigt, aber sie waren früher da. Die isBeingPresented, isBeingDismissed, isMovingFromParentViewControllerund isMovingToParentViewControllersind nur gültig , innerhalb der 4 - view[Will|Did][Disa|A]ppearMethoden.
rmaddy
29

Swift 5
Hier ist eine Lösung, die das mit früheren Antworten erwähnte Problem behebt, wenn sich die isModal()Rückgabe truebei Push UIViewControllerin einem präsentierten UINavigationControllerStapel befindet.

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if navigationController?.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}

Bisher funktioniert es bei mir. Wenn einige Optimierungen vorliegen, teilen Sie diese bitte mit.

Jonauz
quelle
Warum müssen Sie überprüfen tabBarController?.presentingViewController is UITabBarController ? Ist es wichtig, ob das presentingViewControllerauch ein UITabBarController ist?
Hlung
Und wenn navigationController null ist, isModalwird zurückgegeben true. Ist das beabsichtigt?
Hlung
28

self.navigationController! = nil würde bedeuten, dass es sich in einem Navigationsstapel befindet.

Um den Fall zu behandeln, dass der aktuelle Ansichtscontroller gedrückt wird, während der Navigationscontroller modal dargestellt wird, habe ich einige Codezeilen hinzugefügt, um zu überprüfen, ob der aktuelle Ansichtscontroller der Root-Controller im Navigationsstapel ist.

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}
Jibeex
quelle
Wenn Sie modal präsentieren, platzieren Sie den viewController im Allgemeinen auf einem Navigationscontroller und präsentieren ihn. Wenn dies der Fall ist, wäre Ihre Aussage falsch, jedoch wird dieser Fall im Code behandelt. Bitte verbessern Sie Ihre Antwort :)
E-Riddie
Gute Arbeit, die sich mit allen Anwendungsfällen befasst. Platz für ein bisschen Refactoring wahrscheinlich aber trotzdem positiv !!
Jean Raymond Daher
12

Swift 4

var isModal: Bool {
    return presentingViewController != nil ||
           navigationController?.presentingViewController?.presentedViewController === navigationController ||
           tabBarController?.presentingViewController is UITabBarController
}
Charlton Provatas
quelle
Swift 4.2 / iOS 12. Funktioniert immer noch gut, aber beachten Sie, dass navigationController? .PräsentingViewController? .PräsentedViewController === navigationController als wahr ausgewertet wird, wenn beide Null sind (z. B. wenn Sie es auf einem Ansichtscontroller aufrufen, der noch nicht aufgerufen wurde vorgestellt).
Eli Burke
7

Swift 5. Sauber und einfach.

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}
Kirill Kudaev
quelle
1
Das hat den Trick für mich getan
Radu Ursache
3

Wie viele Leute hier vorschlagen, dass "Überprüfungsmethoden" nicht in allen Fällen gut funktionieren, habe ich in meinem Projekt eine Lösung gefunden, um dies manuell zu verwalten. Der Punkt ist, dass wir die Präsentation normalerweise selbst verwalten - dies geschieht nicht hinter den Kulissen und wir müssen nach innen schauen.

DEViewController.h Datei:

#import <UIKit/UIKit.h>

// it is a base class for all view controllers within a project
@interface DEViewController : UIViewController 

// specify a way viewcontroller, is presented  by another viewcontroller
// the presented view controller should manually assign the value to it
typedef NS_ENUM(NSUInteger, SSViewControllerPresentationMethod) {
    SSViewControllerPresentationMethodUnspecified = 0,
    SSViewControllerPresentationMethodPush,
    SSViewControllerPresentationMethodModal,
};
@property (nonatomic) SSViewControllerPresentationMethod viewControllerPresentationMethod;

// other properties/methods...
@end

Die Präsentationen können jetzt folgendermaßen verwaltet werden:

auf Navigationsstapel geschoben:

// DETestViewController inherits from DEViewController
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodPush;
[self.navigationController pushViewController:vc animated:YES];

Modal mit Navigation dargestellt:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
UINavigationController *nav = [[UINavigationController alloc]
                               initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];

modal präsentiert:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
[self presentViewController:vc animated:YES completion:nil];

Außerdem DEViewControllerkönnten wir der "Überprüfung" einen Fallback hinzufügen, wenn die oben genannte Eigenschaft gleich ist SSViewControllerPresentationMethodUnspecified:

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}
Yevhen Dubinin
quelle
3

Angenommen, alle ViewController, die Sie modal präsentieren, sind in einem neuen Navigationscontroller verpackt (was Sie sowieso immer tun sollten), können Sie diese Eigenschaft Ihrem VC hinzufügen.

private var wasPushed: Bool {
    guard let vc = navigationController?.viewControllers.first where vc == self else {
        return true
    }

    return false
}
Demosthese
quelle
1
was du sowieso immer tun solltest - bitte erkläre warum?
Alexander Abakumov
Alexander, das solltest du eigentlich nicht.
Nickdnk
2

Um zu erkennen, dass Ihr Controller gedrückt wird oder nicht, verwenden Sie den folgenden Code an einer beliebigen Stelle:

if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {

    // Not pushed
}
else {

    // Pushed
}

Ich hoffe, dieser Code kann jedem helfen ...

Arash Zeinoddini
quelle
1
Diese Methode funktioniert nicht, wenn Sie dieselbe View Controller-Klasse an mehreren Stellen verwenden, da nur die Klasse davon überprüft wird. Sie können stattdessen die Gleichheit explizit überprüfen.
Gklka
1

self.navigationController != nil würde bedeuten, dass es in einem Navigationsstapel ist.

Daniel
quelle
25
Kann immer noch in einem modalen Navigationscontroller sein
ColdLogic
"Modal" und "Pushed on Navigation Stack" schließen sich also nicht gegenseitig aus. Dies zu denken hängt vom Kontext ab. Wenn Sie jedoch überprüfen, ob self.navigationController nicht gleich Null ist, wird nicht beantwortet, ob es sich um einen Ansichtscontroller eines Navigationscontrollers handelt.
Daniel
@ Daniel Der Unterschied ist zwischen "gedrückt" und "präsentiert". "Modal" hat nichts damit zu tun. Ich glaube, "ColdLogic" bedeutete "präsentiert", als sie "modal" sagten.
rmaddy
1
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}
mkto
quelle
0

Wenn Sie ios 5.0 oder höher verwenden, verwenden Sie bitte diesen Code

-(BOOL)isPresented
{
    if ([self isBeingPresented]) {
        // being presented
         return YES;
    } else if ([self isMovingToParentViewController]) {
        // being pushed
         return NO;
    } else {
        // simply showing again because another VC was dismissed
         return NO;
    }
}
Shahbaz Abbasi
quelle
0

Swift 5
Diese praktische Erweiterung behandelt nur wenige Fälle mehr als frühere Antworten. Diese Fälle sind VC (View Controller) ist die Stamm-VC des App-Fensters, VC wird als untergeordnete VC zur übergeordneten VC hinzugefügt. Es wird nur dann versucht, true zurückzugeben, wenn der Viewcontroller modal dargestellt wird.

extension UIViewController {
    /**
      returns true only if the viewcontroller is presented.
    */
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            if let parent = parent, !(parent is UINavigationController || parent is UITabBarController) {
                return false
            }
            return true
        } else if let navController = navigationController, navController.presentingViewController?.presentedViewController == navController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        }
        return false
    }
}

Dank Jonauz 'Antwort . Auch hier ist Platz für weitere Optimierungen. Bitte besprechen Sie den Fall, der behandelt werden muss, im Kommentarbereich.

Mehedi Hasan
quelle
-1

Für jemanden, der sich fragt, wie man ViewController mitteilt, dass es präsentiert wird

wenn Apräsentiert / drücktB

  1. Definieren Sie ein enumund propertyinB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Überprüfen Sie nun im AAnsichts-Controller, Bob er durch Zuweisen angezeigt / verschoben wirdpresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Verwendung in BView Controller

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }
Saif
quelle
-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

Dadurch erfahren Sie, ob viewController angezeigt oder gepusht wird

iCoder86
quelle
4
Diese Eigenschaft ist veraltet.
Morkrom