72 lines
2.8 KiB
Swift
72 lines
2.8 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
|
|
|
|
/// Compact top/bottom padding used by a landscape two-page spread. This
|
|
/// applies only beyond the actual safe area, which is always preserved.
|
|
public static let landscapeVerticalTextMargin: CGFloat = 12
|
|
|
|
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)
|
|
)
|
|
}
|
|
}
|