Files
orca/mobile/modules/orca-window-controls-inset/ios/OrcaWindowControlsInsetSubscriber.swift
a803cbead8 feat(mobile): iPhone Duo key rail with D-pad, split terminal panes and half-fold layout (#35177)
Carries over #33221 and #33210. Replaces our #34484 Duo support with the community design, on top of main's rebuilt terminal key bar (#36166).

Fixes #33200

Co-authored-by: Takeshi Matsumoto <zytakeshi520@gmail.com>
Co-authored-by: Lars Keggenhoff <lars@kghf.io>
2026-10-30 16:35:12 +00:00

68 lines
2.5 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import ExpoModulesCore
import UIKit
// iPadOS 26 window controls overlap the plain safe area; fold the corner-adapted inset into the root so all headers clear them.
public class OrcaWindowControlsInsetSubscriber: ExpoAppDelegateSubscriber {
public func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
if let window = application.delegate?.window ?? nil {
WindowControlsInsetProbe.install(in: window)
}
// Under the UIScene lifecycle the scene delegate creates the window after launch.
NotificationCenter.default.addObserver(
forName: UIWindow.didBecomeKeyNotification, object: nil, queue: .main
) { notification in
if let window = notification.object as? UIWindow,
window === UIApplication.shared.delegate?.window ?? nil
{
WindowControlsInsetProbe.install(in: window)
}
}
return true
}
}
/// Measure window resize/safe-area changes outside the root’s extra inset to avoid feedback.
private final class WindowControlsInsetProbe: UIView {
private var rootObservation: NSKeyValueObservation?
static func install(in window: UIWindow) {
guard !window.subviews.contains(where: { $0 is WindowControlsInsetProbe }) else { return }
let probe = WindowControlsInsetProbe(frame: window.bounds)
probe.autoresizingMask = [.flexibleWidth, .flexibleHeight]
probe.isUserInteractionEnabled = false
probe.isAccessibilityElement = false
window.insertSubview(probe, at: 0)
// The dev launcher and reloads swap the root controller; the new one needs the inset too.
probe.rootObservation = window.observe(\.rootViewController, options: [.new]) { [weak probe] _, _ in
probe?.setNeedsLayout()
}
}
override func safeAreaInsetsDidChange() {
super.safeAreaInsetsDidChange()
setNeedsLayout()
}
override func layoutSubviews() {
super.layoutSubviews()
guard let window, let root = window.rootViewController else { return }
let extraTop = Self.windowControlsTopInset(of: window)
if root.additionalSafeAreaInsets.top != extraTop {
root.additionalSafeAreaInsets.top = extraTop
}
}
private static func windowControlsTopInset(of window: UIWindow) -> CGFloat {
#if compiler(>=6.2)
if #available(iOS 26.0, *) {
let adapted = window.edgeInsets(for: .safeArea(cornerAdaptation: .vertical)).top
return max(0, adapted - window.safeAreaInsets.top)
}
#endif
return 0
}
}