※ 記事最下部に追記あり(2022/07/06 19:25)
開発環境
> xcodebuild -version Xcode 14.0 Build version 14A5229c
Xcodeはbetaだが本記事の内容はiOS16未満のAPIを使用。
モチベーション
背景色からpreferredStatusBarStyleを動的に生成したい。
view controllerの背景色が白に近い時は .darkContent に、黒に近い時は .lightContent を設定したい。
作ったもの
動画
差分
.darkContent | .lightContent |
---|---|
コード
import UIKit final class ViewController: UIViewController { @IBAction func pushButton(_ sender: Any) { let secondViewController = storyboard?.instantiateViewController( identifier: "secondViewController" ) { coder in SecondViewController( coder: coder, backgroundColor: .systemIndigo ) } if let secondViewController = secondViewController { secondViewController.modalPresentationStyle = .fullScreen self.present(secondViewController, animated: true) } } } final class SecondViewController: UIViewController { let backgroundColor: UIColor override var preferredStatusBarStyle: UIStatusBarStyle { return backgroundColor.binarized() == .white ? .darkContent : .lightContent } init?(coder: NSCoder, backgroundColor: UIColor) { self.backgroundColor = backgroundColor super.init(coder: coder) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = backgroundColor } } private extension UIColor { enum BinarizationType { case black case white } func binarized() -> BinarizationType { var white: CGFloat = 0 self.getWhite(&white, alpha: nil) return white >= 0.5 ? .white : .black } }
以下ポイント。
preferredStatusBarStyleはview controllerの初期化時に指定する必要がある
let secondViewController = storyboard?.instantiateViewController( identifier: "secondViewController" ) { coder in SecondViewController( coder: coder, backgroundColor: .systemIndigo ) }
preferredStatusBarStyleはgetオンリーなプロパティのため、view controllerの初期化時に指定する必要がある。ということは、指定の判断材料である背景色もview controller初期化時に値が存在する必要がある。そこで、view controllerの初期化をinstantiateViewController(identifier:creator:)で行って背景色を指定する形を取った。
UIColorを二値化する
final class SecondViewController: UIViewController { let backgroundColor: UIColor override var preferredStatusBarStyle: UIStatusBarStyle { return backgroundColor.binarized() == .white ? .darkContent : .lightContent } // 省略 } private extension UIColor { enum BinarizationType { case black case white } func binarized() -> BinarizationType { var white: CGFloat = 0 self.getWhite(&white, alpha: nil) return white >= 0.5 ? .white : .black } }
getWhite(_:alpha:)を使ってUIColorのグレースケールを取得できるので、0.5をしきい値として白黒で返す処理を書いた。
所感
短時間で自分の希望通りの実行結果を得られるコードが書けた。UIKit力が上がっているのかもしれない。そうであることを願う。
追記(2022/07/06 19:25)
社の先輩に教えてもらった。 上記までの内容でなくともpreferredStatusBarStyleを変更できる。
import UIKit final class ViewController: UIViewController { @IBAction func pushButton(_ sender: Any) { guard let secondViewController = storyboard?.instantiateViewController(withIdentifier: "secondViewController") as? SecondViewController else { return } secondViewController.backgroundColor = .systemIndigo secondViewController.modalPresentationStyle = .fullScreen self.present(secondViewController, animated: true) } } final class SecondViewController: UIViewController { var backgroundColor: UIColor? { didSet { guard let backgroundColor = backgroundColor else { return } view.backgroundColor = backgroundColor setNeedsStatusBarAppearanceUpdate() } } override var preferredStatusBarStyle: UIStatusBarStyle { guard let backgroundColor = backgroundColor else { return .default } return backgroundColor.binarized() == .white ? .darkContent : .lightContent } }
以下、ポイント。
- preferredStatusBarStyleのgetterにプロパティを見るようにする
- そのプロパティを変更した時にsetNeedsStatusBarAppearanceUpdate()を呼ぶ