- 新增 RDEPUBSafeArea:参考 GKNavigationBarSwift 的 keyWindow 三级查找与临时 window 兜底 - 移除 reflowableContentInsets 中 top/bottom 40 的固定默认值,改按设备真实安全区生成 - 各取安全区处的 ?? .zero 兜底改为回退到 key window 值 - DEBUG 构建下为文本可读区域绘制红色边框,方便查看排版范围 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.6 KiB
Swift
68 lines
2.6 KiB
Swift
import UIKit
|
|
|
|
/// Device-level safe area lookup modeled after GKNavigationBarSwift.
|
|
/// Reads the real safe area from the key window so correct values are
|
|
/// available even before a view has been laid out in the hierarchy.
|
|
/// Must be called on the main thread.
|
|
public enum RDEPUBSafeArea {
|
|
|
|
/// Minimum text margins applied when the device safe area on an edge is
|
|
/// smaller (e.g. no notch / no home indicator). These are aesthetic
|
|
/// paddings, not approximations of the safe area itself.
|
|
public static let minimumVerticalTextMargin: CGFloat = 20
|
|
|
|
public static let minimumHorizontalTextMargin: CGFloat = 16
|
|
|
|
public static func keyWindow() -> UIWindow? {
|
|
let scenes = UIApplication.shared.connectedScenes
|
|
.compactMap { $0 as? UIWindowScene }
|
|
if let window = scenes
|
|
.filter({ $0.activationState == .foregroundActive })
|
|
.flatMap({ $0.windows })
|
|
.first(where: { $0.isKeyWindow }) {
|
|
return window
|
|
}
|
|
if let window = scenes
|
|
.flatMap({ $0.windows })
|
|
.first(where: { $0.isKeyWindow }) {
|
|
return window
|
|
}
|
|
return UIApplication.shared.delegate?.window ?? nil
|
|
}
|
|
|
|
public static func insets() -> UIEdgeInsets {
|
|
if let window = keyWindow() {
|
|
return window.safeAreaInsets
|
|
}
|
|
// No key window yet (early launch): create a detached window to read
|
|
// the device safe area, same fallback as GKNavigationBarSwift.
|
|
let window = UIWindow(frame: UIScreen.main.bounds)
|
|
if window.safeAreaInsets.bottom <= 0 {
|
|
window.rootViewController = UIViewController()
|
|
}
|
|
return window.safeAreaInsets
|
|
}
|
|
|
|
/// Prefers insets measured from a view already installed in the hierarchy;
|
|
/// falls back to the key-window insets when the view is not laid out yet
|
|
/// and reports .zero.
|
|
public static func resolve(_ measuredInsets: UIEdgeInsets?) -> UIEdgeInsets {
|
|
if let measuredInsets, measuredInsets != .zero {
|
|
return measuredInsets
|
|
}
|
|
return insets()
|
|
}
|
|
|
|
/// Default reflowable content insets derived from the live device safe
|
|
/// area instead of hard-coded heights.
|
|
public static func defaultReflowableContentInsets() -> UIEdgeInsets {
|
|
let safe = insets()
|
|
return UIEdgeInsets(
|
|
top: max(safe.top, minimumVerticalTextMargin),
|
|
left: max(safe.left, minimumHorizontalTextMargin),
|
|
bottom: max(safe.bottom, minimumVerticalTextMargin),
|
|
right: max(safe.right, minimumHorizontalTextMargin)
|
|
)
|
|
}
|
|
}
|