Skip to content

Instantly share code, notes, and snippets.

@joncardasis
Created July 10, 2018 19:12
Show Gist options
  • Save joncardasis/d4d70a2d59131c9fe0e74e7ea18f5096 to your computer and use it in GitHub Desktop.
Save joncardasis/d4d70a2d59131c9fe0e74e7ea18f5096 to your computer and use it in GitHub Desktop.
A swizzling method which introduces a `statusBarStyle` to control the display of the status bar on a view controller.
//
// UIViewController+StatusBar.swift
// StatusBarTesApp
//
// Created by Jonathan Cardasis (C) on 7/10/18.
// Copyright © 2018 Jonathan Cardasis. All rights reserved.
//
import UIKit
extension UIViewController {
/// A convenience variable for setting the status bar style of the current controller.
var statusBarStyle: UIStatusBarStyle {
get {
return statusBarController?.controllerStatusBarStyle ?? preferredStatusBarStyle
}
set {
if statusBarController == nil {
self.statusBarController = StatusBarController()
}
statusBarController?.controllerStatusBarStyle = newValue
}
}
}
// MARK: - Pass StatusBarStyle to Host Controllers
extension UITabBarController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return selectedViewController
}
}
extension UINavigationController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return visibleViewController
}
}
// MARK: - Private Implementation
fileprivate extension UIViewController {
private struct AssociatedKey {
static var statusBar = "uiviewcontroller_statusBarController"
}
private var statusBarController: StatusBarController? {
get {
return objc_getAssociatedObject(self, &AssociatedKey.statusBar) as? StatusBarController
}
set(newValue) {
self.statusBarController?.detach()
newValue?.attach(to: self)
objc_setAssociatedObject(self, &AssociatedKey.statusBar, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// MARK: - Swizzling
@objc var swizzledChildViewControllerForStatusBarStyle: UIViewController? {
return statusBarController
}
fileprivate static func setupChildViewControllerForStatusBarStyleSwizzling() {
let original = #selector(getter: UIViewController.childViewControllerForStatusBarStyle)
let swizzled = #selector(getter: UIViewController.swizzledChildViewControllerForStatusBarStyle)
let originalMethod = class_getInstanceMethod(UIViewController.self, original)
let swizzledMethod = class_getInstanceMethod(UIViewController.self, swizzled)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
fileprivate static var isSwizzlingPerformed: Bool = false
fileprivate static func swizzleIfNeeded() {
guard !isSwizzlingPerformed else { return }
UIViewController.setupChildViewControllerForStatusBarStyleSwizzling()
isSwizzlingPerformed = true
}
}
/// An invisible viewcontroller which becomes a child VC and controls
/// the display of the device's status bar.
fileprivate class StatusBarController: UIViewController {
var controllerStatusBarStyle: UIStatusBarStyle = .default {
didSet {
guard oldValue != controllerStatusBarStyle else { return }
self.parent?.setNeedsStatusBarAppearanceUpdate()
}
}
init() {
UIViewController.swizzleIfNeeded()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
view.isHidden = true
view.frame = .zero
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return controllerStatusBarStyle
}
func attach(to viewController: UIViewController) {
viewController.addChildViewController(self)
viewController.view.addSubview(view)
didMove(toParentViewController: viewController)
}
func detach() {
willMove(toParentViewController: nil)
view.removeFromSuperview()
removeFromParentViewController()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment