Чи можна визначити, чи ViewController представлений як Modal?


117

Чи можна перевірити в класі ViewController, чи він представлений як контролер модального перегляду?

Відповіді:


96

Оскільки modalViewControllerзаставлено в iOS 6, ось версія, яка працює для iOS 5+ і компілюється без попереджень.

Завдання-C:

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

Швидкий:

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

Підказка капелюха на відповідь Феліпе.


2
хороший улов, я просто повинен був використовувати його знову через довгий час і помітив, що старіння сталося ... Я відредагував свою відповідь, щоб люди почали шукати тут правильний код під час використання iOS 6+, дякую
Феліпе Сабіно

10
Не працює, якщо контролер батьківського перегляду є модальним способом, на який натискається наш контролер подання.
сенс-питання

2
Є помилка, ми повинні перевірити, чи обидві сторони нульові, тому що nil == nilповертається YES, і це не той результат, якого ми хочемо.
CocoaBob

1
@GabrielePetronella Ви не заперечуєте, якщо я оновлю відповідь, щоб також включати впровадження методу Swift?
Водоспад Майкл

1
@MichaelWaterfall, що буде дуже вдячний, спасибі
Габріеле Петронелла,

77

Якщо ви шукаєте iOS 6+, ця відповідь застаріла, і ви повинні перевірити відповідь Габріелі Петронелли.


Немає акуратного способу зробити це як властивість або метод, що є рідним для UIKit. Що ви можете зробити, це перевірити кілька аспектів вашого контролера, щоб переконатися, що він представлений як модальний.

Отже, щоб перевірити, чи поданий поточний (представлений як selfу наведеному нижче коді) контролер модальним способом чи ні, у мене є функція нижче або в UIViewControllerкатегорії, або (якщо вашому проекту не потрібно використовувати інші контролери UIKit, як UITableViewControllerнаприклад) у базовому контролері, який успадковують інші мої контролери

-(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: Я додав останню перевірку, щоб перевірити, чи використовується UITabBarController, і ви представляєте інший UITabBarController як модальний.

EDIT 2: додано чек для iOS 5+, де UIViewControllerвже не відповідає parentViewController, а presentingViewControllerзамість цього.

EDIT 3: Я створив для цього суть лише на випадок https://gist.github.com/3174081


Майте на увазі, що modalViewControllerвластивість застаріло на iOS 6. Документація пропонує presentedViewControllerзамість цього використовувати .
Барт Джейкобс

@BartJacobs хороший момент! Я не переглянув цю відповідь після випуску iOS6, тому це може бути не сучасним. Я спробую зробити кілька тестів пізніше тижня, щоб оновити його, tks!
Феліпе Сабіно

NSLog(@"%@", self.navigationController.parentViewController)відбитки (null)- ви могли б пояснити, чому? Мій ViewController з'єднаний з контролером модального перегляду через navController у розкадровці.
Роман

@oyatek Ви можете використовувати пастібін чи щось подібне і показати якийсь код?
Феліпе Сабіно

@Feilpe Я знайшов проблему - .parentViewControllerзастарілий, .presentingViewControllerзамість цього потрібно використовувати.
Роман

35

У iOS5 +, як ви бачите в UIViewController Class Reference , ви можете отримати його з властивості "presentingViewController".

ПредставленняViewController Контролер подання, який представив цей контролер подання. (лише для читання)

@ властивість (неатомічна, лише для читання) UIViewController * presentingViewController
Discussion

Якщо контролер перегляду, який отримав це повідомлення, представлений іншим контролером перегляду, ця властивість містить контролер перегляду, який його представляє. Якщо контролер подання не представлений, але представлений один з його предків, ця властивість містить контролер подання, який представляє найближчого предка. Якщо ні представник контролера, ні хтось із його предків не представлені, ця властивість не відповідає нулю.

Доступність
Доступно в iOS 5.0 та новіших версіях.
Задекларовано в
UIViewController.h


3
Працює чудово, використовуйте if (self.presentingViewController) {// Це модальний виглядContoller} else {// Це звичайний ViewController}
mashdup

2
ІМХО, це єдина правильна відповідь тут. Просто перевірте наявність a presentingViewController. Він також працюватиме в контролерах перегляду контейнерів, оскільки він автоматично обходить предків.
Даніель Рінсер

17

Якщо цього немає, ви можете визначити властивість для цього ( presentedAsModal) у своєму підкласі UIViewController і встановити його YESперед тим, як представити ViewController як модальний вигляд.

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

Ви можете перевірити це значення в viewWillAppearпереосмисленні.

Я вважаю, що не існує офіційної власності, яка б вказувала, як представлений вид, але ніщо не заважає вам створити свій власний.


RIght, і це те, що я зробив, але я шукав якесь інше акуратне рішення. Дякую.
lukewar

це рішення не працює, якщо ви представляєте UINavigationControllerяк модальний ... якщо ви не створите спеціальний контролер навігації просто для додавання цього властивості. Після цього всередині контролерів вам доведеться продовжувати кастинг self.navigationControllerдо цього користувальницького класу щоразу, коли потрібно перевіряти, чи контролер представлений як модальний
Феліпе Сабіно

8

Відповідь Петронелли не працює, якщо self.navigationController представлений модально, але self не дорівнює self.navigationController.viewControllers [0], у такому випадку self натискається.

Ось як ви могли вирішити проблему.

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

І в Свіфті:

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

Це має спрацювати.

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

На жаль, це не працює. Це була моя перша спроба. Але повернувся modalViewController ins nil :(.
lukewar

Якщо ви просто отримаєте "self.parentViewController", чи повертає він правильний батьківський об'єкт?
кубі

4
Проблема може полягати в тому, що ваш підклас UIViewController знаходиться всередині UINavigationController або UITabBarController (або обох), і в цьому випадку вам може знадобитися копати трохи більше в ієрархії перегляду, щоб дізнатися батьків, представлених як контролер модального перегляду.
hpique

@hgpc Мені знадобився цей чак у своєму проекті, тому я просто додав відповідь, щоб перевірити як UINavigationControllerі UITabBarControllerвипадки. Він працює досить добре
Феліпе Сабіно


2

Якщо вам не потрібно розрізняти повноекранні модальні перегляди та немодальні перегляди, як це стосується мого проекту (я мав справу з проблемою, яка виникає лише з аркушів форм та аркушів сторінок), ви можете використовувати modalPresentationStyle властивість 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

У Свіфті :

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
}

У цьому випадку використання є проблема. Якщо я перебуваю в контролері кореневого перегляду UINavigationController, він все одно повертає значення true без будь-яких модальних подань.
mariusLAN

1
Перший, якщо твердження охоплює все, що є у другому, якщо твердження, що робить друге твердження зайвим. Я не впевнений, у чому тут наміри.
ізоіфон

1

У своєму проекті у мене є контролер перегляду (Detail), який можна представити або модально (при додаванні нового елемента), або з натисканням (при редагуванні існуючого) контролером Master view. Коли користувач натискає [Готово], контролер детального перегляду викликає метод контролера головного перегляду, щоб повідомити, що він готовий закритись. Майстер повинен визначити, як детально представлений, щоб знати, як його закрити. Ось як я це роблю:

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

0

Такий хак може спрацювати.

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
}

Однак я вважаю, що моя попередня відповідь - це більш чисте рішення.


0

Що для мене спрацювало наступне:

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

Наскільки я тестував, це працює для iOS7 та iOS8. Однак не спробував на iOS6.


0

Я трохи оглянувся, щоб знайти правильну відповідь на це питання, і не зміг знайти жодного, який би охопив усі можливі сценарії. Я написав ці кілька рядків коду, які, здається, виконують роботу. Ви можете знайти кілька вбудованих коментарів, щоб зрозуміти, що перевірено.

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

Сподіваюся, що це допоможе.


0

Ось моя модифікована версія @ GabrielePetronella isModal, яка працює з контролерами перегляду, що містяться, тому що вона спочатку виходить з ієрархії parentViewController. Також витягнув код на кілька рядків, щоб було зрозуміло, що він робить.

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.