Минимизированный call-бар: UIKit additionalSafeAreaInsets для сдвига навбара, Telegram-style градиент и UI-рефакторинг

This commit is contained in:
2026-03-30 04:24:48 +05:00
parent 2b25c87a6a
commit f24f7ee555
20 changed files with 1444 additions and 439 deletions

View File

@@ -0,0 +1,69 @@
import SwiftUI
// MARK: - Call Bar Safe Area Bridge
/// UIKit bridge that pushes NavigationStack's nav bar down by setting
/// `additionalSafeAreaInsets.top` on every UINavigationController in the window.
///
/// `UIViewController.navigationController` walks UP the parent chain but the
/// bridge VC is a SIBLING of NavigationStack's UINavigationController, not a child.
/// So we walk DOWN from the window's root VC to find all navigation controllers.
struct CallBarSafeAreaBridge: UIViewRepresentable {
let topInset: CGFloat
func makeUIView(context: Context) -> CallBarInsetView {
let view = CallBarInsetView()
view.isHidden = true
view.isUserInteractionEnabled = false
return view
}
func updateUIView(_ view: CallBarInsetView, context: Context) {
view.topInset = topInset
view.applyInset()
}
}
/// Invisible UIView that finds all UINavigationControllers in the window
/// and sets `additionalSafeAreaInsets.top` on each.
final class CallBarInsetView: UIView {
var topInset: CGFloat = 0
private var lastAppliedInset: CGFloat = -1
override func didMoveToWindow() {
super.didMoveToWindow()
lastAppliedInset = -1 // Force re-apply
applyInset()
}
func applyInset() {
guard topInset != lastAppliedInset else { return }
guard let window, let rootVC = window.rootViewController else { return }
Self.apply(inset: topInset, in: rootVC)
lastAppliedInset = topInset
}
/// Recursively walk child view controllers (NOT presentedViewController)
/// and set additionalSafeAreaInsets.top on every UINavigationController.
private static func apply(inset: CGFloat, in vc: UIViewController) {
if let nav = vc as? UINavigationController,
nav.additionalSafeAreaInsets.top != inset {
nav.additionalSafeAreaInsets.top = inset
}
for child in vc.children {
apply(inset: inset, in: child)
}
}
}
extension View {
/// Push NavigationStack's nav bar down by `inset` points.
/// Uses UIKit `additionalSafeAreaInsets` the only reliable mechanism.
func callBarSafeAreaInset(_ inset: CGFloat) -> some View {
background(
CallBarSafeAreaBridge(topInset: inset)
.frame(width: 0, height: 0)
.allowsHitTesting(false)
)
}
}