UIViewController 通话状态栏问题
Posted
技术标签:
【中文标题】UIViewController 通话状态栏问题【英文标题】:UIViewController In-Call Status Bar Issue 【发布时间】:2017-09-21 13:45:29 【问题描述】:问题:
在通话状态栏消失后,模态显示的视图控制器不会向后移动,顶部留下 20px 的空白/透明空间。
正常:没有问题
通话中:没有问题
通话消失后:
在顶部留下 20 像素高的空白/透明空间,显示下方的橙色视图。但是状态栏仍然存在于透明区域上。导航栏也为状态栏留出了空间,它的位置太低了 20px。
基于 ios 10 模态显示的视图控制器 自定义模态展示 主视图控制器后面是橙色的 不使用自动布局 当旋转到横向时,20px In-Call Bar 离开并仍然留下 20px 间隙。 我选择不以横向显示状态栏。 (即大多数股票应用)
我尝试听 App Delegates:
willChangeStatusBarFrame
didChangeStatusBarFrame
还查看基于控制器的通知:
UIApplicationWillChangeStatusBarFrame
UIApplicationDidChangeStatusBarFrame
当我为上述所有四种方法记录呈现视图的框架时,该框架始终位于 (y: 0) 原点。
更新
视图控制器自定义模态展示
let storyboard = UIStoryboard(name: "StoryBoard1", bundle: nil)
self.modalVC = storyboard.instantiateViewController(withIdentifier: "My Modal View Controller") as? MyModalViewController
self.modalVC!.transitioningDelegate = self
self.modalVC.modalPresentationStyle = .custom
self.modalVC.modalPresentationCapturesStatusBarAppearance = true;
self.present(self.modalVC!, animated: true, completion: nil)
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
let containerView = transitionContext.containerView
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
toViewController!.view.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.0, options: [.curveEaseOut], animations: () -> Void in
toViewController!.view.transform = CGAffineTransform.identity
, completion: (completed) -> Void in
transitionContext.completeTransition(completed)
)
【问题讨论】:
“自定义模态演示”是什么意思?您能否详细说明您是如何呈现模态 VC 的? 更新了 OP。在过渡动画中,我只是将其缩小并以 1:1 的比例制作动画。 你的模态VC通过设置这个属性modalPresentationCapturesStatusBarAppearance
来控制状态栏的外观。您是否覆盖了模态 VC 的 preferredStatusBarStyle
或 prefersStatusBarHidden
方法?
modalPresentationCapturesStatusBarAppearance 如您所见为真。我也确实覆盖了preferredStatusBarStyle 和prefersStatusBarHidden 方法。状态栏在正确的时间正确显示。仅当 In-Call 消失时,视图才不会调整大小。
不幸的是,自己没有时间检查这种行为。但是这个问题很有趣,我很喜欢它:) 到目前为止运气好吗?
【参考方案1】:
3 天来我一直在寻找解决方案。我不喜欢这个解决方案,但没有找到更好的解决方法。
当 rootViewController 视图的高度比窗口高 20 点时,当我收到有关状态栏高度更新的通知时,我会手动设置正确的值。
将方法添加到 AppDelegate.swift
func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect)
if let window = application.keyWindow
window.rootViewController?.view.frame = window.frame
之后它会按预期工作(即使在方向改变之后)。 希望它会对某人有所帮助,因为我在这方面花费了太多时间。
附:它会闪烁一点,但有效。
【讨论】:
这是唯一一个对我有用的简单解决方案,即使在 iOS12.1 上没有闪烁willChangeStatusBarFrame
现在对我有用【参考方案2】:
我也遇到过这个问题,但是用了这个方法后,问题就消失了。
iOS 有它的默认方法willChangeStatusBarFrame
来处理状态栏。请把这个方法检查一下。
func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect)
UIView.animate(withDuration: 0.35, animations: () -> Void in
let windowFrame: CGRect? = ((window?.rootViewController? as? UITabBarController)?.viewControllers[0] as? UINavigationController)?.view?.frame
if newStatusBarFrame.size.height > 20
windowFrame?.origin?.y = newStatusBarFrame.size.height - 20
// old status bar frame is 20
else
windowFrame?.origin?.y = 0.0
((window?.rootViewController? as? UITabBarController)?.viewControllers[0] as? UINavigationController)?.view?.frame = windowFrame
)
希望这篇文章对你有所帮助。 谢谢
【讨论】:
当我在我的 OP 中提到这个委托调用时,我尝试了这个。这会暂时修复它,直到您将视图旋转到不同的方向。由于我没有在横向中显示状态栏,因此这会破坏方向更改的布局。混乱..混乱.. 另外,仅移动原点是不够的,似乎。框架也加高了…… @Gizmodo 尝试编写一个条件来检查设备是处于纵向还是横向模式,如果处于横向模式,则状态栏中没有任何变化,否则按照方法进行更改。尝试一次。 好吧,在横向模式中,In-Call 消失后会留下 20px 的空白空间。这使它与肖像相同。在这两种情况下,当我检查顶视图控制器框架的 Y 原点时,它都是 0。我想知道这是否是一个错误。 但是你在横向模式下隐藏状态栏,试试 unhidden @Gizmodo【参考方案3】:我在修改状态栏时遇到了同样的问题。 解决方案是向系统通知注册状态栏框架的更改,这将允许您更新布局并解决您可能遇到的任何布局问题。 我的解决方案应该对您完全一样:
在您的视图控制器中,在viewWillAppear
中订阅UIApplicationDidChangeStatusBarFrameNotification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(myControllerName.handleFrameResize(_:)), name: UIApplicationDidChangeStatusBarFrameNotification, object: nil)
创建你的选择器方法
func handleFrameResize(notification: NSNotification)
self.view.layoutIfNeeded()
从viewWillDisappear
的通知中心移除你的控制器
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIApplicationDidChangeStatusBarFrameNotification, object: nil)
你还需要你的modal负责状态栏所以你应该设置
destVC.modalPresentationCapturesStatusBarAppearance = true
在呈现视图之前。
您可以在每个容易在状态栏上发生更改的控制器上实现此功能,或者您可以创建另一个类来为每个控制器执行此操作,例如将 self 传递给方法,保留更改布局的引用和有一种方法可以删除自我。你知道,为了重用代码。
【讨论】:
我在 OP 中提到我确实使用了 UIApplicationWillChangeStatusBarFrame、UIApplicationDidChangeStatusBarFrame。问题是,模式视图控制器的原点正在注销 (0, 0) 的原点,即使它的 20px 太低。 不确定您使用它完成了什么。将进行一些测试,我会尽快回复您以获取最新答案 @Gizmodo 在呈现之前将 modalPresentationCapturesStatusBarAppearance 添加到模态似乎可以解决问题。我的堆栈与您的堆栈相同,并且对我有用。我不知道它会对视图的移动做出什么反应,如果这不起作用,您应该考虑为此使用自动布局 看我上面的代码,我以为我在演示之前就在做? @Gizmodo 您是否尝试将您的模式嵌入到导航控制器中?【参考方案4】:我认为这是 UIKit 中的一个错误。当状态栏恢复到正常大小时,包含使用自定义转换呈现的呈现控制器视图的containerView
似乎没有完全向后移动。 (您可以在关闭通话状态栏后查看视图层次结构)
为了解决这个问题,您可以在演示时提供自定义演示控制器。然后,如果您不需要呈现控制器的视图保留在视图层次结构中,您只需返回 true
以获得呈现控制器的 shouldRemovePresentersView
属性,就是这样。
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?
return PresentationController(presentedViewController: presented, presenting: presenting)
class PresentationController: UIPresentationController
override var shouldRemovePresentersView: Bool
return true
或者如果您需要保留当前控制器的视图,您可以观察状态栏框架的变化并手动将containerView
调整为与其父视图相同的大小
class PresentationController: UIPresentationController
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?)
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
NotificationCenter.default.addObserver(self,
selector: #selector(self.onStatusBarChanged),
name: .UIApplicationWillChangeStatusBarFrame,
object: nil)
@objc func onStatusBarChanged(note: NSNotification)
//I can't find a way to ask the system for the values of these constants, maybe you can
if UIApplication.shared.statusBarFrame.height <= 20,
let superView = containerView?.superview
UIView.animate(withDuration: 0.4, animations:
self.containerView?.frame = superView.bounds
)
【讨论】:
【参考方案5】:我一直在寻找解决这个问题的方法。事实上,我发布了一个与此类似的新问题。这里:How To Avoid iOS Blue Location NavigationBar Messing Up My StatusBar?
相信我,这几天我一直在解决这个问题,但由于 iOS 的状态栏会随通话中、热点和位置的变化而变化,因此屏幕混乱真的很烦人。
我已经尝试实现 Modi 的答案,我将那段代码放在我的 AppDelegate 中并对其进行了一些修改,但没有运气。我相信 iOS 会自动执行此操作,因此您不必自己实现。
在我发现问题的罪魁祸首之前,我确实尝试了这个特定问题的所有解决方案。无需实现AppDelegate的方法willChangeStatusBar...
或添加通知观察statusBar变化。
我还重做了我项目的一些流程,以编程方式制作了一些屏幕(我正在使用情节提要)。我做了一些实验,然后检查了我以前和其他当前的项目,为什么他们正确地进行了调整:)
底线是:我用 UITabBarController 以这种错误的方式显示我的主屏幕。
请始终注意modalPresentationStyle
。由于 Noah 的评论,我有了查看我的代码的想法。
示例:
func presentDashboard()
if let tabBarController = R.storyboard.root.baseTabBarController()
tabBarController.selectedIndex = 1
tabBarController.modalPresentationStyle = .fullScreen
tabBarController.modalTransitionStyle = .crossDissolve
self.baseTabBarController = tabBarController
self.navigationController?.present(tabBarController, animated: true, completion: nil)
【讨论】:
【参考方案6】:我用一行代码解决了这个问题
在目标 C 中
tabBar.autoresizingMask = (UIViewAutoResizingFlexibleWidth | UIViewAutoResizingFlexibleTopMargin);
在 Swift 中
self.tabBarController?.tabBar.autoresizingMask =
UIViewAutoresizing(rawValue: UIViewAutoresizing.RawValue(UInt8(UIViewAutoresizing.flexibleWidth.rawValue) | UInt8(UIViewAutoresizing.flexibleTopMargin.rawValue)))`
您只需要从顶部使 tabBar 的 autoresizingMask 变得灵活。
【讨论】:
这在 mytabbar 控制器或带有过渡的视图中对我不起作用【参考方案7】:就我而言,我正在为我的 ViewController 使用自定义演示样式。
问题是Y位置没有计算好。
假设原始屏幕高度为 736p。
尝试打印view.frame.origin.y
和view.frame.height
,你会发现高度是716p,y是20。
但显示高度为 736 - 20(通话状态栏额外高度) - 20(y 位置)。
这就是为什么我们的视图是从 ViewController 的底部切开的,以及为什么到顶部有 20p 的边距。
但是如果你回去看看导航控制器的帧值。
你会发现无论通话状态栏是否显示,y位置始终为0。
所以,我们要做的就是将 y 位置设置为零。
override func viewDidAppear(_ animated: Bool)
super.viewDidAppear(animated)
let f = self.view.frame
if f.origin.y != 0
self.view.frame = CGRect(x: f.origin.x, y: 0, width: f.width, height: f.height)
self.view.layoutIfNeeded()
self.view.updateConstraintsIfNeeded()
【讨论】:
【参考方案8】:确保在容器视图被添加到容器视图之后,将您呈现的视图控制器视图的框架设置为容器视图的边界。这为我解决了这个问题。
containerView.addSubview(toViewController.view)
toViewController.view.frame = containerView.bounds
【讨论】:
以上是关于UIViewController 通话状态栏问题的主要内容,如果未能解决你的问题,请参考以下文章