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


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.


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


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


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.


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

6

To powinno działać.

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

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");
    }

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

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
}

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

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.


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.


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.


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
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.