Как проверить, представлен ли контроллер представления модально или помещен в стек навигации?


126

Как я могу в моем коде контроллера представления различать:

  • представлен модально
  • помещен в стек навигации

Оба presentingViewControllerи isMovingToParentViewControllerявляются YESв обоих случаях, поэтому они не очень полезны.

Что усложняет ситуацию, так это то, что мой родительский контроллер представления иногда является модальным, на который нажимается проверяемый контроллер представления.

Оказывается, моя проблема в том, что я вставляю свой файл HtmlViewController, UINavigationControllerкоторый затем представляется. Вот почему мои собственные попытки и приведенные ниже хорошие ответы не сработали.

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

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

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

Ответы:


125

Возьмите с недоверием, не тестировал.

- (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
Я нашел это в другом сообщении SO. Но не работает, если родительский элемент контроллера представления является модальным; какова ситуация, в которой я оказался.
смысл-имеет значение

2
Как я уже писал, presentingViewControllerвсегда YESв моем случае; не помогает.
значения-вопросы

3
presentingViewControllerвозвращается YESдля нажатого VC, когда UITabBarControllerв качестве корневого установлен объект. Так что в моем случае не подходит.
Евгений Дубинин

5
Это не работает, если вы представляете контроллер представления, а затем он подталкивает другой.
Ли

3
«Это не сработает, если вы представляете контроллер представления, а затем он подталкивает другой». Это не намерение этого, перемещаемый контроллер представления не представлен.
Колин Свелин

87

В Swift :

Добавьте флаг, чтобы проверить, является ли он модальным по типу класса:

// 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
Должно быть лучше в вар, вродеvar isModal: Bool {}
малинуа

1
@malinois изменен
YannSteph

Что делает последний falseпараметр в returnзаявлении?
damjandd

вам нужно изменить, чтобы разрешить PresentingIsNavigation = navigationController? .presentingViewController? .presentViewController == navigationController && navigationController! = nil
famfamfam

78

Вы упустили из виду один метод: isBeingPresented .

isBeingPresented имеет значение true, когда отображается контроллер представления, и false при нажатии.

- (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
Я тоже пробовал это перед публикацией, и это не работает, isBeingPresentedесть NO. Но теперь я вижу причину, я встраиваю представленный контроллер представления в a UINavigationController, и это тот, который я нажимаю.
смысл-имеет значение

1
Вы не можете нажать на контроллер навигации. Возможно, вы имели в виду, что представляете контроллер навигации.
rmaddy

3
@jowie Используйте p, а не poпри печати примитивного значения. poпредназначен для печати объектов.
rmaddy

37
Документация для isBeingPresented- Этот метод возвращает YES только при вызове из методов viewWillAppear: и viewDidAppear :.
funct7

4
@Terrence Кажется, последняя документация не показывает эту информацию, но раньше она там была. isBeingPresented, isBeingDismissed, isMovingFromParentViewControllerИ isMovingToParentViewControllerдействуют только в пределах 4 -х view[Will|Did][Disa|A]ppearметодов.
rmaddy

29

Swift 5
Вот решение, которое решает проблему, упомянутую в предыдущих ответах, когда isModal()возвращается, trueесли нажатие UIViewControllerнаходится в представленном UINavigationControllerстеке.

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

Пока это работает для меня. Если какие-то оптимизации, поделитесь пожалуйста.


Зачем нужна проверка tabBarController?.presentingViewController is UITabBarController ? Имеет ли значение, если это presentingViewControllerтоже UITabBarController?
Хлунг,

И если navigationController равен нулю, isModalвернется true. Это предназначено?
Хлунг,

28

self.navigationController! = nil означает, что он находится в стеке навигации.

Чтобы справиться со случаем, когда текущий контроллер представления нажат, когда контроллер навигации представлен модально, я добавил несколько строк кода, чтобы проверить, является ли текущий контроллер представления корневым контроллером в стеке навигации.

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

В общем, когда вы представляете модально, вы помещаете viewController в navigationController и представляете его. Если это так, ваше утверждение было бы неправильным, однако в коде этот случай обрабатывается. Пожалуйста, улучшите свой ответ :)
E-Riddie

хорошая работа, которая касается всех вариантов использования. место для небольшого рефакторинга, возможно, но все же upvote !!
Jean Raymond Daher

12

Swift 4

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

Swift 4.2 / iOS 12. По-прежнему работает хорошо, но имейте в виду, что navigationController? .PresentingViewController? .PresentViewController === navigationController будет оценивать значение true, если оба значения равны нулю (например, если вы вызываете его на контроллере представления, который еще не был представлены).
Эли Берк,


3

Как многие здесь предполагают, методы «проверки» работают не во всех случаях, в моем проекте я придумал решение, позволяющее управлять этим вручную. Дело в том, что мы обычно управляем презентацией самостоятельно - это не то, что происходит за кулисами, и мы должны заниматься самоанализом.

DEViewController.h файл:

#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

Теперь презентациями можно было управлять следующим образом:

помещен в стек навигации:

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

представлены модально с навигацией:

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

представлены модально:

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

Кроме того, DEViewControllerмы могли бы добавить откат к «проверке», равно ли вышеупомянутое свойство 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

Предполагая, что все viewController, которые вы представляете модально, заключены в новый navigationController (что вы всегда должны делать в любом случае), вы можете добавить это свойство в свой VC.

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

    return false
}

1
что вы всегда должны делать в любом случае - пожалуйста, объясните почему?
Александр Абакумов

Александр, не стоит, правда.
nickdnk

2

Чтобы обнаружить, что ваш контроллер нажат или нет, просто используйте приведенный ниже код в любом месте:

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

    // Not pushed
}
else {

    // Pushed
}

Надеюсь, этот код поможет кому угодно ...


1
Этот метод не работает, когда вы используете один и тот же класс контроллера представления в нескольких местах, поскольку он проверяет только его класс. Вместо этого вы можете явно проверить равенство.
gklka

1

self.navigationController != nil будет означать, что это в стеке навигации.


25
Может все еще быть в модальном контроллере навигации
ColdLogic

Таким образом, «модальный» и «помещенный в стек навигации» не исключают друг друга. Думая, что это зависит от контекста, но проверка того, не является ли self.navigationController равным нулю, отвечает на вопрос, является ли он контроллером представления контроллера навигации.
Даниэль

@Daniel Разница между «толкнул» и «представил». «Модальный» не имеет к этому никакого отношения. Я считаю, что «ColdLogic» имел в виду «представлен», когда они сказали «модальный».
rmaddy

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

1

Swift 5
Это удобное расширение обрабатывает немного больше случаев, чем предыдущие ответы. В этих случаях VC (контроллер представления) является корневым VC окна приложения, VC добавляется в качестве дочернего к родительскому VC. Он пытается вернуть true только в том случае, если viewcontroller представлен модально.

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

Спасибо за ответ Джонауза . Опять же, есть место для дополнительных оптимизаций. Обсудите, пожалуйста, дело, которое необходимо рассмотреть, в разделе комментариев.


0

Если вы используете iOS 5.0 или более позднюю версию, используйте этот код

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

Для тех, кому интересно, как сообщить ViewController, что он представлен

если Aпредставляет / толкаетB

  1. Определите enumи propertyвB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Теперь в Aконтроллере просмотра укажите, отображается Bли он / отправляется, назначивpresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Использование в BView Controller

    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
}

Это позволит вам узнать, представлен ли viewController или нажата


4
Это свойство устарело.
Моркром
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.