Czy można określić, czy ViewController jest przedstawiany jako modalny?

117

Czy można sprawdzić wewnątrz klasy ViewController, że jest prezentowany jako kontroler widoku modalnego?

letni
źródło

Odpowiedzi:

96

Ponieważ modalViewControllerzostała wycofana w iOS 6, oto wersja, która działa na iOS 5+ i która kompiluje się bez ostrzeżeń.

Cel C:

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

Szybki:

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

Cynk kapelusza do odpowiedzi Felipe.

Gabriele Petronella
źródło
2
dobry połów, po prostu musiałem go użyć ponownie po długim czasie i zauważył, że dezaprobata stało ... I edytowane moją odpowiedź tak, że ludzie zacząć szukać tutaj poprawnego kodu przy użyciu iOS 6 +, dzięki
Felipe Sabino
10
Nie działa, jeśli nadrzędny kontroler widoku jest modalny, na który wypychany jest nasz kontroler widoku.
znaczenie ma znaczenie
2
Jest błąd, powinniśmy sprawdzić, czy obie strony są zerowe, ponieważ nil == nilzwraca YES, a nie jest to wynik, którego chcemy.
CocoaBob
1
@GabrielePetronella Czy masz coś przeciwko, jeśli zaktualizuję odpowiedź, tak aby zawierała również szybką implementację metody?
Michael Waterfall
1
@MichaelWaterfall, który byłby bardzo mile widziany, dzięki
Gabriele Petronella
77

Jeśli szukasz systemu iOS 6+, ta odpowiedź jest przestarzała i powinieneś sprawdzić odpowiedź Gabriele Petronella


Nie ma na to zgrabnego sposobu, ponieważ jest to właściwość lub metoda natywna dla UIKit. Możesz sprawdzić kilka aspektów swojego kontrolera, aby upewnić się, że jest on przedstawiony jako modalny.

Tak więc, aby sprawdzić, czy obecny (przedstawiony selfw kodzie poniżej) kontroler jest przedstawiony w sposób modalny, czy nie, mam poniżej funkcję albo w UIViewControllerkategorii, albo (jeśli twój projekt nie musi używać innych kontrolerów UIKit, jak UITableViewControllerna przykład) w kontrolerze podstawowym, który dziedziczą moje inne kontrolery

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

}

EDYCJA: Dodałem ostatnie sprawdzenie, czy używany jest UITabBarController, i przedstawiasz inny UITabBarController jako modalny.

EDYCJA 2: dodano kontrolę iOS 5+, gdzie UIViewControllerjuż nie odpowiada parentViewController, ale presentingViewControllerzamiast tego.

EDYCJA 3: Stworzyłem dla niego sedno na wszelki wypadek https://gist.github.com/3174081

Felipe Sabino
źródło
Należy pamiętać, że ta modalViewControllerwłaściwość jest przestarzała od wersji iOS 6. Dokumentacja sugeruje jej użycie presentedViewController.
Bart Jacobs
@BartJacobs dobra uwaga! Nie spojrzałem na tę odpowiedź po wydaniu iOS6, więc może być nieaktualna. Postaram się zrobić kilka testów w dalszej części tygodnia, aby to zaktualizować, tks!
Felipe Sabino
NSLog(@"%@", self.navigationController.parentViewController)wydruki (null)- czy mógłbyś wyjaśnić dlaczego? Mój ViewController jest połączony z kontrolerem widoku modalnego poprzez navController w scenorysie.
Roman
@oyatek czy możesz użyć pastebin lub czegoś podobnego i pokazać jakiś kod?
Felipe Sabino
@Feilpe Znalazłem problem - .parentViewControllerjest przestarzały, .presentingViewControllernależy go zamiast tego użyć.
Roman
35

W iOS5 +, jak widać w odwołaniu do klasy UIViewController , można je pobrać z właściwości „ presentViewController ”.

PresentViewController Kontroler widoku, który przedstawił ten kontroler widoku. (tylko czytać)

@property (nonatomic, readonly) UIViewController * presentViewController
Discussion

Jeśli kontroler widoku, który odebrał ten komunikat, jest prezentowany przez inny kontroler widoku, ta właściwość zawiera kontroler widoku, który go przedstawia. Jeśli kontroler widoku nie jest prezentowany, ale prezentowany jest jeden z jego przodków, ta właściwość przechowuje kontroler widoku przedstawiający najbliższego przodka. Jeśli nie jest przedstawiony ani kontroler widoku, ani żaden z jego przodków, ta właściwość jest zerowa.

Dostępność
Dostępne w iOS 5.0 i nowszych.
Zadeklarowane w
UIViewController.h

Raj
źródło
3
Działa idealnie, użyj if (self.presentingViewController) {// This is a modal viewContoller} else {// This is a normal ViewController}
mashdup
2
IMHO, to jedyna poprawna odpowiedź tutaj. Po prostu sprawdź obecność pliku presentingViewController. Będzie również działać w kontrolerach widoku kontenera, ponieważ automatycznie przechodzi przez przodków.
Daniel Rinser
17

Jeśli tak nie jest, możesz zdefiniować właściwość this ( presentedAsModal) w podklasie UIViewController i ustawić ją na YESprzed przedstawieniem ViewController jako widoku modalnego.

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

Możesz sprawdzić tę wartość w swoim viewWillAppearnadpisaniu.

Uważam, że nie ma oficjalnej właściwości określającej sposób prezentacji widoku, ale nic nie stoi na przeszkodzie, aby utworzyć własny.

hpique
źródło
RIght i tak właśnie zrobiłem, ale szukałem innego zgrabnego rozwiązania. Dzięki.
letni
to rozwiązanie nie działa, jeśli prezentujesz UINavigationControllerjako modalne ... chyba że utworzysz niestandardowy kontroler nawigacji tylko po to, aby dodać tę właściwość. A potem, wewnątrz kontrolerów, będziesz musiał przesyłać self.navigationControllerdo tej niestandardowej klasy za każdym razem, gdy będziesz musiał sprawdzić, czy kontroler jest prezentowany jako modalny
Felipe Sabino,
8

Odpowiedź Petronelli nie działa, jeśli self.navigationController jest prezentowane modalnie, ale self nie jest równe self.navigationController.viewControllers [0], w tym przypadku self jest wypychane.

Oto, jak możesz rozwiązać problem.

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

A w 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
źródło
6

To powinno działać.

if(self.parentViewController.modalViewController == self)…
kubi
źródło
Niestety to nie działa. To była moja pierwsza próba. Ale zwrócił modalViewController ins nil :(.
lukewar
Jeśli otrzymujesz po prostu „self.parentViewController”, czy zwraca on prawidłowy obiekt nadrzędny?
kubi
4
Problem może polegać na tym, że podklasa UIViewController znajduje się wewnątrz UINavigationController lub UITabBarController (lub obu), w takim przypadku może zajść potrzeba dokładniejszego zbadania hierarchii widoków, aby znaleźć element nadrzędny, który został przedstawiony jako kontroler widoku modalnego.
hpique
@hgpc Potrzebowałem tego chck w moim projekcie, więc właśnie dodałem odpowiedź, aby sprawdzić oba przypadki UINavigationControlleri UITabBarController. Jak na razie działa całkiem nieźle
Felipe Sabino
4

Najlepszy sposób na sprawdzenie

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Sunny Shah
źródło
2

Jeśli nie musisz rozróżniać między pełnoekranowymi widokami modalnymi a widokami niemodalnymi, co ma miejsce w moim projekcie (miałem do czynienia z problemem, który występuje tylko w przypadku arkuszy formularzy i arkuszy stron), możesz skorzystać z modalPresentationStyle właściwość 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
źródło
2

W 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
}
King-Wizard
źródło
Wystąpił problem z tym przypadkiem użycia. Jeśli jestem w głównym kontrolerze widoku UINavigationController, nadal zwraca wartość true bez żadnej prezentacji modalnej.
mariusLAN
1
Pierwsza instrukcja if obejmuje wszystko, co znajduje się w drugiej instrukcji if, czyniąc drugą instrukcję zbędną. Nie jestem pewien, jaki jest zamiar.
isoiphone
1

W moim projekcie mam kontroler widoku (Szczegóły), który może być prezentowany modalnie (podczas dodawania nowego elementu) lub za pomocą push (podczas edycji istniejącego) przez kontroler widoku Master. Gdy użytkownik naciśnie przycisk [Gotowe], kontroler widoku szczegółowego wywołuje metodę kontrolera widoku głównego, aby powiadomić, że jest gotowy do zamknięcia. Mistrz musi określić, w jaki sposób przedstawiony jest szczegół, aby wiedzieć, jak go zamknąć. Oto jak to robię:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}
Olex
źródło
0

Taki hack może zadziałać.

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
}

Myślę jednak, że moja poprzednia odpowiedź jest czystszym rozwiązaniem.

hpique
źródło
0

To, co dla mnie zadziałało, to:

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

O ile to przetestowałem, działa to na iOS7 i iOS8. Nie próbowałem jednak na iOS6.

mixtly87
źródło
0

Rozejrzałem się trochę, aby znaleźć właściwą odpowiedź na to pytanie i nie mogłem znaleźć żadnej, która obejmowałaby wszystkie możliwe scenariusze. Napisałem kilka wierszy kodu, które wydają się działać. Możesz znaleźć kilka komentarzy w tekście, aby dowiedzieć się, co zostało sprawdzone.

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

Mam nadzieję, że to pomoże.

DennyLou
źródło
0

Oto moja zmodyfikowana wersja @ GabrielePetronella's isModal, która działa z zawartymi kontrolerami widoku, ponieważ najpierw przechodzi w górę hierarchii parentViewController. Wyciągnęliśmy również kod do wielu wierszy, aby było jasne, co robi.

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
źródło