19

问题:

通话状态栏消失后,模态呈现的视图控制器不会向后移动,顶部留下 20px 的空白/透明空间。


正常:没有问题

在此处输入图像描述


通话中:没有问题

在此处输入图像描述


通话中消失后:

在顶部留下一个 20 像素高的空白/透明空间,显示下方的橙色视图。但是状态栏仍然存在于透明区域上。导航栏还为状态栏留出了空间,它的位置太低了 20 像素。

在此处输入图像描述

在此处输入图像描述


  • 基于 iOS 10
  • 模态呈现的视图控制器
  • 自定义模态演示
  • 后面的主视图控制器是橙色的
  • 不使用自动布局
  • 当旋转到Landscape时,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)

        })
 }
4

8 回答 8

6

我一直在寻找解决方案 3 天。我不喜欢这个解决方案,但没有找到更好的解决方法。

当 rootViewController 视图的高度比窗口高 20 点时,我遇到了这种情况,当我收到有关状态栏高度更新的通知时,我手动设置了正确的值。

将方法添加到 AppDelegate.swift

func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect) {
        if let window = application.keyWindow {
            window.rootViewController?.view.frame = window.frame
        }
    }

之后它按预期工作(即使在方向改变之后)。希望它会对某人有所帮助,因为我在这上面花了太多时间。

PS它闪烁了一下,但有效。

于 2018-02-23T13:44:28.367 回答
5

我也遇到了这个问题,但是在我使用这个方法之后,问题就消失了。

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
    })
}

希望这件事对你有帮助。
谢谢

于 2017-04-28T09:46:48.650 回答
3

我一直在寻找解决这个问题的方法。事实上,我发布了一个与此类似的新问题。在这里:如何避免 iOS 蓝色位置导航栏弄乱我的状态栏?

相信我,我已经解决了几天了,由于 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)
    }
}
于 2017-09-13T17:47:11.967 回答
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)
    
  • 您还需要您的模态来负责状态栏,因此您应该设置

    destVC.modalPresentationCapturesStatusBarAppearance = true 在呈现视图之前。

您可以在每个容易在状态栏上发生更改的控制器上实现此功能,或者您可以创建另一个将为每个控制器执行此操作的类,例如将 self 传递给一个方法,保留更改布局的引用并拥有一个方法删除自我。你知道,为了重用代码。

于 2017-04-28T09:54:32.787 回答
3

我认为这是 UIKit 中的一个错误。当containerView状态栏恢复到正常大小时,包含使用自定义转换呈现的呈现控制器视图的 似乎没有完全向后移动。(您可以在关闭通话状态栏后查看视图层次结构)

为了解决这个问题,您可以在演示时提供自定义演示控制器。然后,如果您不需要呈现控制器的视图保留在视图层次结构中,您可以返回呈现控制器trueshouldRemovePresentersView属性,仅此而已。

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
            })
        }
    }
}
于 2017-05-03T03:13:47.583 回答
1

我通过使用一行代码解决了这个问题

在目标 C

tabBar.autoresizingMask = (UIViewAutoResizingFlexibleWidth | UIViewAutoResizingFlexibleTopMargin);

在斯威夫特

self.tabBarController?.tabBar.autoresizingMask = 
  UIViewAutoresizing(rawValue: UIViewAutoresizing.RawValue(UInt8(UIViewAutoresizing.flexibleWidth.rawValue) | UInt8(UIViewAutoresizing.flexibleTopMargin.rawValue)))`

您只需要从顶部使 tabBar 的 autoresizingMask 变得灵活。

于 2018-05-16T09:31:39.457 回答
1

就我而言,我正在为我的 ViewController 使用自定义演示样式。问题是Y位置没有计算好。假设原始屏幕高度为 736p。尝试打印view.frame.origin.yand 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()
    }
}
于 2019-06-04T04:11:18.567 回答
0

在添加到容器视图之后,请务必将您呈现的视图控制器视图的框架设置为容器视图的边界。这为我解决了这个问题。

containerView.addSubview(toViewController.view)
toViewController.view.frame = containerView.bounds
于 2018-12-10T19:17:26.760 回答