模拟器状态栏魔法

修改 iOS 模拟器,使其拥有完美的状态栏,然后运行你的应用,每次都能截取完美的屏幕截图。所做的修改旨在匹配你在 Apple 网站上看到的图像,具体如下:

难道我不能只用 xcrun simctl status_bar 吗? 🚀

从 Xcode 11 开始,simctl 命令行工具包含一个 status_bar 选项,允许你覆盖模拟器中状态栏的外观。希望这最终能取代对 SimulatorStatusMagic 的需求,但目前它仍然存在一些漏洞,使这个项目仍然具有相关性。特别是,simctl status_bar 目前无法在状态栏中添加本地化的日期和时间字符串。

用法

1) 注入到 Springboard(iOS 17+ 上必需)

太长不看版: 运行 build_and_inject.sh booted 将把默认状态栏应用到正在运行的模拟器。用模拟器 UDID 替换 "booted" 以定位特定模拟器。

从 iOS 17 开始,SimulatorStatusMagic 使用的 API 无法被 Springboard 以外的进程访问。因此,在 iOS 17+ 中,我们需要将 SimulatorStatusMagic 注入到 Springboard 进程本身,我们通过将其构建为动态库,然后更新 Springboard 的 launchd 配置以加载我们的动态库来实现。

运行 build_and_inject.sh 将为你完成所有这些操作。如果你想更改状态栏中使用的任何值,你需要更新 DynamicLibrary/main.m。

2) 在屏幕截图自动化 UI 测试中使用(iOS 16 及更低版本)

import SimulatorStatusMagic

final class YourUITests: XCTestCase {

    override func setUpWithError() throws {
      SDStatusBarManager.sharedInstance().enableOverrides()
      // ... your other setup code
    }

    override func tearDownWithError() throws {
      SDStatusBarManager.sharedInstance().disableOverrides()
      // ... your other teardown code
    }

由于该包仅添加到你的 UI 测试中,因此对你的应用目标没有影响。

Cocoapods/Carthage 建议在你的 debug 配置中包含 SDStatusBarManager,这样代码就永远不会包含在 release 版本中。当你想应用完美的狀態欄時,调用 [[SDStatusBarManager sharedInstance] enableOverrides]。要恢复标准状态栏,调用 [[SDStatusBarManager sharedInstance] disableOverrides]

3) 使用演示应用启用/禁用覆盖(iOS 16 及更低版本)

我如何移除自定义设置?

再次运行该应用,然后点击 "Restore Default Status Bar"。使用常规菜单选项重置 iOS 模拟器也有效。

如何自动化示例应用?

如果你更喜欢自动化应用本身来自动启用或禁用覆盖,可以使用环境变量来完成。

运行命令:

SIMULATOR_STATUS_MAGIC_OVERRIDES = enable

或者

SIMULATOR_STATUS_MAGIC_OVERRIDES = disable

覆盖将在启动时自动启用或禁用。

这在设备上有效吗?

否。状态栏服务器在设备上被阻止。但是,macOS 提供了在使用 QuickTime 录制你的设备屏幕时包含完美状态栏的功能(阅读更多)。

这是如何工作的?

最好的方法是查看 源代码,它应该可以帮助你了解它的工作原理 :)

更新以支持新的 iOS 版本

通常有一个通用模式来更新此项目以支持新的 iOS 版本,尽管如果 Apple 将来更改某些内容,这可能会发生变化。

准备新文件。

  1. 复制先前版本的 SDStatusBarOverriderPostXX_Y.{h,m} 文件,并将其更新为新版本。
  2. 更新 SDStatusBarManager.m 以在检测到新的操作系统版本时引用新的覆盖器。

在运行时标头中找到更新的结构体。

  1. 下载最新版本的 dsdump 工具。
  2. 针对 UIKitCore 框架二进制文件运行 dsdump,以生成私有运行时标头。
./dsdump --objc -a x86_64 --verbose=5 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore --defined > ~/Desktop/UIKitCore.txt
  1. 在输出中查找 UIStatusBarServerListener

更新新的覆盖器中的结构体。

有两个结构体,StatusBarRawData 和 StatusBarOverrideData,需要更新。每个结构体对应于 UIStatusBarServerListener 的运行时标头输出中的一行。如果你研究一下,应该很容易找出映射关系。

  1. 更新 StatusBarRawData 和 StatusBarOverrideData 以匹配运行时标头中结构体的任何更改。

检查它是否有效

应该就是这样了!

  1. 运行示例应用,并验证状态栏是否已正确更新。
  2. 如果已将任何新内容添加到需要调整的状态栏,请对你的新 SDStatusBarOverrider 进行其他更改。

贡献

我们欢迎贡献!发现错误了吗?如果你通过附加 pull request 报告它,你将获得一颗金星 :)

但是,该项目的范围是故意限制的。我们不打算向其中添加选项以允许对状态栏进行最终自定义。它的目的只是很好地完成一项工作,即将状态栏更改为与 Apple 的营销材料相匹配。