动态隐藏状态栏时如何保留状态栏占用的空间?
Posted
技术标签:
【中文标题】动态隐藏状态栏时如何保留状态栏占用的空间?【英文标题】:How to preserve space occupied by status bar when hiding status bar animately? 【发布时间】:2021-05-29 08:55:49 【问题描述】:我倾向于隐藏状态栏,动画方式如下。
var statusBarHidden: Bool = false
didSet
UIView.animate(withDuration: Constants.config_shortAnimTime) () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
override var prefersStatusBarHidden: Bool
return statusBarHidden
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation
return .slide
extension ViewController: SideMenuNavigationControllerDelegate
func sideMenuWillAppear(menu: SideMenuNavigationController, animated: Bool)
statusBarHidden = true
func sideMenuDidAppear(menu: SideMenuNavigationController, animated: Bool)
func sideMenuWillDisappear(menu: SideMenuNavigationController, animated: Bool)
func sideMenuDidDisappear(menu: SideMenuNavigationController, animated: Bool)
statusBarHidden = false
不过,我也想保留状态栏占用的空间,这样当状态栏出现时,整个app就不会被“上推”了
我可以知道我怎样才能做到这一点吗?
谢谢。
【问题讨论】:
【参考方案1】:您可以使用additionalSafeAreaInsets
添加占位符高度,替换状态栏。
但对于像 iPhone 12 这样带有凹口的设备,空间会自动保留,因此您无需添加任何额外的高度。
class ViewController: UIViewController
var statusBarHidden: Bool = false /// no more computed property, otherwise reading safe area would be too late
override var prefersStatusBarHidden: Bool
return statusBarHidden
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation
return .slide
@IBAction func showButtonPressed(_ sender: Any)
statusBarHidden.toggle()
if statusBarHidden
sideMenuWillAppear()
else
sideMenuWillDisappear()
lazy var overlayViewController: UIViewController =
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "OverlayViewController")
()
var additionalHeight: CGFloat
if view.window?.safeAreaInsets.top ?? 0 > 20 /// is iPhone X or other device with notch
return 0 /// add 0 height
else
/// the height of the status bar
return view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0
extension ViewController
/// add placeholder height to substitute status bar
func addAdditionalHeight(_ add: Bool)
if add
if let navigationController = self.navigationController
/// set insets of navigation controller if you're using navigation controller
navigationController.additionalSafeAreaInsets.top = additionalHeight
else
/// set insets of self if not using navigation controller
self.additionalSafeAreaInsets.top = additionalHeight
else
if let navigationController = self.navigationController
/// set insets of navigation controller if you're using navigation controller
navigationController.additionalSafeAreaInsets.top = 0
else
/// set insets of self if not using navigation controller
self.additionalSafeAreaInsets.top = 0
func sideMenuWillAppear()
addChild(overlayViewController)
view.addSubview(overlayViewController.view)
overlayViewController.view.frame = view.bounds
overlayViewController.view.frame.origin.x = -400
overlayViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
overlayViewController.didMove(toParent: self)
addAdditionalHeight(true) /// add placeholder height
UIView.animate(withDuration: 1)
self.overlayViewController.view.frame.origin.x = -100
self.setNeedsStatusBarAppearanceUpdate() /// hide status bar
func sideMenuDidAppear()
func sideMenuWillDisappear()
addAdditionalHeight(false) /// remove placeholder height
UIView.animate(withDuration: 1)
self.overlayViewController.view.frame.origin.x = -400
self.setNeedsStatusBarAppearanceUpdate() /// show status bar
completion: _ in
self.overlayViewController.willMove(toParent: nil)
self.overlayViewController.view.removeFromSuperview()
self.overlayViewController.removeFromParent()
func sideMenuDidDisappear()
结果(在 iPhone 12、iPhone 8、iPad Pro 第 4 代测试):
iPhone 12 (notch) | iPhone 8 (no notch) |
---|---|
iPhone 12 + navigation bar | iPhone 8 + navigation bar |
---|---|
Demo GitHub repo
【讨论】:
感谢您的提示。我试着听从你的建议。但是,顶部空间仍然受到动画的影响,如 - imgur.com/a/UW4aq7a 所示,你知道我在代码中做错了什么吗? - gist.github.com/yccheok/084439dee440e3ce9200c104950b7529 谢谢。 @CheokYanCheng 我已经添加了演示 GitHub 存储库的链接。在您的项目中,您是否在其他任何地方调用过setNeedsStatusBarAppearanceUpdate
?
同时搜索layoutIfNeeded
。这可能会影响动画。
谢谢。在用你的演示代码测试之后,我知道我的代码有什么问题 - gist.github.com/yccheok/b32fd2d321c18818037a62e06b1bbab9 似乎当我们调整顶部时,prefersStatusBarHidden
也会被触发。因此,我们需要首先改变 bool 变量。非常感谢【参考方案2】:
首先,目前不可能使UINavigationController
以这种方式运行。但是,您可以将 UINavigationController
实例包装在容器视图控制器中。
这将使您能够控制从UINavigationController
视图布局开始的顶部空间的管理。在这个容器类中,你可以像下面这样管理它 -
class ContainerViewController: UIViewController
private lazy var statusBarBackgroundView: UIView =
let view = UIView(frame: .zero)
view.backgroundColor = .clear
view.translatesAutoresizingMaskIntoConstraints = false
return view
()
private lazy var statusBarBackgroundViewHeightConstraint: NSLayoutConstraint =
statusBarBackgroundView.heightAnchor.constraint(equalToConstant: 0)
()
var statusBarHeight: CGFloat
if #available(ios 13.0, *)
guard let statusBarMananger = self.view.window?.windowScene?.statusBarManager
else return 0
return statusBarMananger.statusBarFrame.height
else
return UIApplication.shared.statusBarFrame.height
var statusBarHidden: Bool = false
didSet
self.statusBarBackgroundViewHeightConstraint.constant = self.statusBarHidden ? self.lastKnownStatusBarHeight : 0
self.view.layoutIfNeeded()
private var lastKnownStatusBarHeight: CGFloat = 0
override func viewDidLoad()
super.viewDidLoad()
let topView = self.statusBarBackgroundView
self.view.addSubview(topView)
NSLayoutConstraint.activate([
topView.topAnchor.constraint(equalTo: self.view.topAnchor),
topView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
statusBarBackgroundViewHeightConstraint,
topView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
let height = self.statusBarHeight
if height > 0
self.lastKnownStatusBarHeight = height
func setUpNavigationController(_ navCtrl: UINavigationController)
self.addChild(navCtrl)
navCtrl.didMove(toParent: self)
self.view.addSubview(navCtrl.view)
navCtrl.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
navCtrl.view.topAnchor.constraint(equalTo: statusBarBackgroundView.bottomAnchor),
navCtrl.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
navCtrl.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
navCtrl.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
self.view.layoutIfNeeded()
现在从您的呼叫站点,您可以执行以下操作 -
class ViewController: UIViewController
var statusBarHidden: Bool = false
didSet
UIView.animate(withDuration: Constants.config_shortAnimTime) () -> Void in
/// Forward the call to ContainerViewController to act on this update
(self.navigationController?.parent as? ContainerViewController)?.statusBarHidden = self.statusBarHidden
/// Keep doing whatever you are doing now
self.setNeedsStatusBarAppearanceUpdate()
【讨论】:
以上是关于动态隐藏状态栏时如何保留状态栏占用的空间?的主要内容,如果未能解决你的问题,请参考以下文章