如何做布局来处理IOS 8中的自定义呈现视图控制器的通话(双高度)状态栏?

在iOS 7中,我能够处理自定义呈现的视图控制器的布局,通过添加约束条件来处理调用状态栏,其中呈现的视图控制器具有与呈现视图控制器相同的中心和相同的宽度和高度。 这是因为宽度和高度使用自动resize,否则,当通话状态栏消失时,出现的视图控制器将由于某种原因从顶部向下按20个点。

然而,在iOS 8中,这个技巧不再适用。 首先,我通过实验发现,呈现视图控制器似乎不一定在容器视图中,所以我不能将这些约束添加到容器视图。 ( “A Look Inside Presentation Controllers”2014 WWDCvideo的示例代码并没有将呈现视图控制器放在那里)。看起来,让呈现的视图控制器使用自动布局或自动resize来填充容器视图是可行的,但是我发现当通话状态栏消失时,容器视图本身可能会被压低20点(这在iOS 7中并不是这样)。

我一直在玩“内置演示控制器”示例代码 ,但即使这样的代码也不能正确处理通话状态栏。 ( UIPresentationController ,我仍然试图去处理新的UIPresentationController API。)

什么对我来说是添加一个观察者UIApplicationWillChangeStatusBarFrameNotification ,然后findtransitionContext.containerView()并更新框架。 我注意到当查看debugging时,返回到正常的状态栏高度时, transitionContext.containerView()框架没有更新。

 func willChangeStatusBarFrame(notification: NSNotification) { if let userInfo = notification.userInfo { if let value = userInfo[UIApplicationStatusBarFrameUserInfoKey] as? NSValue { let statusBarFrame = value.CGRectValue() let transitionView = UIApplication.sharedApplication().delegate!.window!!.subviews.last! as UIView var frame = transitionView.frame frame.origin.y = statusBarFrame.height-20 frame.size.height = UIScreen.mainScreen().bounds.height-frame.origin.y UIView.animateWithDuration(0.35) { transitionView.frame = frame } } } } 

在导航堆栈顶部的视图控制器内部,我添加了:

 - (void)viewDidLoad { [super viewDidLoad]; self.view.frame = [[UIScreen mainScreen] bounds]; // rest of the code } 

现在,我不再有顶部偏移问题了。 不知道这是否是适合您的解决scheme。 如果这个视图控制器有时候不是全屏显示的,那么在viewDidLoad里面显然手动框架设置是一个坏主意。

在appdelegate中添加以下两个方法。 在模拟器上按下Command + Y切换呼叫状态栏并进行testing。 解决scheme仅使用自动布局进行testing。

 -(void) application:(UIApplication *)application didChangeStatusBarFrame: (CGRect)oldStatusBarFrame { [self.window layoutIfNeeded]; [self.window updateConstraintsIfNeeded]; } -(void) application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame { [self.window layoutIfNeeded]; [self.window updateConstraintsIfNeeded]; } 

如果通话栏在应用程序启动时已经可见,则didChange通知无效。 另外,我没有看到在通知事件中获取statusBarSize的必要unrollment,你可以简单地通过UIApplication.shared.statusBarSize获取它。

这是Swift 3中的解决scheme,适用于我:

 class MainTabBarViewController: UITabBarController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) setNeedsStatusBarAppearanceUpdate() } override func viewDidLoad() { super.viewDidLoad() let notificationCenter = NotificationCenter.default notificationCenter.addObserver( self, selector: #selector(statusBarFrameDidChange), name: .UIApplicationDidChangeStatusBarFrame, object: nil) fixViewSizeDependingOnStatusbar() self.view.layoutIfNeeded() } private func fixViewSizeDependingOnStatusbar() { self.view.frame = CGRect( x:0, y:0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - UIApplication.shared.statusBarFrame.height + 20) // I have no clue why I have to add 20, // looks like UIScreen.main.bounds already // shrinked by 20 for defaultStatusBar size? } override func viewWillLayoutSubviews() { fixViewSizeDependingOnStatusbar() super.viewWillLayoutSubviews() } func statusBarFrameDidChange() { fixViewSizeDependingOnStatusbar() self.view.layoutIfNeeded() } }