imzyf / ios-swift-learning-notes

📝 iOS Swift Learning Notes - see Issues
MIT License
0 stars 0 forks source link

StatusBar 相关 #44

Open imzyf opened 6 years ago

imzyf commented 6 years ago

显示 隐藏 样式 动画

plist 中的 UIViewControllerBasedStatusBarAppearance 设置为 no 或者移除

setStatusBarStyle 不再使用

    override var prefersStatusBarHidden: Bool {
        return true
    }
imzyf commented 6 years ago
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

在有 navigationbar 时颜色不发生变化,原因是:

These methods control the attributes of the status bar when this view controller is shown. They can be overridden in view controller subclasses to return the desired status bar attributes.

大意是说当 view controller 显示的时候,这个方法可以用来控制状态栏的属性。你可以在 UIViewController 的子类中重载该方法返回你期望的状态栏属性。

那为什么我现在重载该方法却不起作用呢?事实上preferredStatusBarStyle()根本未被调用。原因能是 UINavigationController 作为一个容器“承包”了这些事情。我们所添加的 view controller 都在它的导航栈中,可以说这些控制器都是导航栏控制器的child view controller。此时导航栏控制器才是真正能操作状态栏的 Boss。

所以要在 UINavigationController 设置

imzyf commented 6 years ago

在导航栈中的第一个 view controller 里的状态栏为白色,第二个 view controller 里的状态栏为黑色。上面这种在导航栏控制器中控制状态栏属性的一刀切的粗暴做法显然不符合我们的要求,那么这里有另外一个方法 :

// Override to return a child view controller or nil. If non-nil, that view controller's status bar appearance attributes will be used. If nil, self is used. Whenever the return values from these methods change, -setNeedsUpdatedStatusBarAttributes should be called.
@available(iOS 7.0, *)
public func childViewControllerForStatusBarStyle() -> UIViewController?

大意是,如果这个方法返回值为 non-nil,则将更改状态栏属性的控制权移交给你返回的那个控制器。如果返回值为 nil 或者不重载该方法,那么由自己负责控制状态栏的属性。当状态栏的样式被更改之前,该控制器的-setNeedsUpdatedStatusBarAttributes方法应该被调用。

如果在该 UIViewController 已经在显示在当前,你可能还要在当前页面不时的更改状态栏的 style,那么你需要先调用-setNeedsStatusBarAppearanceUpdate方法(它通知系统去调用当前UIViewController 的-preferredStatusBarStyle方法)。

要实现我们的需求,就只能在NavigationController中重载-childViewControllerForStatusBarStyle方法,既然它返回一个视图控制器的实例,那么我们只要将导航栈中的topViewController作为返回值(该方法会被调用多次,且每次~~设置状态栏 style ~~push/pop view controller 该方法都会被调用),然后在需要设置状态栏 style 的 view controller 中像最开始一样重载-preferredStatusBarStyle方法即可。

override func childViewControllerForStatusBarStyle() -> UIViewController? { return self.topViewController }

http://blog.zhwayne.com/2015/12/16/%E7%8A%B6%E6%80%81%E6%A0%8F%E9%A3%8E%E6%A0%BC%E5%8F%8A%E7%94%A8%E6%B3%95%E7%AE%80%E8%BF%B0/

imzyf commented 6 years ago
class ViewController: UIViewController {

    var isHidden:Bool = false
    @IBAction func clicked(sender: AnyObject) {
        isHidden = !isHidden
        UIView.animate(withDuration: 0.5) { () -> Void in
            self.setNeedsStatusBarAppearanceUpdate()
        }
    }

    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return UIStatusBarAnimation.slide
    }
    override var prefersStatusBarHidden: Bool {
        return isHidden
    }
}
imzyf commented 6 years ago
extension UINavigationController {
    func preferredStatusBarStyle() -> UIStatusBarStyle {
        if self.presentingViewController != nil {
            // NavigationController的presentingViewController不会为nil时,通常意味着Modal
            return self.presentingViewController!.preferredStatusBarStyle()
        }
        else {
            guard self.topViewController != nil else { return .Default }
            return (self.topViewController!.preferredStatusBarStyle());
        }
    }
}
imzyf commented 6 years ago

注意:我们通常使用的viewController都是嵌套在UINavigationController中使用的,此时在viewController中使用

背景色

另一种方法就是通过设置navigationBar的setBarTintColor颜色来改变状态栏颜色

childViewControllerForStatusBarStyle

这个函数的返回值默认返回nil,此时系统就会调用当前viewControllerA的preferredStatusBarStyle函数;如果返回值是另一个viewControllerB那么系统就会调用viewControllerB的preferredStatusBarStyle函数。

运用这个函数就可以解决嵌套UINavigationController设置样式无效的问题。

解释一下为什么嵌套UINavigationController的viewController的preferredStatusBarStyle函数设置无效: 在我们嵌套了UINavigationController的时候,我们的 AppDelegate.window.rootViewController 通常是我们创建的navigationController,这时首先会调用的是navigationController中的childViewControllerForStatusBarStyle函数,因为默认返回nil,那么接下来就会调用navigationController本身的preferredStatusBarStyle函数,所以我们在viewController中通过preferredStatusBarStyle函数设置的状态栏样式就不会被调用发现,所以也就无效了。

所以我们要自己创建一个继承于UINavigationcontroller的 NavigationController,在这个子类中重写 childViewControllerForStatusBarStyle函数

这样navigationController中的childViewControllerForStatusBarStyle函数会返回navigationController中最上层的viewController,那么viewController中的preferredStatusBarStyle函数的设置就会被系统获知

preferredStatusBarUpdateAnimation

这个函数返回了动画效果。动画效果只有在prefersStatusBarHidden 函数返回值变化的时候才会展示,同时要通过调用 [self setNeedsStatusBarAppearanceUpdate]函数来重绘状态栏

自定义

我们可以通过隐藏系统状态栏,然后自定义UIWindow 通过设置setWindowLevel:UIWindowLevelStatusBar实现自定义状态栏。