Files
orca/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellKeyboardAccessory.swift
T
Jinwoo Hong ba858ee446 feat(mobile): the keyboard covers the page like a native screen, and the shell says its height (#23110)
The shell no longer shortens the WebView for the keyboard; it publishes the keyboard height like the safe-area insets, so native's keyboard lift, refit hold and dismiss key run on the page unchanged. Keyboard and inset arithmetic read the shell's OS through a host-os seam. One page-version floor (manifest pageVersion, shell floor 1) replaces per-feature accept negotiation; a page below the floor gets the existing update wall, a desktop with no bundle keeps native screens. iOS shell drops the form accessory bar and its own keyboard observers. Native session screens untouched.
2026-09-28 04:07:35 -04:00

51 lines
2.1 KiB
Swift

import ObjectiveC
import UIKit
import WebKit
/// Removes WKWebView's form accessory bar (up, down, done). Native screens have none, and the
/// keyboard event the shell publishes counts it, so the page lifted by the bar as well. WebKit
/// exposes no switch, so the content view is moved onto a subclass whose `inputAccessoryView` is
/// nil, the same technique react-native-webview's `hideKeyboardAccessoryView` uses.
func hideKeyboardAccessoryBar(of webView: WKWebView) {
guard
let content = webView.scrollView.subviews.first(where: {
String(describing: type(of: $0)).hasPrefix("WKContent")
})
else {
return
}
let base: AnyClass = type(of: content)
let name = "\(NSStringFromClass(base))_OrcaNoAccessory"
let subclass: AnyClass
if let existing = NSClassFromString(name) {
subclass = existing
} else {
guard let created = objc_allocateClassPair(base, name, 0) else {
return
}
let noAccessory: @convention(block) (AnyObject) -> UIView? = { _ in nil }
let selector = #selector(getter: UIResponder.inputAccessoryView)
let typeEncoding = method_getTypeEncoding(class_getInstanceMethod(UIResponder.self, selector)!)
class_addMethod(created, selector, imp_implementationWithBlock(noAccessory), typeEncoding)
objc_registerClassPair(created)
subclass = created
}
object_setClass(content, subclass)
}
/// Stops WKWebView reacting to the keyboard itself. It listens for the keyboard's frame and scrolls
/// the document to reveal the focused field, on top of the lift the page already applies from the
/// height the shell sends, so the page moved twice. The shell's own listeners are unaffected.
func ignoreKeyboardNotifications(in webView: WKWebView) {
for name in [
UIResponder.keyboardWillShowNotification,
UIResponder.keyboardDidShowNotification,
UIResponder.keyboardWillHideNotification,
UIResponder.keyboardDidHideNotification,
UIResponder.keyboardWillChangeFrameNotification,
UIResponder.keyboardDidChangeFrameNotification
] {
NotificationCenter.default.removeObserver(webView, name: name, object: nil)
}
}