繁体   English   中英

如何检查视图控制器是模态呈现还是推送到导航堆栈上?

[英]How to check if a view controller is presented modally or pushed on a navigation stack?

在我的视图控制器代码中,我如何区分:

  • 模态地呈现
  • 推送到导航堆栈上

在这两种情况下, presentingViewControllerisMovingToParentViewController都是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];

我想我最好在它处于模态时告诉我的视图控制器,而不是试图确定。

带一点盐,没有测试。

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

斯威夫特

添加一个标志来测试它是否是类类型的模态:

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

您忽略了一种方法: isBeingPresented

isBeingPresented在视图控制器被呈现时为真,在被推送时为假。

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

斯威夫特 5
这是解决先前答案中提到的问题的解决方案,如果推送的UIViewController位于呈现的UINavigationController堆栈中,则当isModal()返回true时。

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

到目前为止,它确实对我有用。 如果有一些优化,请分享。

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

斯威夫特 4

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

Swift 5.干净简单。

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}

正如这里的许多人所建议的那样,“检查”方法并不适用于所有情况,在我的项目中,我提出了手动管理的解决方案。 关键是,我们通常自己管理演示文稿——这不是幕后发生的事情,我们必须反省。

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

假设您以模态方式呈现的所有 viewController 都包含在一个新的 navigationController 中(无论如何您都应该这样做),您可以将此属性添加到您的 VC 中。

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

    return false
}

斯威夫特 5
这个方便的扩展处理比以前的答案更多的案例。 这些情况是 VC(视图控制器)是应用程序窗口的根 VC,VC 作为子项添加到父 VC。 仅当以模态呈现视图控制器时,它才会尝试返回 true。

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

感谢Jonauz 的回答 再次有更多优化的空间。 需要处理的案例请在评论区讨论。

如果您使用的是 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;
    }
}

要检测您的控制器是否被推送,只需在您想要的任何地方使用以下代码:

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

    // Not pushed
}
else {

    // Pushed
}

我希望这段代码可以帮助任何人...

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

self.navigationController != nil表示它在导航堆栈中。

这个解决方案怎么样 - 在 iOS 15 和 Xcode 13.1 下测试:

var isPresented: Bool {
    if let nvc = navigationController {
        return nvc.viewControllers.firstIndex(of: self) == 0
    } else {
        return presentingViewController != nil
    }
}

对于一些想知道的人,如何告诉 ViewController 它正在呈现

如果A正在展示/推动B

  1. B定义enumproperty

     enum ViewPresentationStyle { case Push case Present } //and write property var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed
  2. 现在在A视图控制器中,通过分配presentationStyle告诉B它是否正在呈现/推送

    func presentBViewController() { let bViewController = B() bViewController.vcPresentationStyle = .Present //telling B that it is being presented self.presentViewController(bViewController, animated: true, completion: nil) }
  3. B视图控制器中的使用

    override func viewDidLoad() { super.viewDidLoad() if self.vcPresentationStyle == .Present { //is being presented } else { //is being pushed } }

在我的视图控制器代码中,我如何区分:

  • 模态呈现
  • 推入导航堆栈

presentingViewControllerisMovingToParentViewController在这两种情况下都为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];

我想我最好告诉我的视图控制器何时是模态的,而不是试图确定。

id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

这会让你知道 viewController 是呈现还是推送

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM