26

我创建了一个 UIViewController 子类,它可以被推送到 UINavigationController 的导航堆栈中,也可以从任何 UIViewController 呈现(模态)。我需要确定是否显示了我的视图控制器,如果显示了,我需要在视图控制器的顶部添加一个带有关闭按钮的工具栏。(否则,如果将其推入导航堆栈,则将添加默认关闭按钮,用户可以使用该按钮返回。)

在所有可用版本中,从 UIViewController 子类内部说 4.3、5.0 到 6.0,如果满足以下条件,我是否可以假设视图控制器已呈现(模态)。

if(self.parentViewController == nil || self.navigationController == nil)
4

13 回答 13

36

在 iOS 5 中,UIViewController 获得了一个名为 的只读属性presentingViewController,它取代了旧的语义parentViewController(现在描述了包含)。当视图控制器需要访问呈现它的视图控制器时,可以使用此属性 - 注意:如果您是 API 新手,这通常会超出您的预期!

此外,isBeingPresented引入该属性几乎可以解决您当前所处的情况。在您的视图控制器的viewWillAppear:.

更新

我过度阅读了您似乎也以 iOS 4.3 为目标:
在这种情况下,您需要保护对的调用,isBeingPresented然后if ([self respondsToSelector:…])您可以在else块中检查 parentViewController 是否不为零。

向后兼容的另一种方法可能是覆盖+resolveInstanceMethod:-isBeingPresented在运行时添加实现。这将使您的呼叫站点保持清洁,一旦您放弃古老的 iOS 支持,您就会摆脱运行时魔法;-)

但是请注意,在 iOS <5 上运行时,这有一些极端情况,并且您的初始方法也是如此:

视图控制器可以包含在任何其他视图控制器中——包括导航控制器。当最后一种情况发生时,你就不走运了:parentViewControllerwill be nil, while navigationControllerwill not。您可以尝试添加大量笨拙的代码来缓解旧 iOS 中的这种限制……或者您可以放手。

于 2013-03-06T07:43:53.527 回答
13

我使用这段代码来检查 UIViewController 是否存在。

if (uiviewcontroller.presentingViewController != nil) {
   // do something
}
于 2013-06-08T13:56:27.647 回答
8

我有一个类似的案例,但是我展示的视图控制器包含在它自己的导航控制器中。因此,在该视图控制器中,当我需要确定是否添加关闭按钮和后退按钮时,我只需检查导航控制器堆栈大小。如果显示屏幕,堆栈大小应该是一(需要关闭按钮)......如果它是使用现有导航控制器推送的,那么堆栈大小将大于一(需要返回按钮)。

BOOL presented = [[self.navigationController viewControllers] count] == 1;
于 2014-04-09T00:08:14.403 回答
7

为了处理这种行为,我通常在 viewWillAppear/viewWillDisappear 方法中设置/重置一个 BOOL 来切换它。

顺便说一句,您的测试条件似乎不正确。我认为你应该使用

if(self.parentViewController != nil || self.navigationController != nil)

为什么不能总是将工具栏添加到视图控制器?是否存在视图已加载但从未呈现的情况?

于 2013-02-26T14:10:15.710 回答
5

在 iOS 9(或更高版本)上的 Swift 中:

if viewController.viewIfLoaded?.window != nil {
    // viewController is visible
}
于 2019-03-11T17:46:15.103 回答
3

@saikamesh。

当您使用 UINavigationController 导航您的 viewControllers 时,我认为您可以使用topViewController( Doc here ) 和visibleViewController( Doc again ) 来达到您的意图。

你提到:

当它被推入导航堆栈时,将添加默认的关闭按钮,通过使用该按钮用户可以返回

如果特定 UIViewController 的实例很重要,我认为最好创建一个共享单例实例并提供一个全局显示标志:

id specificVC = [SpecificViewController sharedInstance];
if (specificVC.isPushed) {
    [self.navController popToViewController:specificVC animated:YES];
}

并检查它是否存在:

if ([self.navController.visibleViewController isKindOfClass:[SpecificViewController class]]) {
    // Hide or add close button
    self.isPresented = YES;
}

或者,您可以阅读广为接受的答案

:) 希望有所帮助。

于 2013-03-01T06:54:15.783 回答
2

请以这种方式检查:

 for (UIViewController*vc in [self.navigationController viewControllers]) {
    if ([vc isKindOfClass: [OffersViewController class]]){ //this line also checks OffersViewController is presented or not 

        if(vc.isViewLoaded){
             NSLog(@"Yes");
        }

    }
}
于 2013-03-01T06:27:42.207 回答
2

可以这样操作,又快又安全

UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

// Find the top controller on the view hierarchy
while (topController.presentedViewController) {
    topController = topController.presentedViewController;
}

// If the top controller it is not already presented
if (![topController isKindOfClass:[YourViewController class]]) {
    // Present it
    [topController presentViewController:yourViewController animated:YES completion:nil];
}
else {
// do some stuff here
}
于 2015-03-21T16:10:01.127 回答
1

您可以随时使用导航控制器中的 modalViewController 属性检查是否显示了模态视图控制器。前任:

   UIViewController *presentedController = self.navigationController.modalViewController;
   if (presentedController) {
      // At this point, you have a view controller presented from your navigation controller
      if ([presentedController isKindOfClass:[controllerYouWantToCheck class]]) {
         // add your toolbar/buttons/etc here
      }
   }
于 2013-03-05T09:52:13.180 回答
1

我在这里没有看到的一个优雅的答案:

// Edit: Added 2 other modal cases
extension UIViewController {
    var isModal: Bool { 
        return self.presentingViewController?.presentedViewController == self
            || (navigationController != nil && navigationController?.presentingViewController?.presentedViewController == navigationController)
            || tabBarController?.presentingViewController is UITabBarController
    }
}

信用:基于这个要点

于 2017-03-03T15:52:11.167 回答
1

正如马丁里德所说,这是最好的方式

            BOOL presented = [[self.navigationController viewControllers] count] == 1;
        if (presented) {
            [self dismissViewControllerAnimated:YES completion:^{
                // do whatever you need here
            }];
        }
        else {
            [self.navigationController popViewControllerAnimated:YES];
        }
于 2019-07-25T12:46:02.327 回答
0

如果是我,我会有一个自定义的 init 方法并在创建 vc 时使用它。

vc = [[[MyUIViewControllerSubClass alloc] init] initWithToolbarAndCloseButton:YES];
于 2013-02-26T14:10:23.227 回答
0

@AmitaiB 回答中的小修改以创建函数,

func isModallyPresented(tmpVC:UIViewController) -> Bool {
        return tmpVC.presentingViewController?.presentedViewController == tmpVC
            || (tmpVC.navigationController != nil && tmpVC.navigationController?.presentingViewController?.presentedViewController == tmpVC.navigationController)
            || tmpVC.tabBarController?.presentingViewController is UITabBarController
    }

只需致电检查:

if(isModallyPresented(tmpVC:myTopVC)){
//return true if viewcontroller is presented 
}
于 2019-02-15T07:19:44.933 回答