Files
orca/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellKeyboardWarmup.swift
T

61 lines
2.0 KiB
Swift

import UIKit
@MainActor
internal enum MobileWebShellKeyboardWarmup {
enum Preparation {
case ready(after: TimeInterval)
case waitingForTextInput
case failed
}
private static var readyAt: TimeInterval?
static func prepare(in window: UIWindow) -> Preparation {
if let readyAt {
return .ready(after: max(0, readyAt - ProcessInfo.processInfo.systemUptime))
}
for scene in UIApplication.shared.connectedScenes {
guard let scene = scene as? UIWindowScene else { continue }
for sceneWindow in scene.windows {
if let responder = firstResponder(in: sceneWindow), hasActiveTextInput(responder) {
return .waitingForTextInput
}
}
}
let previousResponder = firstResponder(in: window)
let field = UITextField(frame: CGRect(x: -100, y: -100, width: 40, height: 30))
field.isAccessibilityElement = false
field.accessibilityElementsHidden = true
window.addSubview(field)
var focused = false
// iOS split-keyplane initialization crashes on first WebKit focus: developer.apple.com/forums/thread/845347.
UIView.performWithoutAnimation {
focused = field.becomeFirstResponder()
if focused {
field.resignFirstResponder()
previousResponder?.becomeFirstResponder()
}
}
field.removeFromSuperview()
guard focused else { return .failed }
readyAt = ProcessInfo.processInfo.systemUptime + 0.3
return .ready(after: 0.3)
}
private static func hasActiveTextInput(_ responder: UIView) -> Bool {
if let field = responder as? UITextField {
return field.isEditing
}
guard let input = responder as? UITextInput else { return false }
return input.selectedTextRange != nil || input.markedTextRange != nil
}
private static func firstResponder(in view: UIView) -> UIView? {
if view.isFirstResponder { return view }
for child in view.subviews {
if let responder = firstResponder(in: child) { return responder }
}
return nil
}
}