BezelKit 是一个 Swift 包,旨在简化在应用中访问设备特定边框尺寸的过程。
了解确切的边框尺寸对于对齐 UI 元素、创建沉浸式体验或在您需要像素级完美的设计布局时至关重要。
通过提供易于使用的 API,BezelKit
使开发者能够更专注于应用的功能,而不是与设备指标作斗争。
- Apple 没有 提供公开 API 来获取设备边框尺寸
- 使用内部 API 可能会危及 App Store 资格
- 静态边框值可能会导致跨设备的 UI 扭曲
BezelKit
为准确的边框指标提供了一个易于使用的解决方案
Apple 目前没有提供公开 API 来获取其设备的边框半径。
尽管存在内部 API,但使用它会危及应用在 App Store 中的资格——对于仅仅是一个 UI 元素而言,这种风险是不值得的。
另一个考虑因素源于不同 Apple 设备之间屏幕边框尺寸的可变性。为静态边框值设置是成问题的,原因如下:
虽然 Apple 提供了 ContainerRelativeShape
内边距,但其功能目前仅限于 Widgets。对于所有其他应用程序,此 API 报告一个正方形矩形,使其不适合我们的需求。
一个美观的解决方案看起来像这样
就支持的设备而言,它涵盖了所有设备的初始版本。请参阅受支持的设备列表。
BezelKit 包使用 Swift Package Manager (SPM) 以实现简单便捷的分发。请按照以下步骤将其添加到您的项目中
在 Xcode 中,点击 File -> Swift Packages -> Add Package Dependency
在搜索栏中,输入 https://github.com/markbattistella/BezelKit
并点击 Next
。
指定您要使用的版本。您可以选择确切的版本、使用最新版本或设置版本范围,然后点击 Next
[!Tip] 最好查看更改日志以了解不同版本之间的差异
最后,选择您想要使用 BezelKit
的目标,然后点击 Finish
。
使用 BezelKit
非常简单,可以帮助您避免与设备指标相关的复杂性。
导入 BezelKit 模块
import BezelKit
访问设备边框尺寸
let currentBezel = CGFloat.deviceBezel
有关高级用法,包括完美缩放 UI 元素和设置回退尺寸,请阅读以下章节。
BezelKit
包不仅提供了一种访问设备特定边框尺寸的简便方法,还可以实现 UI 中圆角的完美缩放。
当您在外层有一个圆角,并且内部 UI 元素也需要一个圆角时,保持完美的纵横比对于和谐的设计至关重要。这确保您的 UI 可以在不同的设备上完美缩放。
以下是如何实现它
let outerBezel = CGFloat.BezelKit
let innerBezel = outerBezel - distance // Perfect ratio
通过遵循这种方法,您可以确保您的 UI 元素相对于设备的边框尺寸完美缩放。
您可以使用 deviceBezel(with:)
函数传入边距大小,它将返回设备边框,但会根据内部比率完美缩放。
该软件包提供了一种指定回退边框尺寸的简便方法。默认情况下,如果 CGFloat.deviceBezel
属性无法确定设备的边框尺寸,则返回 0.0
。
除了指定回退值之外,您还可以选择即使在确定边框尺寸为零时也返回回退值。
对于基于 UIKit 的应用程序,您可以在 AppDelegate
中的 application(_:didFinishLaunchingWithOptions:)
方法中设置回退值。这是您可以为您的应用程序设置回退值的最早时间点。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Sets a fallback value of 10.0 and enables zero-check
CGFloat.setFallbackDeviceBezel(10.0, ifZero: true)
return true
}
}
对于 SwiftUI 应用程序,您可以在主内容视图的 init()
函数中设置此值。
import SwiftUI
@main
struct YourApp: App {
init() {
// Sets a fallback value of 10.0 and enables zero-check
CGFloat.setFallbackDeviceBezel(10.0, ifZero: true)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
重要提示
之前有人指出使用 .onAppear
修饰符来设置回退值。这导致了一个启动时不更新的错误
您只需要调用 setFallbackDeviceBezel(_:ifZero:)
一次。尽早执行此操作可确保回退设置应用于您的整个应用程序。
如果您已设置回退值,则当 CGFloat.deviceBezel
无法确定当前设备的边框尺寸时,或者在可选情况下,当边框尺寸为零时,它将返回此回退值。
// With fallback set to 10.0 and zero check enabled
let currentBezel = CGFloat.deviceBezel
print("Current device bezel: \(currentBezel)")
// Output will be 10.0 if the device is not in the JSON data or the bezel is zero
如果未设置回退值,则当设备特定的边框尺寸不可用时,CGFloat.BezelKit
默认为 0.0
。
// With no fallback set and zero check disabled
let currentBezel = CGFloat.deviceBezel
print("Current device bezel: \(currentBezel)")
// Output will be 0.0 if the device is not in the JSON data
BezelKit 提供可选的错误处理来管理意外问题,例如缺少设备边框数据或数据解析问题。
通过使用 BezelKit 的错误回调,开发者可以收到这些小故障的警报,从而使他们能够根据自己的意愿处理这些问题,无论是记录以进行调试还是用户通知。
这确保了更流畅和更具弹性的应用程序体验。
import SwiftUI
import BezelKit
struct ContentView: View {
@State private var showErrorAlert: Bool = false
@State private var errorMessage: String = ""
var body: some View {
RoundedRectangle(cornerRadius: .deviceBezel)
.stroke(Color.green, lineWidth: 20)
.ignoresSafeArea()
.alert(isPresented: $showErrorAlert) {
Alert(title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Got it!")))
}
.onAppear {
DeviceBezel.errorCallback = { error in
errorMessage = error.localizedDescription
showErrorAlert = true
}
}
}
}
import UIKit
import BezelKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
DeviceBezel.errorCallback = { [weak self] error in
let alert = UIAlertController(title: "Error",
message: error.localizedDescription,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
let bezelValue = CGFloat.deviceBezel
// Use bezelValue for your views
}
}
这是一个比较,比较了对所有设备使用静态、单一值,以及与 BezelKit
相比,渲染时的外观,BezelKit
将适应每个设备。
这是对所有设备使用静态、单一值时的代码
import SwiftUI
struct ContentView: View {
var body: some View {
RoundedRectangle(cornerRadius: 60)
.stroke(.green, lineWidth: 20)
.ignoresSafeArea()
}
}
在固定值配置中,没有曲面屏幕的设备看起来很奇怪,虽然此 cornerRadius
是为 iPhone 14 Pro Max 设计的,但在 iPhone 14 上看起来很笨重,在 iPhone 14 Pro 上看起来还不错。
这是使用 BezelKit
时的代码
import SwiftUI
import BezelKit
struct ContentView: View {
var body: some View {
RoundedRectangle(cornerRadius: .deviceBezel)
.stroke(.green, lineWidth: 20)
.ignoresSafeArea()
}
}
正如您所见,在没有设置 setFallbackBezelKit
的情况下,iPhone SE(第三代)的值设置为 0.0
,并且没有曲线。但是,所有其他曲面设备都具有一致的外观。
BezelKit
目前不 支持显示缩放的影响。当生成器运行时,它会在设备的“标准”缩放级别上执行所有提取。
如果在“缩放”级别上运行,则边框半径将有所不同。但是,由于物理设备无法根据缩放级别进行更改,因此使用“标准”是正确的 CGFloat 数字。
在 xcrun simctl
中也没有自动缩放级别的方法,因此它必须是手动包含,并且在此时(除非通过 Issues 提出),使用缩放值 _displayRoundedCorner
并没有真正的益处。
有关生成新边框,请参阅 BezelKit - Generator
存储库。
运行脚本时,最好从 BezelKit
目录执行,因为其中一个脚本行是将编译后的 JSON 复制到 /Resources
目录。从生成器仓库的角度来看,这将不存在。
非常欢迎贡献。如果您发现错误或有增强功能的想法,请打开一个 issue 或提供一个 pull request。
在做出贡献时,请遵循当前代码库中存在的代码风格。
注意
任何 pull request 都需要具有以下格式的标题,否则将被拒绝。
YYYY-mm-dd - {title}
eg. 2023-08-24 - Updated README file
我喜欢跟踪从记录请求到完成的日期,以便进行排序和数据提取。它可以帮助我了解事情已经待处理了多久,并提供 OCD 结构。
BezelKit 包在 MIT 许可下发布。有关更多信息,请参阅 LICENCE。