淡化状态栏文本而不删除状态栏本身

时间:2014-09-07 21:19:40

标签: ios swift statusbar alpha

我想隐藏状态栏文本而不删除状态栏本身。

我在整个屏幕上呈现一个新的视图控制器,当视图控制器占据整个屏幕时,我不希望状态栏文本可见。请注意,呈现视图控制器具有UINavigationBar,而呈现的视图控制器则不具有true

我尝试在prefersStatusBarHidden中简单地返回modalPresentationCapturesStatusBarAppearance,但这会导致状态栏框被删除,导致呈现视图控制器上的导航控制器向上滑动,这在新的时候可见视图控制器仍然从底部开始动画。

然后我尝试为显示的视图控制器设置truefalse,然后在prefersStatusBarHidden中返回.Fade,在preferredStatusBarUpdateAnimation中返回setNeedsStatusBarAppearanceUpdate(),在viewDidAppear中调用prefersStatusBarHidden,但这会导致状态栏文本从白色变为黑色并保持可见状态。将true设置为{{1}}会删除状态栏,就像我之前提到的那样。

理想情况下,当我启动segue时,我会开始淡化状态栏上的文本,并且当动画完成时,alpha将达到0。然后解雇后重新回到alpha 1.这或类似的解决方案是否可行?我只需要支持iOS 8 +。

2 个答案:

答案 0 :(得分:1)

解决方案1 ​​:我所拥有的解决方案非常hacky,但它确实有效。首先,呈现的视图控制器应将modalPresentationCapturesStatusBarAppearance设置为YES,并应为YES返回prefersStatusBarHidden

现在,问题是不要让导航栏向上滑动。要执行此操作,请先定义UINavigationBar子类并覆盖setFrame:setBounds:setCenter:,以便即使在想要向上滑动时也能保持导航栏的形状和位置不变状态栏隐藏时。像这样:

const CGFloat kStatusBarHeight = 20;
const CGFloat kDefaultNavigationBarHeight = 44;
const CGFloat kLandscapeNavigationBarHeight = 32;

...

- (void)setCenter:(CGPoint)center {
    center = [self forcedCenterForCenter:center];
    [super setCenter:center];
}

- (void)setBounds:(CGRect)bounds {
    bounds = [self forcedBoundsForBounds:bounds];
    [super setBounds:bounds];
}

- (void)setFrame:(CGRect)frame {
    frame = [self forcedFrameForFrame:frame];
    [super setFrame:frame];
}

- (CGPoint)forcedCenterForCenter:(CGPoint)center {
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
        center.y = kStatusBarHeight + kDefaultNavigationBarHeight * 0.5;
    }
    else {
        // No status bar in landscape orientation in iOS 8.
        center.y = kLandscapeNavigationBarHeight * 0.5;
    }

    return center;
}

- (CGRect)forcedBoundsForBounds:(CGRect)bounds {
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
        bounds.size.height = kDefaultNavigationBarHeight;
    }
    else {
        bounds.size.height = kLandscapeNavigationBarHeight;
    }

    return bounds;
}

- (CGRect)forcedFrameForFrame:(CGRect)frame {
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
        frame.origin.y = kStatusBarHeight;
        frame.size.height = kDefaultNavigationBarHeight;
    }
    else {
        // No status bar in landscape orientation in iOS 8.
        frame.origin.y = 0.0;
        frame.size.height = kLandscapeNavigationBarHeight;
    }

    return frame;
}

我们打电话给这个班级ForcedNavigationBar。如果您只是将此课程传递到UINavigationController的{​​{1}},则导航栏将保留在原位,但您会遇到其他问题。首先,有时,导航栏不会向上延伸以覆盖状态栏。其次,推入导航控制器的视图控制器不应再使用其initWithNavigationBarClass:toolbarClass:属性来进行布局,因为topLayoutGuide可能会考虑实际上已隐藏的状态栏,即使我们总是强制空间对于想象中的状态栏。

要解决第一个问题,首先要将topLayoutGuide属性设置为ForcedNavigationBar并覆盖backgroundColor,使[UIColor clearColor]的背景不可见(不要调用drawRect: super)。 (导航栏的按钮仍然可见。)现在,我们在真实的导航栏下面放置一个虚拟导航栏,以提供一个背景,它总是为虚构的状态栏提供空间。我们可以通过继承UINavigationController(称之为ForcedNavigationBar)并向虚拟导航栏添加约束来实现此目的:

- (void)viewDidLoad {
    [super viewDidLoad];

    UINavigationBar * backgroundNavigationBar = [[UINavigationBar alloc] init];
    [self.navigationBar.superview insertSubview:backgroundNavigationBar belowSubview:self.navigationBar];
    {
        backgroundNavigationBar.translatesAutoresizingMaskIntoConstraints = NO;

        // The background bar always hugs the top of the screen.
        NSLayoutConstraint * topConstraint = [NSLayoutConstraint constraintWithItem:backgroundNavigationBar
                                                                          attribute:NSLayoutAttributeTop
                                                                          relatedBy:NSLayoutRelationEqual
                                                                             toItem:self.view
                                                                          attribute:NSLayoutAttributeTop
                                                                         multiplier:1
                                                                           constant:0];
        [self.view addConstraint:topConstraint];

        NSLayoutConstraint * bottomConstraint = [NSLayoutConstraint constraintWithItem:backgroundNavigationBar
                                                                             attribute:NSLayoutAttributeBottom
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.navigationBar
                                                                             attribute:NSLayoutAttributeBottom
                                                                            multiplier:1
                                                                              constant:0];
        [self.navigationBar.superview addConstraint:bottomConstraint];

        NSLayoutConstraint * leftConstraint = [NSLayoutConstraint constraintWithItem:backgroundNavigationBar
                                                                           attribute:NSLayoutAttributeLeft
                                                                           relatedBy:NSLayoutRelationEqual
                                                                              toItem:self.navigationBar
                                                                           attribute:NSLayoutAttributeLeft
                                                                          multiplier:1
                                                                            constant:0];
        [self.navigationBar.superview addConstraint:leftConstraint];

        NSLayoutConstraint * rightConstraint = [NSLayoutConstraint constraintWithItem:backgroundNavigationBar
                                                                            attribute:NSLayoutAttributeRight
                                                                            relatedBy:NSLayoutRelationEqual
                                                                               toItem:self.navigationBar
                                                                            attribute:NSLayoutAttributeRight
                                                                           multiplier:1
                                                                             constant:0];
        [self.navigationBar.superview addConstraint:rightConstraint];
    }
}

要解决第二个问题,您只需不要使用topLayoutGuide并根据您想要定位导航栏的位置在顶部设置空间。对于非滚动视图的视图,您可以更新相对于viewWillLayoutSubviews中导航栏定位它们的约束。如果您有滚动视图(如表格视图),则可以在contentInset中调整其viewDidLayoutSubviews的顶部。 (以前,您可以将滚动视图的contentInset的顶部设置为topLayoutGuide.length。)

我提供的代码可以在iPhone 5屏幕上运行。在iOS 8中,您必须使此代码“自适应”,因此它仍然可以在iPhone 6屏幕上运行。例如,在横向方向上,导航栏可能不会在iPhone 6上被压扁。

编辑:解决方案2 :使用snapshotViewAfterScreenUpdates拍摄展示视图控制器的快照视图,并在呈现或解除之前将其插入视图层次结构中。这样,您不必担心导航栏向上或向下滑动。 (我没有亲自试过这个,但看起来很有希望。)

编辑2:此解决方案的一个小问题是导航栏在解雇之前没有状态栏的空间。如果您使用UIViewControllerAnimatedTransitioning解雇,则使用空动画块调用[UIView animationWithDuration:...]似乎(某种程度上)强制系统让“to”视图控制器(导航控制器)指示状态栏外观。然后,为了使“to”视图控制器布局并带有状态栏的空间,可以在容器视图或某个视图上调用layoutIfNeeded,并在其中查看“to”视图中的控制器。在animateTransition:中完成所有这些操作,但在此方法中实际执行动画之前。

答案 1 :(得分:1)

实际上,您可以直接使用状态栏访问窗口,并随意使用它。

- (UIWindow *)statusBarWindow
{
    return (UIWindow *)[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"];
}

当然,它是无证件的功能,在App Review Process方面可能很危险,但我只有积极的经验。