Come verificare se un controller di visualizzazione viene presentato in modo modale o inserito in uno stack di navigazione?


126

Come posso, nel mio codice del controller di visualizzazione, distinguere tra:

  • presentato in modo modale
  • inserito nello stack di navigazione

Entrambi presentingViewControllere isMovingToParentViewControllersono YESin entrambi i casi, quindi non sono molto utili.

Ciò che complica le cose è che il mio controller di visualizzazione genitore a volte è modale, su cui viene premuto il controller di visualizzazione da controllare.

Si scopre che il mio problema è che ho incorporato il mio HtmlViewControllerin un UINavigationControllerche viene quindi presentato. Ecco perché i miei tentativi e le buone risposte di seguito non hanno funzionato.

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

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

Immagino che farei meglio a dire al mio controller di visualizzazione quando è modale, invece di provare a determinare.

Risposte:


125

Prendi con un pizzico di sale, non testato.

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

12
L'ho trovato in un altro post SO. Ma non funziona se il genitore del controller di visualizzazione spinto è un modale; qual è la situazione che sto avendo.
significato-conta

2
Come ho scritto, presentingViewControllerè sempre YESnel mio caso; non aiuta.
significato-conta

3
presentingViewControllerrestituisce YESper VC spinto, quando c'è un UITabBarControlleressere impostato come root. Quindi, non è adatto nel mio caso.
Yevhen Dubinin,

5
Questo non funziona se presenti un controller di visualizzazione, quindi ne spinge un altro.
Lee

3
"Questo non funziona se si presenta un controller di visualizzazione, quindi ne spinge un altro" Non è questa l'intenzione, il controller di visualizzazione spostato non viene presentato.
Colin Swelin

87

In Swift :

Aggiungi un flag per verificare se è un modale dal tipo di classe:

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

4
Dovrebbe essere migliore in un var, comevar isModal: Bool {}
malinois

1
@malinois è cambiato
YannSteph

Cosa fa l'ultimo falseparametro returnnell'istruzione?
damjandd

hai bisogno di modifiche per consentire a presentationIsNavigation = navigationController? .presentingViewController? .presentedViewController == navigationController && navigationController! = nil
famfamfam

78

È trascurato un metodo: isBeingPresented.

isBeingPresented è vero quando viene presentato il controller di visualizzazione e falso quando viene inviato.

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

2
Ho provato anche questo prima di postare, e non funziona, isBeingPresentedè NO. Ma ora vedo il motivo, sto incorporando il mio controller di visualizzazione presentato in a UINavigationController, ed è quello che sto spingendo.
significato-conta

1
Non puoi spingere un controller di navigazione. Forse volevi dire che stai presentando il controller di navigazione.
rmaddy

3
@jowie Usa p, non poquando si stampa un valore primitivo. poserve per stampare oggetti.
rmaddy

37
Documentazione per isBeingPresented: questo metodo restituisce YES solo quando viene chiamato dall'interno dei metodi viewWillAppear: e viewDidAppear:.
funct7

4
@Terrence Sembra che la documentazione più recente non mostri queste informazioni, ma prima c'era. La isBeingPresented, isBeingDismissed, isMovingFromParentViewControllere isMovingToParentViewControllersono validi solo all'interno dei 4 view[Will|Did][Disa|A]ppearmetodi.
rmaddy

29

Swift 5
Ecco la soluzione che risolve il problema menzionato con le risposte precedenti, quando i isModal()ritorni truese spinti si UIViewControllertrovano in uno UINavigationControllerstack presentato .

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

Per me finora funziona. Se alcune ottimizzazioni, condividi.


Perché devi controllare tabBarController?.presentingViewController is UITabBarController ? Ha importanza se anche questo presentingViewControllerè un UITabBarController?
Hlung

E se navigationController è nullo, isModaltornerà true. Questo è inteso?
Hlung

28

self.navigationController! = nil significherebbe che è in uno stack di navigazione.

Per gestire il caso in cui il controller di visualizzazione corrente venga premuto mentre il controller di navigazione è presentato in modo modale, ho aggiunto alcune righe di codice per verificare se il controller di visualizzazione corrente è il controller principale nello stack di navigazione.

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

In generale, quando presenti in modo modale, metti viewController su un navigationController e lo presenti. Se è così, la tua dichiarazione sarebbe sbagliata, tuttavia sul codice questo caso è gestito. Per favore, migliora la tua risposta :)
E-Riddie

buon lavoro che si occupa di tutti i casi d'uso. spazio per un po 'di refactoring probabilmente ma ancora positivo !!
Jean Raymond Daher

12

Swift 4

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

Swift 4.2 / iOS 12. Funziona ancora bene, ma tieni presente che navigationController? .PresentingViewController? .PresentedViewController === navigationController restituirà true se entrambi sono nulli (ad esempio, se lo chiami su un controller di visualizzazione che non è ancora stato presentato).
Eli Burke

7

Swift 5. Pulito e semplice.

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}

1
questo ha funzionato per me
Radu Ursache il

3

Come molte persone qui suggeriscono, che i metodi di "controllo" non funzionano bene per tutti i casi, nel mio progetto ho trovato una soluzione per gestirlo manualmente. Il punto è che di solito gestiamo la presentazione da soli - questo non è ciò che accade dietro le quinte e dobbiamo introspettirci.

DEViewController.h file:

#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

Le presentazioni ora potrebbero essere gestite in questo modo:

inserito nello stack di navigazione:

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

presentato in modo modale con la navigazione:

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

presentato in modo modale:

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

Inoltre, DEViewControllerpotremmo aggiungere un fallback al "controllo" se la suddetta proprietà è uguale a 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;
    }
}

3

Supponendo che tutti i viewController che presenti in modo modale siano racchiusi in un nuovo navigationController (cosa che dovresti sempre fare comunque), puoi aggiungere questa proprietà al tuo VC.

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

    return false
}

1
che dovresti sempre fare comunque - per favore spiega perché?
Alexander Abakumov

Alexander, non dovresti, davvero.
nickdnk

2

Per rilevare il tuo controller viene premuto o meno, usa il codice sottostante ovunque tu voglia:

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

    // Not pushed
}
else {

    // Pushed
}

Spero che questo codice possa aiutare chiunque ...


1
Questo metodo non funziona quando si utilizza la stessa classe del controller di visualizzazione in più posizioni, poiché ne controlla solo la classe. Puoi invece controllare esplicitamente l'uguaglianza.
gklka

1

self.navigationController != nil significherebbe che è in uno stack di navigazione.


25
Può ancora essere in un controller di navigazione modale
ColdLogic

Quindi "modale" e "inserito nello stack di navigazione" non si escludono a vicenda. Pensare che questo dipenda dal contesto, ma controllare se self.navigationController non è nullo risponde se si tratta di un controller di visualizzazione di un controller di navigazione.
Daniel

@Daniel La differenza è tra "spinto" e "presentato". "Modal" non ha nulla a che fare con questo. Credo che "ColdLogic" significasse "presentato" quando dicevano "modale".
rmaddy

1
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}

0

Se utilizzi iOS 5.0 o versioni successive, utilizza questo codice

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

0

Swift 5
Questa pratica estensione gestisce pochi casi in più rispetto alle risposte precedenti. Questi casi sono VC (view controller) è il VC radice della finestra dell'app, VC viene aggiunto come figlio al VC padre. Prova a restituire true solo se il viewcontroller viene presentato in modo modale.

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

Grazie alla risposta di Jonauz . Anche in questo caso c'è spazio per ulteriori ottimizzazioni. Si prega di discutere il caso che deve essere gestito nella sezione commenti.


-1

Per qualcuno che si sta chiedendo, come dire a ViewController che viene presentato

se Asta presentando / spingendoB

  1. Definisci un enumand propertyinB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Ora nel Acontroller di visualizzazione, Bindica se viene presentato / inviato tramite assegnazionepresentationStyle

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

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }

-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

Questo ti farà sapere se viewController viene presentato o premuto


4
Questa proprietà è deprecata.
Mork dal
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.