如何在iOS 8中为自定义呈现的视图控制器处理调用(双高)状态栏的布局?

时间:2014-09-17 05:00:13

标签: ios iphone cocoa-touch uiviewcontroller ios8

在iOS 7中,我能够处理自定义呈现视图控制器的布局,还可以通过添加约束来解释通话状态栏,其中呈现的视图控制器具有与呈现相同的中心和相同的宽度和高度查看控制器。这对于宽度和高度使用自动调整大小是首选,因为否则,当通话状态栏消失时,由于某种原因,呈现的视图控制器将从顶部向下推动20个点。

然而,在iOS 8中,这个技巧不再起作用了。首先,我通过实验发现,呈现视图控制器似乎不一定在容器视图中,因此我无法将这些约束添加到容器视图中。 (sample code"A Look Inside Presentation Controllers" 2014 WWDC video没有将呈现视图控制器放在那里。)似乎让所呈现的视图控制器使用自动布局或自动调整大小来填充容器视图会起作用,但是我发现当呼叫状态栏消失时,容器视图本身可能会被按下20分(在iOS 7中不是这种情况)。

我一直在使用"A Look Inside Presentation Controllers" sample code,但即使这段代码也无法正确处理通话状态栏。 (顺便说一句,我仍然试图处理新的UIPresentationController API。)

6 个答案:

答案 0 :(得分:2)

对我有用的是为UIApplicationWillChangeStatusBarFrameNotification添加观察者,然后找到transitionContext.containerView()并更新框架。我注意到在查看调试时,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
            }
        }
    }
}

答案 1 :(得分:0)

在推送到导航堆栈顶部的视图控制器中,我添加了:

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

现在,我不再遇到顶部偏移问题了。不确定这是否适合您。如果这个视图控制器有时不是全屏显示,那么显然在viewDidLoad中手动设置框架是一个坏主意。

答案 2 :(得分:0)

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

-(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];
}

答案 3 :(得分:0)

如果应用程序启动时调用栏已经显示,则didChange通知无效。此外,我没有看到在通知事件中获取statusBarSize的必要重新注册,您只需通过UIApplication.shared.statusBarSize获取它。

这是Swift 3中适合我的解决方案:

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

答案 4 :(得分:0)

我通过简单的一行代码解决了这个问题

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

供参考click here

答案 5 :(得分:0)

快捷键4

问题在于模式展示是在容器视图(transitionContext.containerView)内进行的,默认情况下,该容器未配置为自动布局,视图控制器也不在其中显示。

因此,在animateTransition(using transitionContext: UIViewControllerContextTransitioning)方法中,配置容器视图以利用自动布局:

guard let toViewController = transitionContext.viewController(forKey: .to),
    let root = UIApplication.shared.keyWindow!.rootViewController else {

        return transitionContext.completeTransition(false)

}

containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.leadingAnchor.constraint(equalTo: root.view.leadingAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: root.view.widthAnchor).isActive = true
containerView.heightAnchor.constraint(equalTo: root.view.heightAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: root.view.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: root.view.bottomAnchor).isActive = true

因为视图控制器通常是从根目录显示的(以避免警告不要将视图控制器显示在分离的视图控制器上),所以将容器锚定到根目录的视图(最可靠地响应于双高状态栏的更改) )。

然后将显示的视图控制器配置为也利用自动布局:

toViewController.view.leadingAnchor.constraint(equalTo: root.view.leadingAnchor).isActive = true
toViewController.view.widthAnchor.constraint(equalTo: root.view.widthAnchor).isActive = true
toViewController.view.heightAnchor.constraint(equalTo: root.view.heightAnchor).isActive = true
toViewController.view.trailingAnchor.constraint(equalTo: root.view.trailingAnchor).isActive = true
toViewController.view.bottomAnchor.constraint(equalTo: root.view.bottomAnchor).isActive = true

只要您没有在其他地方显式设置呈现的视图控制器的框架(通常以编程方式创建的视图控制器就是这种情况),则该模态视图控制器将像所有其他对象一样响应双倍高度状态栏其他视图控制器。