Можно ли определить, представлен ли ViewController как модальный?

117

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

lukewar
источник

Ответы:

96

Поскольку modalViewControllerв iOS 6 она устарела, вот версия, которая работает для iOS 5+ и компилируется без предупреждений.

Objective-C:

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

Swift:

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;        

}

РЕДАКТИРОВАТЬ: я добавил последнюю проверку, чтобы увидеть, используется ли UITabBarController, и вы представляете другой UITabBarController как модальный.

РЕДАКТИРОВАТЬ 2: добавлена ​​проверка iOS 5+, где UIViewControllerбольше не отвечает parentViewController, а presentingViewControllerвместо этого.

РЕДАКТИРОВАТЬ 3: Я создал для него суть на всякий случай https://gist.github.com/3174081

Фелипе Сабино
источник
Имейте в виду, что это modalViewControllerсвойство устарело в iOS 6. В документации предлагается использовать его presentedViewController.
Барт Джейкобс,
@BartJacobs хорошая мысль! Я не смотрел этот ответ после выпуска iOS6, поэтому он может быть устаревшим. Я постараюсь сделать несколько тестов позже на неделе, чтобы обновить его, спасибо!
Фелипе Сабино
NSLog(@"%@", self.navigationController.parentViewController)отпечатки (null)- не могли бы вы объяснить почему? Мой ViewController связан с контроллером модального представления через navController в раскадровке.
Роман
@oyatek вы можете использовать pastebin или что-то подобное и показать какой-нибудь код?
Фелипе Сабино
@Feilpe Я обнаружил, что проблема - .parentViewControllerустарела, .presentingViewControllerдолжна использоваться вместо нее .
Роман
35

В iOS5 +, как вы можете видеть в описании класса UIViewController , вы можете получить его из свойства «presentingViewController».

PresentingViewController Контроллер представления, представивший этот контроллер представления. (Только для чтения)

@property (неатомный, только для чтения) UIViewController * PresentingViewController
Обсуждение

Если контроллер представления, получивший это сообщение, представлен другим контроллером представления, это свойство содержит контроллер представления, который его представляет. Если контроллер представления не представлен, но представлен один из его предков, это свойство содержит контроллер представления, представляющий ближайшего предка. Если ни контроллер представления, ни какой-либо из его предков не представлены, это свойство имеет значение nil.

Доступность
Доступно в iOS 5.0 и новее.
Объявлено в
UIViewController.h

Радж
источник
3
Отлично работает, используйте if (self.presentingViewController) {// Это модальный ViewContoller} else {// Это нормальный ViewController}
mashdup
2
ИМХО, это единственно правильный ответ. Просто проверьте наличие файла presentingViewController. Он также будет работать в контроллерах представления контейнера, поскольку он автоматически проходит по предкам.
Даниэль Ринсер
17

Если нет, вы можете определить свойство для this ( presentedAsModal) в своем подклассе UIViewController и установить его YESперед представлением ViewController в виде модального представления.

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

Вы можете проверить это значение в своем viewWillAppearпереопределении.

Я считаю, что нет официального свойства, которое бы указывало, как представлено представление, но ничто не мешает вам создать свое собственное.

hpique
источник
Правильно, и это то, что я сделал, но я искал какое-то другое аккуратное решение. Спасибо.
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]];

И в 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

Это должно работать.

if(self.parentViewController.modalViewController == self)…
куби
источник
К сожалению, это не работает. Это была моя первая попытка. Но вернул modalViewController ins nil :(.
lukewar
Если вы просто получите self.parentViewController, вернет ли он правильный родительский объект?
Куби
4
Проблема может заключаться в том, что ваш подкласс UIViewController находится внутри UINavigationController или UITabBarController (или обоих), и в этом случае вам может потребоваться немного больше покопаться в иерархии представлений, чтобы найти родителя, который был представлен как модальный контроллер представления.
hpique
@hgpc мне нужен этот ПРОВ в моем проекте, так что я просто добавил ответ для проверки как UINavigationControllerи UITabBarControllerслучаев. Пока что это работает довольно хорошо
Фелипе Сабино
4

Лучший способ проверить

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Солнечный Шах
источник
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;
}
arlomedia
источник
2

В 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
}
Король-Мастер
источник
В этом варианте использования есть проблема. Если я нахожусь в корневом контроллере представления UINavigationController, он все равно возвращает true без какого-либо модального представления.
mariusLAN
1
Первый оператор if охватывает все, что находится во втором операторе if, делая второй оператор избыточным. Я не уверен, что здесь задумано.
isoiphone
1

В моем проекте у меня есть контроллер представления (Detail), который может быть представлен либо модально (при добавлении нового элемента), либо с помощью push (при редактировании существующего) с помощью главного контроллера представления. Когда пользователь нажимает [Готово], контроллер детального представления вызывает метод главного контроллера представления, чтобы уведомить о его готовности к закрытию. Мастер должен определить, как Детализация представлена, чтобы знать, как ее закрыть. Вот как я это делаю:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}
Olex
источник
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
}

Однако я думаю, что мой предыдущий ответ - более чистое решение.

hpique
источник
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 не пробовал.

mixtly87
источник
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;
}

Надеюсь на эту помощь.

DennyLou
источник
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
}
Райан
источник