Jak sprawdzić, czy kontroler widoku jest prezentowany modalnie lub wypychany na stosie nawigacyjnym?


126

Jak w kodzie kontrolera widoku mogę rozróżnić:

  • prezentowane modalnie
  • na stosie nawigacyjnym

Obie presentingViewControlleri isMovingToParentViewControllerYESw obu przypadkach, więc nie są zbyt pomocne.

To, co komplikuje sprawę, to fakt, że mój nadrzędny kontroler widoku jest czasami modalny, na który wpychany jest kontroler widoku do sprawdzenia.

Okazuje się, że moim problemem jest to, że osadzam mój HtmlViewControllerplik, UINavigationControllerktóry jest następnie prezentowany. Dlatego moje własne próby i poniższe dobre odpowiedzi nie działały.

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

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

Wydaje mi się, że lepiej byłoby powiedzieć mojemu kontrolerowi widoku, kiedy jest modalny, zamiast próbować określić.

Odpowiedzi:


125

Weź z przymrużeniem oka, nie testowałem.

- (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
Znalazłem to w innym poście SO. Ale nie działa, jeśli rodzic kontrolera widoku wypchniętego jest modalny; jaka jest sytuacja, którą mam.
znaczenie ma znaczenie

2
Jak napisałem, presentingViewControllerjest zawsze YESw moim przypadku; nie pomaga.
znaczenie ma znaczenie

3
presentingViewControllerzwraca YESdla wypchniętego VC, gdy istnieje UITabBarControllerbyt ustawiony jako root. Więc nie pasuje w moim przypadku.
Yevhen Dubinin

5
To nie działa, jeśli przedstawisz kontroler widoku, a następnie wypchnie on inny.
Lee

3
„To nie zadziała, jeśli przedstawisz kontroler widoku, a następnie wypchnie on inny”. Nie jest to celem, ponieważ przekazany kontroler widoku nie jest prezentowany.
Colin Swelin,

87

W Swift :

Dodaj flagę, aby sprawdzić, czy jest modalna według typu klasy:

// 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
Powinno być lepiej w var, jakvar isModal: Bool {}
malinois

1
@malinois zostaje zmienione
YannSteph

Co robi ostatni falseparametr w returninstrukcji?
damjandd,

musisz zmienić, aby umożliwić PresentingIsNavigation = navigationController? .presentingViewController? .presentedViewController == navigationController && navigationController! = nil
famfamfam

78

Państwo pominąć jedną metodę: isBeingPresented.

isBeingPresented jest prawdziwe, gdy kontroler widoku jest prezentowany i fałszywy, gdy jest wypychany.

- (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
Próbowałem to też przed wysłaniem, a to nie działa, isBeingPresentedjest NO. Ale teraz widzę powód, osadzam mój kontroler widoku prezentowanego w a UINavigationController, i to jest ten, który naciskam.
znaczenie ma znaczenie

1
Nie możesz nacisnąć kontrolera nawigacyjnego. Być może chodziło Ci o to, że prezentujesz kontroler nawigacyjny.
rmaddy

3
@jowie Użyj p, a nie popodczas drukowania wartości pierwotnej. posłuży do drukowania obiektów.
rmaddy

37
Dokumentacja dla isBeingPresented- ta metoda zwraca wartość YES tylko wtedy, gdy jest wywoływana z wnętrza metod viewWillAppear: i viewDidAppear:.
funct7

4
@Terrence Wygląda na to, że najnowsza dokumentacja nie pokazuje tych informacji, ale kiedyś tam były. isBeingPresented, isBeingDismissed, isMovingFromParentViewControllerI isMovingToParentViewControllersą ważne tylko wewnątrz 4 view[Will|Did][Disa|A]ppearmetod.
rmaddy

29

Swift 5
Oto rozwiązanie, które rozwiązuje problem wspomniany w poprzednich odpowiedziach, kiedy isModal()zwraca, truejeśli push UIViewControllerjest w prezentowanym UINavigationControllerstosie.

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

Jak na razie to działa. Jeśli jakieś optymalizacje, udostępnij.


Dlaczego musisz to sprawdzić tabBarController?.presentingViewController is UITabBarController ? Czy to ma znaczenie, jeśli presentingViewControllerjest to również UITabBarController?
Hlung

A jeśli navigationController ma wartość nil, isModalzwróci true. Czy to jest zamierzone?
Hlung

28

self.navigationController! = nil oznaczałoby, że znajduje się w stosie nawigacyjnym.

Aby obsłużyć przypadek, w którym bieżący kontroler widoku jest wypychany, podczas gdy kontroler nawigacji jest prezentowany modalnie, dodałem kilka wierszy kodu, aby sprawdzić, czy bieżący kontroler widoku jest kontrolerem głównym w stosie nawigacji.

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

Ogólnie rzecz biorąc, kiedy prezentujesz modalnie, umieszczasz viewController na navigationController i prezentujesz go. Jeśli tak jest, twoje oświadczenie byłoby błędne, jednak na kodzie ten przypadek jest obsługiwany. Popraw swoją odpowiedź :)
E-Riddie

dobra robota, która zajmuje się wszystkimi przypadkami użycia. miejsce na trochę refaktoryzacji prawdopodobnie ale nadal upvote !!
Jean Raymond Daher

12

Szybki 4

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

Swift 4.2 / iOS 12. Nadal działa dobrze, ale pamiętaj, że navigationController? .PresentingViewController? .PresentedViewController === navigationController oceni jako true, jeśli oba są zerowe (na przykład, jeśli wywołasz to na kontrolerze widoku, który jeszcze nie został przedstawione).
Eli Burke,


3

Jak wielu ludzi tutaj sugeruje, że metody „sprawdzania” nie działają dobrze we wszystkich przypadkach, w moim projekcie wymyśliłem rozwiązanie umożliwiające ręczne zarządzanie. Chodzi o to, że zwykle zarządzamy prezentacją samodzielnie - to nie jest to, co dzieje się za kulisami i musimy introspekować.

DEViewController.h plik:

#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

Prezentacje można teraz zarządzać w ten sposób:

na stosie nawigacyjnym:

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

prezentowane modalnie z nawigacją:

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

przedstawione modalnie:

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

Również w DEViewControllermoglibyśmy dodać funkcję powrotu do „sprawdzenia” czy wspomniana nieruchomość jest równa 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

Zakładając, że wszystkie viewControllery, które prezentujesz modalnie, są opakowane w nowy navigationController (co i tak zawsze powinieneś robić), możesz dodać tę właściwość do swojego VC.

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

    return false
}

1
które i tak powinieneś zawsze robić - proszę wyjaśnić dlaczego?
Alexander Abakumov

Alexander, naprawdę nie powinieneś.
nickdnk

2

Aby wykryć, że kontroler jest wciśnięty lub nie wystarczy użyć poniższego kodu w dowolnym miejscu:

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

    // Not pushed
}
else {

    // Pushed
}

Mam nadzieję, że ten kod może pomóc każdemu ...


1
Ta metoda nie działa, gdy używasz tej samej klasy kontrolera widoku w wielu miejscach, ponieważ sprawdza tylko jej klasę. Zamiast tego możesz jawnie sprawdzić równość.
gklka

1

self.navigationController != nil oznaczałoby, że jest w stosie nawigacyjnym.


25
Nadal może znajdować się w modalnym kontrolerze nawigacji
ColdLogic

Tak więc „modalne” i „wypchnięte na stosie nawigacyjnym” nie wykluczają się wzajemnie. Myślenie to zależy od kontekstu, ale sprawdzenie, czy self.navigationController nie jest równe nil, daje odpowiedź, czy jest to kontroler widoku kontrolera nawigacji.
Daniel

@Daniel Różnica jest między „wypchniętym” a „zaprezentowanym”. „Modal” nie ma z tym nic wspólnego. Myślę, że „ColdLogic” oznaczało „prezentowany”, kiedy mówili „modalne”.
rmaddy,

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

1

Swift 5
To poręczne rozszerzenie obsługuje kilka przypadków więcej niż poprzednie odpowiedzi. Te przypadki to VC (kontroler widoku), który jest głównym VC okna aplikacji, VC jest dodawany jako dziecko do nadrzędnego VC. Próbuje zwrócić prawdę tylko wtedy, gdy kontroler widoku jest przedstawiony modalnie.

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

Dzięki odpowiedzi Jonauza . Znowu jest miejsce na więcej optymalizacji. Prosimy o omówienie sprawy, którą należy załatwić w sekcji komentarzy.


0

Jeśli używasz ios 5.0 lub nowszego, użyj tego kodu

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

-1

Dla kogoś, kto się zastanawia, jak powiedzieć ViewController, że jest prezentowany

jeśli Aprezentuje / pchaB

  1. Zdefiniuj enumi propertywB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Teraz w Awidoku kontrolera powiedz, Bczy jest prezentowany / popychany, przypisującpresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Użycie w Bkontrolerze widoku

    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
}

Dzięki temu wiesz, czy viewController jest prezentowany czy wypychany


4
Ta właściwość jest przestarzała.
Morkrom
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.