diff --git a/CustomKeyboard/KeyboardViewController.m b/CustomKeyboard/KeyboardViewController.m index 347b227..06491fe 100644 --- a/CustomKeyboard/KeyboardViewController.m +++ b/CustomKeyboard/KeyboardViewController.m @@ -31,6 +31,8 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20; - (void)viewDidLoad { [super viewDidLoad]; [self setupUI]; + // 指定 HUD 的承载视图(扩展里无法取到 App 的 KeyWindow) + [KBHUD setContainerView:self.view]; } diff --git a/CustomKeyboard/PrefixHeader.pch b/CustomKeyboard/PrefixHeader.pch index f0c1ed9..53bc43b 100644 --- a/CustomKeyboard/PrefixHeader.pch +++ b/CustomKeyboard/PrefixHeader.pch @@ -18,6 +18,7 @@ // 公共配置 #import "KBConfig.h" #import "Masonry.h" +#import "KBHUD.h" // 复用 App 内的 HUD 封装 // 通用链接(Universal Links)统一配置 // 配置好 AASA 与 Associated Domains 后,只需修改这里即可切换域名/path。 diff --git a/CustomKeyboard/View/KBFunctionView.m b/CustomKeyboard/View/KBFunctionView.m index 9df1ef0..2d8366d 100644 --- a/CustomKeyboard/View/KBFunctionView.m +++ b/CustomKeyboard/View/KBFunctionView.m @@ -164,6 +164,10 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId"; // 用户点击功能标签:优先 UL 拉起主App,失败再 Scheme;两次都失败则提示开启完全访问 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + + [KBHUD showInfo:@"处理中…"]; +// return; + UIInputViewController *ivc = [self findInputViewController]; if (!ivc) return; @@ -181,6 +185,7 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId"; [ivc.extensionContext openURL:scheme completionHandler:^(BOOL ok2) { if (!ok2) { // 两条路都失败:大概率未开完全访问或宿主拦截。给出指引层。 +// [KBHUD showWithStatus:@"点击测试"]; dispatch_async(dispatch_get_main_queue(), ^{ [KBFullAccessGuideView showInView:self]; }); } }]; diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 85d4a83..de04151 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; }; + 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97082EB31B14007BD342 /* KBHUD.m */; }; 04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAAE2EAF86530089C901 /* Assets.xcassets */; }; 04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB12EAF86530089C901 /* LaunchScreen.storyboard */; }; 04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB42EAF86530089C901 /* Main.storyboard */; }; @@ -147,11 +148,11 @@ 04FC970B2EB334F8007BD342 /* UIImageView+KBWebImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+KBWebImage.m"; sourceTree = ""; }; 04FC970C2EB334F8007BD342 /* KBWebImageManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBWebImageManager.h; sourceTree = ""; }; 04FC970D2EB334F8007BD342 /* KBWebImageManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBWebImageManager.m; sourceTree = ""; }; + 04FC98012EB36AAB007BD342 /* KBConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBConfig.h; sourceTree = ""; }; 2C1092FB2B452F95B15D4263 /* Pods_CustomKeyboard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CustomKeyboard.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51FE7C4C42C2255B3C1C4128 /* Pods-keyBoard.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-keyBoard.release.xcconfig"; path = "Target Support Files/Pods-keyBoard/Pods-keyBoard.release.xcconfig"; sourceTree = ""; }; 727EC7532EAF848B00B36487 /* keyBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = keyBoard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 967065BB5230E43F293B3AF9 /* Pods_keyBoard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_keyBoard.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 04FC98012EB36AAB007BD342 /* KBConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBConfig.h; sourceTree = ""; }; A1B2C3D22EB0A0A100000001 /* KBFunctionTagCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFunctionTagCell.h; sourceTree = ""; }; A1B2C3D32EB0A0A100000001 /* KBFunctionTagCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFunctionTagCell.m; sourceTree = ""; }; A1B2C3E02EB0C0A100000001 /* KBNetworkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBNetworkManager.h; sourceTree = ""; }; @@ -183,15 +184,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 04FC98002EB36AAB007BD342 /* Shared */ = { - isa = PBXGroup; - children = ( - 04FC98012EB36AAB007BD342 /* KBConfig.h */, - ); - path = Shared; - sourceTree = ""; - }; - 04C6EAB92EAF86530089C901 /* keyBoard */ = { isa = PBXGroup; children = ( @@ -244,9 +236,9 @@ 04FC95742EB095DE007BD342 /* KBFunctionPasteView.h */, 04FC95752EB095DE007BD342 /* KBFunctionPasteView.m */, A1B2C3D22EB0A0A100000001 /* KBFunctionTagCell.h */, - A1B2C3D32EB0A0A100000001 /* KBFunctionTagCell.m */, - A1B2C3F12EB35A9900000001 /* KBFullAccessGuideView.h */, - A1B2C3F22EB35A9900000001 /* KBFullAccessGuideView.m */, + A1B2C3D32EB0A0A100000001 /* KBFunctionTagCell.m */, + A1B2C3F12EB35A9900000001 /* KBFullAccessGuideView.h */, + A1B2C3F22EB35A9900000001 /* KBFullAccessGuideView.m */, 04FC95B02EB0B2CC007BD342 /* KBSettingView.h */, 04FC95B12EB0B2CC007BD342 /* KBSettingView.m */, ); @@ -530,6 +522,14 @@ path = Manager; sourceTree = ""; }; + 04FC98002EB36AAB007BD342 /* Shared */ = { + isa = PBXGroup; + children = ( + 04FC98012EB36AAB007BD342 /* KBConfig.h */, + ); + path = Shared; + sourceTree = ""; + }; 2C53A0856097DCFBE7B55649 /* Pods */ = { isa = PBXGroup; children = ( @@ -753,6 +753,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */, 04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */, 04FC95792EB09BC8007BD342 /* KBKeyBoardMainView.m in Sources */, 04FC95732EB09570007BD342 /* KBFunctionBarView.m in Sources */, @@ -764,8 +765,8 @@ 04FC95B22EB0B2CC007BD342 /* KBSettingView.m in Sources */, 04FC95702EB09516007BD342 /* KBFunctionView.m in Sources */, 04FC956D2EB054B7007BD342 /* KBKeyboardView.m in Sources */, - 04FC95672EB0546C007BD342 /* KBKey.m in Sources */, - A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */, + 04FC95672EB0546C007BD342 /* KBKey.m in Sources */, + A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -897,15 +898,8 @@ GCC_PREFIX_HEADER = keyBoard/KeyBoardPrefixHeader.pch; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; - INFOPLIST_KEY_CFBundleURLTypes = ( - { - CFBundleURLName = com.keyBoardst.keyboard; - CFBundleURLSchemes = ( - kbkeyboard, - ); - }, - ); INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法"; + INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.keyBoardst.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboard\n );\n}"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -922,9 +916,9 @@ STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; }; - name = Debug; - }; 727EC76D2EAF848C00B36487 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 51FE7C4C42C2255B3C1C4128 /* Pods-keyBoard.release.xcconfig */; @@ -938,15 +932,8 @@ GCC_PREFIX_HEADER = keyBoard/KeyBoardPrefixHeader.pch; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; - INFOPLIST_KEY_CFBundleURLTypes = ( - { - CFBundleURLName = com.keyBoardst.keyboard; - CFBundleURLSchemes = ( - kbkeyboard, - ); - }, - ); INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法"; + INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.keyBoardst.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboard\n );\n}"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; diff --git a/keyBoard/Class/Categories/KBHUD.h b/keyBoard/Class/Categories/KBHUD.h index b6ab738..06a65d2 100644 --- a/keyBoard/Class/Categories/KBHUD.h +++ b/keyBoard/Class/Categories/KBHUD.h @@ -11,6 +11,7 @@ // #import +@class UIView; // forward declare to avoid importing UIKit in header typedef NS_ENUM(NSUInteger, KBHUDMaskType) { /// 不加遮罩,事件可透传到后面的视图(与 SVProgressHUDMaskTypeNone 类似) @@ -59,6 +60,13 @@ NS_ASSUME_NONNULL_BEGIN /// 设置自动隐藏的时长(success/error/info),默认 1.2s + (void)setAutoDismissInterval:(NSTimeInterval)interval; +/// 设置缺省承载视图(App Extension 环境下必须设置,例如在键盘扩展里传入 self.view) +/// 注意:内部弱引用,不会形成循环引用。 +/// 若不设置,在非 Extension 的 App 内默认加到 KeyWindow;在 Extension 内将不会显示。 +/// 可在 viewDidLoad 或 viewDidAppear 调用一次即可。 +/// @param view 作为 HUD 的承载父视图 + + (void)setContainerView:(nullable UIView *)view; + @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Categories/KBHUD.m b/keyBoard/Class/Categories/KBHUD.m index a0d3211..2d1f399 100644 --- a/keyBoard/Class/Categories/KBHUD.m +++ b/keyBoard/Class/Categories/KBHUD.m @@ -5,6 +5,7 @@ #import "KBHUD.h" #import +#import #ifndef KBSCREEN #define KBSCREEN [UIScreen mainScreen].bounds.size @@ -16,6 +17,7 @@ static __weak MBProgressHUD *sHUD = nil; static KBHUDMaskType sMaskType = KBHUDMaskTypeClear; // 全局默认遮罩 static BOOL sDefaultTapToDismiss = NO; // 全局默认:不允许点击关闭 static NSTimeInterval sAutoDismiss = 1.2; +static __weak UIView *sContainerView = nil; // 缺省承载视图(扩展里必须设置) #pragma mark - Private Helpers @@ -24,11 +26,37 @@ static NSTimeInterval sAutoDismiss = 1.2; } + (MBProgressHUD *)ensureHUDWithMask:(KBHUDMaskType)mask tap:(BOOL)tap { + // 先尝试使用外部指定的容器视图(扩展环境推荐) + UIView *hostView = sContainerView; +#ifndef KB_APP_EXTENSION + // App 内退回到 KeyWindow + if (!hostView) { + // KB_KeyWindow 在 App 目标的 PrefixHeader 中定义;在扩展内不依赖它 + UIWindow *win = nil; + // 避免强依赖某个前缀:这里以运行时方式访问 UIApplication 以规避编译期的 App Extension 限制 + Class uiAppClass = NSClassFromString(@"UIApplication"); + if (uiAppClass && [uiAppClass respondsToSelector:@selector(sharedApplication)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + id app = [uiAppClass performSelector:@selector(sharedApplication)]; + if ([app respondsToSelector:@selector(keyWindow)]) { + win = [app keyWindow]; + } + if (!win && [app respondsToSelector:@selector(windows)]) { + NSArray *wins = [app windows]; + win = wins.firstObject; + } +#pragma clang diagnostic pop + } + hostView = win; + } +#endif + + if (!hostView) { return nil; } + MBProgressHUD *hud = sHUD; if (!hud) { - UIWindow *win = KB_KeyWindow(); - if (!win) { win = UIApplication.sharedApplication.windows.firstObject; } - hud = [MBProgressHUD showHUDAddedTo:win animated:YES]; + hud = [MBProgressHUD showHUDAddedTo:hostView animated:YES]; sHUD = hud; // 外观:深色圆角,白色文字,模仿 SVProgressHUD 默认 hud.removeFromSuperViewOnHide = YES; @@ -92,6 +120,7 @@ static NSTimeInterval sAutoDismiss = 1.2; + (void)_showText:(NSString *)text icon:(nullable UIImage *)icon { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:sDefaultTapToDismiss]; + if (!hud) { return; } hud.mode = icon ? MBProgressHUDModeCustomView : MBProgressHUDModeText; hud.label.text = text ?: @""; hud.detailsLabel.text = nil; @@ -114,6 +143,7 @@ static NSTimeInterval sAutoDismiss = 1.2; + (void)showWithStatus:(NSString *)status allowTapToDismiss:(BOOL)allow { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:allow]; + if (!hud) { return; } hud.mode = MBProgressHUDModeIndeterminate; hud.label.text = status ?: @""; }]; @@ -126,6 +156,7 @@ static NSTimeInterval sAutoDismiss = 1.2; + (void)showProgress:(float)progress status:(NSString *)status allowTapToDismiss:(BOOL)allow { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:allow]; + if (!hud) { return; } hud.mode = MBProgressHUDModeDeterminate; hud.progress = progress; hud.label.text = status ?: @""; @@ -162,4 +193,8 @@ static NSTimeInterval sAutoDismiss = 1.2; [self showWithStatus:nil allowTapToDismiss:allow]; } ++ (void)setContainerView:(UIView *)view { + sContainerView = view; +} + @end