【问题标题】:Overriding view controller based status bar appearance覆盖基于视图控制器的状态栏外观
【发布时间】:2017-03-19 15:00:04
【问题描述】:

我有一个应用程序,其基于视图控制器的状态栏外观设置为YES。我的一些视图是深色的,我的一些视图的内容是浅色的,并且该应用程序具有非常复杂的视图控制器层次结构,但它可以完美地与子类化和覆盖适当的方法以及捕获演示样式的模态视图等相结合)。

但是,我需要一种 全局 方式来查看顶部的特定项目(在状态栏后面,在我的应用程序范围内),就像个人热点/GarageBand 录音/通话中等栏一样在顶部。由于栏的背景颜色,我想在显示栏时覆盖状态栏的外观(它可以显示在应用程序的任何位置,所以我将UIWindow 子类化并将其演示代码并直接放在那里查看)。该栏在带有浅色内容状态栏的屏幕上完全按照我想要的方式显示(因为我的栏的文本为白色,背景为深色),但在深色内容状态栏上看起来很糟糕(不,我无法更改栏的颜色)。

如何在全局覆盖“无论当前呈现的视图控制器是什么”的首选状态栏样式(当然,不遍历所有视图控制器中状态栏方法的所有实例),同时仍然使用基于视图控制器的状态酒吧外观?我的应用面向 iOS 8.0+。

【问题讨论】:

    标签: ios uiviewcontroller ios8 uistatusbar


    【解决方案1】:

    我以一种非常老套(但有效)的方式结束了。它可能并不适用于所有场景,但它适用于我的场景。我保持视图原样,没有触及任何视图或控制器。

    首先,我得到了当前显示的最顶层视图控制器。我使用了来自iPhone -- How to find topmost view controller 的代码并对其进行了一些修改以处理导航控制器和标签栏控制器的情况:

    + (UIViewController*) topmostControllerForViewController:(__kindof UIViewController*)topController
    {
        while (topController.presentedViewController) {
            topController = topController.presentedViewController;
        }
        if([topController isKindOfClass:[UINavigationController class]]){
            UINavigationController *navController = topController;
            return [self topmostControllerForViewController:navController.visibleViewController];
        }
        if([topController isKindOfClass:[UITabBarController class]]){
            UITabBarController *tabController = topController;
            return [self topmostControllerForViewController:tabController.selectedViewController];
        }
        return topController;
    }
    
    + (UIViewController*) topmostController
    {
        __kindof UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
        return [self topmostControllerForViewController:topController];
    }
    

    然后我创建了一个没有视图的视图控制器(view 是 nil)。在它的init 方法中(如果放入viewDidLoad:,则不在第一次调用中起作用,因为它在转换过程中被调用并且为时已晚),我添加了以下内容:

    self.modalPresentationCapturesStatusBarAppearance = YES;
    self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    

    该代码允许我的“虚拟”视图(更少)控制器处理所有呈现上下文,包括状态栏外观以及其他视图控制器在呈现时发生的情况。当在当前上下文中呈现时,后面的视图控制器不会从视图层次结构中删除。如果我不这样做,它将被删除并且屏幕将变黑(因为我没有任何视图并且我希望显示以前的视图控制器)。

    到目前为止一切顺利。然后,我正常显示了我的栏,但同时,以模态方式呈现了该视图控制器,没有任何视图。因为视图控制器没有任何视图并且在当前上下文中呈现,所以它在视觉上没有以任何方式出现,但是由于它是一个模态呈现并且虚拟视图控制器被设置为捕获呈现样式,它触发了 iOS向我的应用询问状态栏样式。我只是在视图控制器方法中设置了我想要的状态栏样式。

    出了点小问题。当我展示新的视图控制器时,系统在我之前的视图控制器之上添加了一个UITransitionView。如果有一个实际的视图,它将位于转换视图的顶部。过渡视图是完全透明的,但它启用了用户交互并捕获了所有触摸事件,这使我的应用程序在我关闭控制器之前无响应。我需要我以前的视图控制器来接收触摸事件。我已经深入挖掘并发现模态演示添加过渡视图的位置,并在过渡动画完成后呈现视图控制器时将其删除:

    for (UIView *view in self.subviews) {
            NSString *className = NSStringFromClass([view class]);
            if([className hasPrefix:@"UIT"] && className.length == 16){
                //this must be UITransitionView, but I'm not using it directly since it may interfere with private API usage and get app rejected by Apple.
                //now, we need to find another transition view inside this and remove it
                for (UIView *innerView in view.subviews) {
                    className = NSStringFromClass([innerView class]);
                    if([className hasPrefix:@"UIT"] && className.length == 16){
                        //this is the transition view that we need to remove
                        [innerView removeFromSuperview];
                    } 
                }
            }
    }
    

    由于UITransitionView 是私有视图类型,我不确定它是否会导致 App Store 出现问题,因此我通过检查第一个字母 UIT 并检查长度对 UITransitionView 进行了启发式检查的类名。它不是万无一失的,但它似乎有效并且不太可能返回误报。

    现在一切都按预期工作。它很hacky,并且将来可能会中断,特别是如果模态表示在引擎盖下发生变化。但请放心,它有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-02
      • 2017-06-04
      • 1970-01-01
      • 1970-01-01
      • 2017-06-09
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      相关资源
      最近更新 更多