前言

最近在iPhone 12 系列机型上开发项目时,发现使用项目提供的获取状态栏、导航栏高度方法获取到的高度是错误的,随后跟踪排查最终解决这个问题,所以自己想简单的总结一下问题原因和解决办法。

本文主要介绍问题原因和解决办法,最终提供一个能准确获取iPhone 状态栏、导航栏、TabBar高度的方法。

问题原因

出现问题的原因是,我们大多开发在使用获取状态栏、导航栏高度方法都是以下方法来获取的:

// 状态栏高度,iPhoneX 是判断是否为刘海屏
#define StatusBar_Height (isIPhoneX ? 44.0f : 20.0f)

在iOS 14系统以前,使用这种方法获取是没有问题的,但是在之后,刘海屏的手机状态栏高度就不在是统一的44px咯,根据下表我们看一看目前iOS 15系统上的各机型的状态栏高度:

机型状态栏高度
iPhone XR/1148px
iPhone X/11 Pro/ 11 Pro Max/12 mini44px
iPhone 12/12 Pro/Pro Max47px

使用系统方法获取状态栏高度

可以看出使用上面方法已经不在满足我们的开发需求了,于是我就想到使用系统提供的方法来获取,通过 UIApplication 单例中的 statusBarFrame 属性获取状态状态栏改度:

[UIApplication sharedApplication].statusBarFrame.size.height;

我发现 iOS 13.0系统 之后,UIApplication 单例中的 statusBarFrame 属性被废弃了。不建议使用了,系统希望我们使用 UIStatusBarManager 类中的 statusBarFrame 来进行获取:

if (@available(iOS 13.0, *)) {
	NSSet *set = [UIApplication sharedApplication].connectedScenes;
    UIWindowScene *windowScene = [set anyObject];
    UIStatusBarManager *statusBarManager = windowScene.statusBarManager;
    return statusBarManager.statusBarFrame.size.height;
}


代码模块

为了统一和方便快速获取系统顶部和底部安全区、顶部状态栏和导航栏、底部 tabBar,我将新建一个分类来实现这些方法,方便项目全局引用:

在 UIDevice+StateHeight.h 中,声明各方法名称:

@interface UIDevice ()

/** 顶部安全区高度 **/
+ (CGFloat)dev_safeDistanceTop;

/** 底部安全区高度 **/
+ (CGFloat)dev_safeDistanceBottom;

/** 顶部状态栏高度(包括安全区) **/
+ (CGFloat)dev_statusBarHeight;

/** 导航栏高度 **/
+ (CGFloat)dev_navigationBarHeight;

/** 状态栏+导航栏的高度 **/
+ (CGFloat)dev_navigationFullHeight;

/** 底部导航栏高度 **/
+ (CGFloat)dev_tabBarHeight;

/** 底部导航栏高度(包括安全区) **/
+ (CGFloat)dev_tabBarFullHeight;

@end

在 UIDevice+StateHeight.m 中实现各方法进行。

#import "UIDevice+StateHeight.h"

@implementation UIDevice ()

// 顶部安全区高度
+ (CGFloat)dev_safeDistanceTop {
    if (@available(iOS 13.0, *)) {
        NSSet *set = [UIApplication sharedApplication].connectedScenes;
        UIWindowScene *windowScene = [set anyObject];
        UIWindow *window = windowScene.windows.firstObject;
        return window.safeAreaInsets.top;
    } else if (@available(iOS 11.0, *)) {
        UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
        return window.safeAreaInsets.top;
    }
    return 0;
}

// 底部安全区高度
+ (CGFloat)dev_safeDistanceBottom {
    if (@available(iOS 13.0, *)) {
        NSSet *set = [UIApplication sharedApplication].connectedScenes;
        UIWindowScene *windowScene = [set anyObject];
        UIWindow *window = windowScene.windows.firstObject;
        return window.safeAreaInsets.bottom;
    } else if (@available(iOS 11.0, *)) {
        UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
        return window.safeAreaInsets.bottom;
    }
    return 0;
}


//顶部状态栏高度(包括安全区)
+ (CGFloat)dev_statusBarHeight {
    if (@available(iOS 13.0, *)) {
        NSSet *set = [UIApplication sharedApplication].connectedScenes;
        UIWindowScene *windowScene = [set anyObject];
        UIStatusBarManager *statusBarManager = windowScene.statusBarManager;
        return statusBarManager.statusBarFrame.size.height;
    } else {
        return [UIApplication sharedApplication].statusBarFrame.size.height;
    }
}

// 导航栏高度
+ (CGFloat)dev_navigationBarHeight {
    return 44.0f;
}

// 状态栏+导航栏的高度
+ (CGFloat)dev_navigationFullHeight {
    return [UIDevice vg_statusBarHeight] + [UIDevice vg_navigationBarHeight];
}

// 底部导航栏高度
+ (CGFloat)dev_tabBarHeight {
    return 49.0f;
}

// 底部导航栏高度(包括安全区)
+ (CGFloat)dev_tabBarFullHeight {
    return [UIDevice vg_statusBarHeight] + [UIDevice vg_safeDistanceBottom];
}

@end

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐