inputAccessoryBar for soft keyboard

This commit is contained in:
True Zhuanjia committed 2026-04-24 22:59:37 +08:00
1 parent 7e21292a62
commit e78a351c28
15 files changed
+1640 -46

No files matched your search

@@ -18,6 +18,10 @@ import UIKit
@available(iOS 13.0, *)
@objcMembers
public final class WidgetPickerViewController: UIViewController {
private static var overlayWindow: UIWindow?
private static weak var originalAppWindow: UIWindow?
private static weak var originalHostViewController: UIViewController?
public weak var delegate: WidgetPickerViewControllerDelegate?
public var isEditMode: Bool = false
public var initialCmdString: String?
@@ -27,8 +31,10 @@ public final class WidgetPickerViewController: UIViewController {
public var initialTabIdentifier: String?
public var keyboardPickerMode: VirtualKeyboardMode = .picker
var shortcutPickerNeedAlias: Bool = false
var shortcutPickerNeedButtonMode: Bool = false
public var shortcutPickerTipText: String?
@objc public var shortcutIdentifier: String?
public var usesOverlayPresentation: Bool = false
private let presentationState = WidgetPickerPresentationState()
private var hostingViewController: UIHostingController<WidgetPickerView>?
@@ -36,7 +42,7 @@ public final class WidgetPickerViewController: UIViewController {
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.backgroundColor = usesOverlayPresentation ? .clear : .systemBackground
let rootView = WidgetPickerView(
isEditMode: isEditMode,
@@ -47,16 +53,17 @@ public final class WidgetPickerViewController: UIViewController {
preferredInitialTab: resolvedInitialTab(),
keyboardPickerMode: keyboardPickerMode,
shortcutPickerNeedAlias: shortcutPickerNeedAlias,
shortcutPickerNeedButtonMode: shortcutPickerNeedButtonMode,
shortcutPickerTipText: shortcutPickerTipText,
shortcutIdentififier: shortcutIdentifier,
presentationState: presentationState,
onWidgetCreated: { [weak self] payload in
guard let self else { return }
self.delegate?.widgetPickerViewController(self, didCreateWidget: payload as NSDictionary)
self.dismiss(animated: true)
self.closeSelf(animated: true)
},
onCloseRequested: { [weak self] in
self?.dismiss(animated: true)
self?.closeSelf(animated: true)
}
)
let hostingViewController = UIHostingController(rootView: rootView)
@@ -96,4 +103,95 @@ public final class WidgetPickerViewController: UIViewController {
guard let initialTabIdentifier else { return nil }
return WidgetPickerTab(identifier: initialTabIdentifier)
}
public func presentAsOverlay(in parentViewController: UIViewController, animated: Bool = true) {
guard parent == nil else { return }
guard let originalAppWindow = parentViewController.view.window,
let windowScene = originalAppWindow.windowScene else { return }
usesOverlayPresentation = true
Self.originalAppWindow = originalAppWindow
Self.originalHostViewController = parentViewController
let overlayHostViewController = UIViewController()
overlayHostViewController.view.backgroundColor = .clear
let overlayWindow = UIWindow(windowScene: windowScene)
overlayWindow.frame = windowScene.screen.bounds
overlayWindow.windowLevel = .alert + 1
overlayWindow.backgroundColor = .clear
overlayWindow.rootViewController = overlayHostViewController
overlayWindow.isHidden = false
Self.overlayWindow = overlayWindow
loadViewIfNeeded()
view.translatesAutoresizingMaskIntoConstraints = false
view.alpha = animated ? 0 : 1
overlayHostViewController.addChild(self)
overlayHostViewController.view.addSubview(view)
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: overlayHostViewController.view.topAnchor),
view.bottomAnchor.constraint(equalTo: overlayHostViewController.view.bottomAnchor),
view.leadingAnchor.constraint(equalTo: overlayHostViewController.view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: overlayHostViewController.view.trailingAnchor),
])
didMove(toParent: overlayHostViewController)
guard animated else { return }
UIView.animate(withDuration: 0.18) {
self.view.alpha = 1
}
}
private func closeSelf(animated: Bool) {
if presentingViewController != nil {
dismiss(animated: animated)
return
}
guard parent != nil else {
Self.destroyOverlayWindowAndRestoreAppWindow()
return
}
let removeOverlay = {
self.willMove(toParent: nil)
self.view.removeFromSuperview()
self.removeFromParent()
Self.destroyOverlayWindowAndRestoreAppWindow()
}
guard animated else {
removeOverlay()
return
}
UIView.animate(
withDuration: 0.18,
animations: {
self.view.alpha = 0
},
completion: { _ in
removeOverlay()
}
)
}
private static func destroyOverlayWindowAndRestoreAppWindow() {
overlayWindow?.isHidden = true
overlayWindow?.rootViewController = nil
overlayWindow = nil
originalAppWindow?.makeKeyAndVisible()
refreshSystemGestureDeferral()
originalAppWindow = nil
originalHostViewController = nil
}
private static func refreshSystemGestureDeferral() {
guard let hostViewController = originalHostViewController else { return }
if #available(iOS 11.0, *) {
hostViewController.setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
hostViewController.setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
}