Files
2026-07-13 13:11:28 +08:00

119 lines
4.2 KiB
Plaintext
Vendored
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 <UIKit/UIKit.h>
#import <objc/runtime.h>
#import <dispatch/dispatch.h>
// 参考方案:通过覆盖 WKContentView 的 inputAccessoryView 返回 nil
// 并清空 inputAssistantItem 的按钮组,以隐藏键盘顶部工具栏(Done/箭头)。
@interface _NoInputAccessoryView : NSObject
@end
@implementation _NoInputAccessoryView
- (id)inputAccessoryView {
return nil;
}
@end
static BOOL HulaPatchTargetView(UIView *targetView) {
if (!targetView) { return NO; }
// 清空输入助手按钮组,防止出现默认工具栏按钮
if ([targetView respondsToSelector:@selector(inputAssistantItem)]) {
UITextInputAssistantItem *inputAssistantItem = [targetView inputAssistantItem];
inputAssistantItem.leadingBarButtonGroups = @[];
inputAssistantItem.trailingBarButtonGroups = @[];
}
// 为目标视图类创建一个子类,覆盖 inputAccessoryView 返回 nil
NSString *noInputAccessoryViewClassName = [NSString stringWithFormat:@"%@_NoInputAccessoryView", targetView.class];
Class newClass = NSClassFromString(noInputAccessoryViewClassName);
if (newClass == nil) {
newClass = objc_allocateClassPair(targetView.class, [noInputAccessoryViewClassName cStringUsingEncoding:NSASCIIStringEncoding], 0);
if(!newClass) {
return NO;
}
Method method = class_getInstanceMethod([_NoInputAccessoryView class], @selector(inputAccessoryView));
class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));
objc_registerClassPair(newClass);
}
// 替换当前对象的类为新类
object_setClass(targetView, newClass);
return YES;
}
static BOOL HulaIsWKContentView(UIView *view) {
if (!view) { return NO; }
// WKWebView 内容视图在不同 iOS 版本中名称可能为 WKContentView/WKScrollContentView 等
NSString *className = NSStringFromClass(view.class);
if ([className hasPrefix:@"WKContent"] || [className hasPrefix:@"WKScroll"] || [className isEqualToString:@"WKContentView"]) {
return YES;
}
return NO;
}
static BOOL HulaPatchViewHierarchy(UIView *view) {
if (!view) { return NO; }
BOOL patched = NO;
if (HulaIsWKContentView(view)) {
patched = HulaPatchTargetView(view) || patched;
}
// 继续递归子视图
for (UIView *sub in view.subviews) {
patched = HulaPatchViewHierarchy(sub) || patched;
}
return patched;
}
static BOOL HulaPatchAllWindows(void) {
BOOL patched = NO;
if (@available(iOS 13.0, *)) {
NSSet<UIScene *> *connectedScenes = UIApplication.sharedApplication.connectedScenes;
for (UIScene *scene in connectedScenes) {
if (![scene isKindOfClass:[UIWindowScene class]]) { continue; }
UIWindowScene *windowScene = (UIWindowScene *)scene;
for (UIWindow *window in windowScene.windows) {
if (window.isHidden) { continue; }
patched = HulaPatchViewHierarchy(window) || patched;
}
}
} else {
for (UIWindow *window in UIApplication.sharedApplication.windows) {
if (window.isHidden) { continue; }
patched = HulaPatchViewHierarchy(window) || patched;
}
}
return patched;
}
static void HulaTryPatchRepeatedly(int attempt, int maxAttempts, NSTimeInterval interval) {
// 在主线程执行,防止与 UIKit 交互的线程问题
dispatch_async(dispatch_get_main_queue(), ^{
BOOL ok = HulaPatchAllWindows();
if (ok || attempt >= maxAttempts) {
// 成功或达到最大重试次数,停止
return;
}
// 继续下一次尝试
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
HulaTryPatchRepeatedly(attempt + 1, maxAttempts, interval);
});
});
}
#ifdef __cplusplus
extern "C" {
#endif
// 对外暴露的入口:启动后反复尝试为 WKWebView 内容视图打补丁
void hula_disable_keyboard_accessory(void) {
// 最多尝试 100 次,每次间隔 0.1 秒(总计约 10 秒)。
// 一般在 WebView 创建后很快会被命中。
HulaTryPatchRepeatedly(0, 100, 0.1);
}
#ifdef __cplusplus
}
#endif