UIViewController In-Call状态栏问题

时间:2017-04-24 18:43:00

标签: ios swift statusbar modalviewcontroller in-call

问题:

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

正常:无问题

enter image description here

待命:无问题

enter image description here

在通话中消失后:

在顶部留下20px高的空/透明空间,露出下面的橙色视图。但是,状态栏仍然存在于透明区域上方。导航栏还为状态栏留出了空间,它的状态栏是只有20px的位置太低。

enter image description here

enter image description here

  • 基于iOS 10
  • 模态呈现的视图控制器
  • 自定义模式演示文稿
  • 后面的主视图控制器是橙色
  • 不使用Autolayout
  • 当旋转到风景时,20px In-Call Bar离开并仍然留下20px的间隙。
  • 我选择退出在横向方向显示状态栏。 (即大多数股票应用程序)

我试着听App代表:

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)

        })
 }

8 个答案:

答案 0 :(得分: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
    })
}
希望这件事能对你有所帮助。
谢谢

答案 1 :(得分:5)

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

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

将方法添加到AppDelegate.swift

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

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

P.S。它眨了一下,但是有效。

答案 2 :(得分:3)

我在修改状态栏的人员hospot遇到了同样的问题。 解决方案是注册系统通知以更改状态栏框架,这将允许您更新布局并应修复您可能遇到的任何布局问题。 我的解决方案对你来说应该是完全相同的:

  • 在视图控制器中,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传递给方法,保持引用以更改布局和有一种方法来消除自我。你知道,为了重用代码。

答案 3 :(得分:3)

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

要解决此问题,您可以在演示时提供自定义演示控制器。然后,如果您不需要呈现控制器的视图保留在视图层次结构中,您只需返回true表示控制器的shouldRemovePresentersView属性,并且&#39是的。

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

答案 4 :(得分:3)

我一直在寻找这个问题的解决方案。事实上,我发布了一个与此类似的新问题。这里:How To Avoid iOS Blue Location NavigationBar Messing Up My StatusBar?

相信我,我已经解决了这几天了,因为iOS的状态栏因通话,热点和位置的变化而让你的屏幕搞砸了,真的很烦人。

我尝试过实施Modi的答案,我把这段代码放在我的AppDelegate中并对其进行了一些修改,但没有运气。而且我相信iOS会自动执行此操作,因此您无需亲自实现。

在我发现问题的罪魁祸首之前,我确实尝试过这个特定问题的每个解决方案。无需实现AppDelegate的方法willChangeStatusBar...或添加通知来观察statusBar更改。

我还通过编程方式(我正在使用故事板)重做我的项目的一些流程。我进行了一些实验,然后检查了我之前和其他当前的项目,为什么他们正在进行适当的调整:)

底线是:我用UITabBarController以错误的方式呈现我的主屏幕。

请始终注意modalPresentationStyle。由于诺亚的评论,我有了查看我的代码的想法。

样品:

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

答案 5 :(得分:1)

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

在目标C

tabBar.autoresizingMask = (UIViewAutoResizingFlexibleWidth | UIViewAutoResizingFlexibleTopMargin);

在Swift中

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

您只需要从顶部开始使用tabbar灵活的自动调整功能。

答案 6 :(得分:1)

对于我来说,我正在为ViewController使用自定义表示样式。 问题在于Y位置的计算不正确。 假设原始萤幕高度为736p。 尝试打印view.frame.origin.yview.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()
    }
}

答案 7 :(得分:0)

在将要显示的视图控制器视图的框架添加到容器视图之后,请确保将其设置为容器视图的边界。这为我解决了这个问题。

containerView.addSubview(toViewController.view)
toViewController.view.frame = containerView.bounds