如何实现iOS应用通话时像App Store一样切换状态栏高度至20pt?
嘿,这个需求我之前做阅读类APP时碰到过,其实要实现通话状态下把状态栏固定为20pt,主要分两步:监听通话状态变化,然后动态调整状态栏高度。下面给你详细拆解:
1. 监听通话状态变化
iOS提供了两种主流方式监听通话状态:CoreTelephony框架(传统兼容方案)和CallKit(iOS 10+官方推荐)。
方式一:使用CoreTelephony(兼容iOS 10以下)
导入CoreTelephony后,通过CTCallCenter监听通话状态变更:
import CoreTelephony class ArticleViewController: UIViewController { private let callCenter = CTCallCenter() override func viewDidLoad() { super.viewDidLoad() // 监听通话状态变化,回调可能在子线程 callCenter.callEventHandler = { [weak self] call in DispatchQueue.main.async { // 通知系统更新状态栏外观 self?.setNeedsStatusBarAppearanceUpdate() } } } }
方式二:使用CallKit(iOS 10+推荐,稳定性更好)
CallKit是苹果官方推荐的通话状态监听方案,适配性更强:
import CallKit class ArticleViewController: UIViewController, CXCallObserverDelegate { private let callObserver = CXCallObserver() override func viewDidLoad() { super.viewDidLoad() // 设置代理监听通话变化,直接指定主线程队列 callObserver.setDelegate(self, queue: DispatchQueue.main) } // 通话状态变更时触发回调 func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { setNeedsStatusBarAppearanceUpdate() } }
2. 动态设置状态栏高度
重写UIViewController的preferredStatusBarHeight属性,根据当前通话状态返回对应的高度:
结合CoreTelephony的实现
override var preferredStatusBarHeight: CGFloat { // 检查是否存在已连接的活跃通话 let hasActiveCall = callCenter.currentCalls?.contains(where: { $0.callState == CTCallStateConnected }) ?? false // 通话时返回20pt,否则使用系统默认状态栏高度 return hasActiveCall ? 20 : UIApplication.shared.statusBarFrame.height }
结合CallKit的实现
override var preferredStatusBarHeight: CGFloat { // 检查是否有已连接且未结束的通话 let hasActiveCall = callObserver.calls.contains(where: { $0.hasConnected && !$0.hasEnded }) return hasActiveCall ? 20 : UIApplication.shared.statusBarFrame.height }
如果页面嵌套在导航控制器中,需要子类化UINavigationController并重写状态栏高度方法——因为导航控制器会接管状态栏的外观控制权:
class CustomNavigationController: UINavigationController, CXCallObserverDelegate { private let callObserver = CXCallObserver() override func viewDidLoad() { super.viewDidLoad() callObserver.setDelegate(self, queue: DispatchQueue.main) } func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { setNeedsStatusBarAppearanceUpdate() } override var preferredStatusBarHeight: CGFloat { let hasActiveCall = callObserver.calls.contains(where: { $0.hasConnected && !$0.hasEnded }) return hasActiveCall ? 20 : super.preferredStatusBarHeight } }
4. 关键注意事项
- UI布局适配:确保页面布局使用Auto Layout,将顶部视图的约束绑定到
safeAreaLayoutGuide.topAnchor,而非固定值,这样状态栏高度变化时页面会自动适配。 - 主线程更新:所有UI相关操作必须在主线程执行,所以监听回调里一定要切换到主线程调用
setNeedsStatusBarAppearanceUpdate()。 - 后台监听(可选):如果需要在后台监听通话状态,需在
Info.plist中添加voip后台模式,但一般阅读APP仅前台监听即可满足需求。
内容的提问来源于stack exchange,提问作者Steve Moser
相关产品推荐
相关产品推荐

