ChatGPT解决这个技术问题 Extra ChatGPT

如何检查视图控制器是否以模态方式呈现或推送到导航堆栈上?

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

模态呈现

推送到导航堆栈

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

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


C
Cœur

带着一粒盐,没有测试。

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

我在另一个 SO 帖子中找到了这个。但是,如果推送的视图控制器的父级是模态的,则不起作用;这就是我遇到的情况。
正如我所写,在我的情况下,presentingViewController 始终是 YES;没有帮助。
当有一个 UITabBarController 被设置为根时,presentingViewController 为推送的 VC 返回 YES。所以,不适合我的情况。
如果您呈现一个视图控制器然后它会推送另一个视图控制器,这将不起作用。
“如果您呈现一个视图控制器然后它会推送另一个视图控制器,这不起作用”这不是本意,推送的视图控制器没有被呈现。
c
craft

在斯威夫特:

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

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

在 var 中应该更好,例如 var isModal: Bool {}
@malinois 已更改
return 语句中的最后一个 false 参数有什么作用?
你需要改变让presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController && navigationController != nil
Swift 5:如果 navigationController 为 nil,则 presentingIsNavigation = true
r
rmaddy

您忽略了一种方法: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
    }
}

我在发布之前也尝试过,但它不起作用,isBeingPresentedNO。但我现在明白了原因,我将呈现的视图控制器嵌入到 UINavigationController 中,这就是我要推动的。
您不能推动导航控制器。也许您的意思是您正在展示导航控制器。
@jowie 在打印原始值时使用 p,而不是 popo 用于打印对象。
isBeingPresented 的文档 - 此方法仅在从 viewWillAppear: 和 viewDidAppear: 方法内部调用时返回 YES。
@Terrence 似乎最新的文档没有显示该信息,但它曾经在那里。 isBeingPresentedisBeingDismissedisMovingFromParentViewControllerisMovingToParentViewController 仅在 4 个 view[Will|Did][Disa|A]ppear 方法内有效。
A
Andrey Gordeev

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

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


为什么需要检查 tabBarController?.presentingViewController is UITabBarController presentingViewController 是否也是 UITabBarController 是否重要?
如果 navigationController 为 nil,isModal 将返回 true。这是故意的吗?
n
nikans

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 上并呈现它。如果是这种情况,您的陈述将是错误的,但是在代码上会处理这种情况。请改进你的答案:)
处理所有用例的好工作。可能有一些重构的空间,但仍然赞成!
t
teradyl

Swift 5. 干净简单。

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

C
Charlton Provatas

斯威夫特 4

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

Swift 4.2 / iOS 12。仍然运行良好,但请注意,如果两者都为 nil,则 navigationController?.presentingViewController?.presentedViewController === navigationController 将评估为 true(例如,如果您在尚未调用的视图控制器上调用它提出了)。
M
Mehedi Hasan

Swift 5 这个方便的扩展处理的案例比以前的答案少。这些情况是VC(视图控制器)是应用程序窗口的根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's answer。再次有更多优化的空间。请在评论部分讨论需要处理的案例。


Y
Yevhen Dubinin

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

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

此外,如果上述属性等于 SSViewControllerPresentationMethodUnspecified,我们可以在 DEViewController 中添加“检查”的后备:

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}

S
Senõr Ganso

假设您以模态方式呈现的所有视图控制器都包装在一个新的导航控制器中(无论如何您都应该这样做),您可以将此属性添加到您的 VC。

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

    return false
}

无论如何你都应该这样做 - 请解释为什么?
亚历山大,你不应该,真的。
A
Arash Zeinoddini

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

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

    // Not pushed
}
else {

    // Pushed
}

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


当您在多个地方使用相同的视图控制器类时,此方法不起作用,因为它只检查它的类。您可以改为显式检查相等性。
Y
Yuchen

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

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

J
JRG-Developer

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


仍然可以在模态导航控制器中
所以'modal'和'pushed on navigation stack'并不是相互排斥的。认为这取决于上下文,但检查 self.navigationController 是否不为零确实可以回答它是否是导航控制器的视图控制器。
@Daniel区别在于“推动”和“呈现”。 “模态”与它无关。当他们说“模态”时,我相信“ColdLogic”的意思是“呈现”。
b
blackjacx

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

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

感谢您的回答。我会试着找时间检查一下。顺便说一句,我认为将第二个 return 语句放在 else { } 块中在语义上更好,因为它与拥有导航控制器的情况相反。
S
Saif

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

如果 A 正在展示/推送 B

在B中定义一个枚举和属性 enum ViewPresentationStyle { case Push case Present } //并写入属性 var vcPresentationStyle : ViewPresentationStyle = .Push //默认值,考虑到B是被推送的 现在在A视图控制器中,告诉B是否正在呈现/pushed by assignment presentationStyle func presentBViewController() { let bViewController = B() bViewController.vcPresentationStyle = .Present //告诉 B 它正在呈现 self.presentViewController(bViewController, animated: true, completion: nil) } 在 B 视图中的用法控制器覆盖 func viewDidLoad() { super.viewDidLoad() if self.vcPresentationStyle == .Present { //正在呈现 } else { //正在推送 } }


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

这将让您知道 viewController 是呈现还是推送


此属性已弃用。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅