You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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
}
3. 适配导航控制器(如果你的APP使用UINavigationController)

如果页面嵌套在导航控制器中,需要子类化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:28:02