È possibile determinare se ViewController viene presentato come modale?


Risposte:


96

Poiché modalViewControllerè stato deprecato in iOS 6, ecco una versione che funziona per iOS 5+ e che si compila senza avvisi.

Objective-C:

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

Swift:

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

Punta di cappello alla risposta di Felipe.


2
buona presa, ho dovuto usarlo di nuovo dopo molto tempo e ho notato che la deprecazione è avvenuta ... Ho modificato la mia risposta in modo che le persone inizino a cercare qui il codice corretto quando usano iOS 6+, grazie
Felipe Sabino

10
Non funziona se il controller di visualizzazione genitore è un modale su cui viene premuto il controller di visualizzazione.
significato-conta

2
C'è un bug, dovremmo controllare se entrambi i lati sono nulli, perché nil == nilritorna YES, e non è il risultato che vogliamo.
CocoaBob

1
@GabrielePetronella Ti dispiace se aggiorno la risposta per includere anche un'implementazione Swift del metodo?
Michael Waterfall

1
@MichaelWaterfall che sarebbe molto apprezzato, grazie
Gabriele Petronella

77

Se stai cercando iOS 6+, questa risposta è deprecata e dovresti controllare la risposta di Gabriele Petronella


Non esiste un modo pulito per farlo, come proprietà o metodo nativo di UIKit. Quello che puoi fare è controllare diversi aspetti del tuo controller per assicurarti che sia presentato come modale.

Quindi, per verificare se il controller corrente (rappresentato come selfnel codice sotto) è presentato in modo modale o meno, ho la funzione qui sotto o in una UIViewControllercategoria, o (se il tuo progetto non ha bisogno di usare altri controller UIKit, come UITableViewControllerad esempio) in un controller di base ereditato dagli altri miei controller

-(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;        

}

EDIT: ho aggiunto l'ultimo controllo per vedere se viene utilizzato un UITabBarController e presenti un altro UITabBarController come modale.

EDIT 2: aggiunto il controllo iOS 5+, dove UIViewControllernon risponde parentViewControllerpiù, ma presentingViewControllerinvece.

EDIT 3: ho creato un succo per questo nel caso https://gist.github.com/3174081


Tieni presente che la modalViewControllerproprietà è deprecata a partire da iOS 6. La documentazione suggerisce di utilizzare presentedViewControllerinvece.
Bart Jacobs,

@BartJacobs buon punto! Non ho guardato questa risposta dopo il rilascio di iOS6, quindi potrebbe non essere aggiornata. Proverò a fare alcuni test più avanti nella settimana per aggiornarlo, tks!
Felipe Sabino

NSLog(@"%@", self.navigationController.parentViewController)stampe (null)- potresti spiegare perché? Il mio ViewController è connesso al controller di visualizzazione modale tramite navController nello storyboard.
Roman

@oyatek puoi usare pastebin o qualcosa di simile e mostrare del codice?
Felipe Sabino

@ Feilpe ho trovato il problema - .parentViewControllerè deprecato, .presentingViewControllerdeve essere utilizzato invece.
Roman

35

In iOS5 +, come puoi vedere in UIViewController Class Reference , puoi ottenerlo dalla proprietà "presentationViewController".

presentandoViewController Il controller di visualizzazione che ha presentato questo controller di visualizzazione. (sola lettura)

@property (nonatomic, readonly) UIViewController * presentationViewController
Discussione

Se il controller di visualizzazione che ha ricevuto questo messaggio viene presentato da un altro controller di visualizzazione, questa proprietà contiene il controller di visualizzazione che lo presenta. Se il controller della visualizzazione non viene presentato, ma viene presentato uno dei suoi antenati, questa proprietà detiene il controller della visualizzazione che presenta l'antenato più vicino. Se non vengono presentati né il view controller né alcuno dei suoi antenati, questa proprietà è nulla.

Disponibilità
Disponibile in iOS 5.0 e versioni successive.
Dichiarato in
UIViewController.h


3
Funziona perfettamente, usa if (self.presentingViewController) {// This is a modal viewContoller} else {// This is a normal ViewController}
mashdup

2
IMHO, questa è l' unica risposta corretta qui. Controlla solo la presenza di un file presentingViewController. Funzionerà anche nei controller di visualizzazione del contenitore, poiché attraversa automaticamente gli antenati.
Daniel Rinser

17

In caso contrario, è possibile definire una proprietà per this ( presentedAsModal) nella sottoclasse UIViewController e impostarla su YESprima di presentare ViewController come vista modale.

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

Puoi controllare questo valore nel tuo viewWillAppearoverride.

Credo che non ci sia una proprietà ufficiale che dichiari come viene presentata la vista, ma nulla ti impedisce di crearne una tua.


Giusto e questo è quello che ho fatto, ma stavo cercando qualche altra soluzione pulita. Grazie.
lukewar

questa soluzione non funziona se si presenta UINavigationControllercome modale ... a meno che non si crei un controller di navigazione personalizzato solo per aggiungere questa proprietà. Dopodiché, all'interno dei controller, dovrai continuare a trasmettere self.navigationControllera questa classe personalizzata ogni volta che devi controllare se il controller è presentato come modale
Felipe Sabino

8

La risposta di Petronella non funziona se self.navigationController è presentato in modo modale ma self non è uguale a self.navigationController.viewControllers [0], in quel caso self viene spinto.

Ecco come potresti risolvere il problema.

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]];

E 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

6

Questo dovrebbe funzionare.

if(self.parentViewController.modalViewController == self)…

Purtroppo questo non funziona. È stato il mio primo tentativo. Ma ha restituito modalViewController ins nil :(.
lukewar

Se ottieni solo "self.parentViewController", restituisce l'oggetto genitore corretto?
kubi

4
Il problema potrebbe essere che la tua sottoclasse UIViewController si trova all'interno di un UINavigationController o di un UITabBarController (o entrambi), nel qual caso potresti dover scavare un po 'di più nella gerarchia della visualizzazione per scoprire il genitore che è stato presentato come controller di visualizzazione modale.
hpique

@hgpc Ho bisogno di questo chck nel mio progetto, così ho solo aggiunto una risposta a verificare la presenza di entrambi UINavigationControllere UITabBarControllercasi. Sta funzionando abbastanza bene finora
Felipe Sabino

4

Il modo migliore per controllare

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }

2

Se non è necessario distinguere tra viste modali a schermo intero e viste non modali, come nel mio progetto (avevo a che fare con un problema che si verifica solo con i fogli modulo e i fogli di pagina), puoi utilizzare modalPresentationStyle proprietà di 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;
}

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
}

C'è un problema con questo caso d'uso. Se sono in un controller di visualizzazione radice di un UINavigationController, restituisce comunque true senza alcuna presentazione modale.
mariusLAN

1
La prima istruzione if copre tutto ciò che è nella seconda istruzione if, rendendo ridondante la seconda istruzione. Non sono sicuro di quale sia l'intenzione qui.
isoiphone

1

Nel mio progetto ho un controller di visualizzazione (Dettaglio) che può essere presentato in modo modale (quando si aggiunge un nuovo elemento) o con push (quando si modifica uno esistente) dal controller di visualizzazione Master. Quando l'utente tocca [Fine], il controller della vista dettagliata chiama il metodo del controller della vista principale per notificare che è pronto per essere chiuso. Il Maestro deve determinare come viene presentato il dettaglio per sapere come chiuderlo. Ecco come lo faccio:

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

0

Un trucco come questo potrebbe funzionare.

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
}

Tuttavia, penso che la mia risposta precedente sia una soluzione più pulita.


0

Quello che ha funzionato per me è seguire:

// 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);

Per quanto l'ho testato, funziona per iOS7 e iOS8. Tuttavia, non ho provato su iOS6.


0

Ho guardato un po 'in giro per trovare la risposta giusta a questa domanda e non sono riuscito a trovare nessuna che coprisse tutti i possibili scenari. Ho scritto queste poche righe di codice che sembrano fare il lavoro. Puoi trovare alcuni commenti in linea per capire cosa è stato controllato.

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

Spero che questo aiuto.


0

Ecco la mia versione modificata di @ GabrielePetronella's isModal, che funziona con i controller di visualizzazione contenuti in quanto sale prima nella gerarchia di parentViewController. Inoltre ha estratto il codice in più righe in modo che sia chiaro cosa sta facendo.

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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.