From c7021e382ee2ee23d2cd14e9e8db653a22a1d881 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 3 Nov 2025 13:25:41 +0800 Subject: [PATCH] fixUI --- CustomKeyboard/KeyboardViewController.m | 6 ++ CustomKeyboard/Manager/KBFullAccessManager.h | 43 ++++++++ CustomKeyboard/Manager/KBFullAccessManager.m | 100 ++++++++++++++++++ CustomKeyboard/View/KBFunctionView.m | 6 +- Shared/KBConfig.h | 5 + Shared/KBKeyboardPermissionManager.h | 37 +++++++ Shared/KBKeyboardPermissionManager.m | 94 ++++++++++++++++ keyBoard.xcodeproj/project.pbxproj | 70 ++++++++---- keyBoard/AppDelegate.m | 19 +++- .../Categories/UIViewController+Extension.h | 15 +++ .../Categories/UIViewController+Extension.m | 12 +++ keyBoard/Class/Home/VC/HomeVC.m | 4 +- 12 files changed, 384 insertions(+), 27 deletions(-) create mode 100644 CustomKeyboard/Manager/KBFullAccessManager.h create mode 100644 CustomKeyboard/Manager/KBFullAccessManager.m create mode 100644 Shared/KBKeyboardPermissionManager.h create mode 100644 Shared/KBKeyboardPermissionManager.m create mode 100644 keyBoard/Class/Categories/UIViewController+Extension.h create mode 100644 keyBoard/Class/Categories/UIViewController+Extension.m diff --git a/CustomKeyboard/KeyboardViewController.m b/CustomKeyboard/KeyboardViewController.m index c4ae3af..320d2bf 100644 --- a/CustomKeyboard/KeyboardViewController.m +++ b/CustomKeyboard/KeyboardViewController.m @@ -13,6 +13,7 @@ #import "KBSettingView.h" #import "Masonry.h" #import "KBAuthManager.h" +#import "KBFullAccessManager.h" static CGFloat KEYBOARDHEIGHT = 256 + 20; @@ -34,6 +35,11 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20; [self setupUI]; // 指定 HUD 的承载视图(扩展里无法取到 App 的 KeyWindow) [KBHUD setContainerView:self.view]; + // 绑定完全访问管理器,便于统一感知和联动网络开关 + [[KBFullAccessManager shared] bindInputController:self]; + __unused id token = [[NSNotificationCenter defaultCenter] addObserverForName:KBFullAccessChangedNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(__unused NSNotification * _Nonnull note) { + // 如需,可在此刷新与完全访问相关的 UI + }]; } diff --git a/CustomKeyboard/Manager/KBFullAccessManager.h b/CustomKeyboard/Manager/KBFullAccessManager.h new file mode 100644 index 0000000..14b1441 --- /dev/null +++ b/CustomKeyboard/Manager/KBFullAccessManager.h @@ -0,0 +1,43 @@ +// +// KBFullAccessManager.h +// 统一封装:检测并管理键盘扩展的“允许完全访问”状态 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, KBFullAccessState) { + KBFullAccessStateUnknown = 0, // 无法确定(降级处理为未开启) + KBFullAccessStateDenied, // 未开启完全访问 + KBFullAccessStateGranted // 已开启完全访问 +}; + +/// 状态变更通知(仅扩展进程内广播) +extern NSNotificationName const KBFullAccessChangedNotification; + +/// 键盘扩展“完全访问”状态管理 +@interface KBFullAccessManager : NSObject + ++ (instancetype)shared; + +/// 绑定当前的 UIInputViewController(用于调用系统私有选择器 hasFullAccess;按字符串反射,避免编译期引用) +- (void)bindInputController:(UIInputViewController *)ivc; + +/// 当前状态(内部做缓存;如需强制刷新,调用 refresh) +- (KBFullAccessState)currentState; + +/// 便捷判断 +- (BOOL)hasFullAccess; + +/// 立即刷新一次状态(若状态有变化会发送 KBFullAccessChangedNotification) +- (void)refresh; + +/// 若未开启,则在传入视图上展示引导弹层(使用现有的 KBFullAccessGuideView);返回是否已开启 +- (BOOL)ensureFullAccessOrGuideInView:(UIView *)parent; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/CustomKeyboard/Manager/KBFullAccessManager.m b/CustomKeyboard/Manager/KBFullAccessManager.m new file mode 100644 index 0000000..ed788ad --- /dev/null +++ b/CustomKeyboard/Manager/KBFullAccessManager.m @@ -0,0 +1,100 @@ +// +// KBFullAccessManager.m +// +// 统一封装“允许完全访问”检测: +// 1) 首选:反射调用 UIInputViewController 的 hasFullAccess(避免直接引用私有 API 标识) +// 2) 兜底:无法判断时返回 Unknown(上层可按需降级为 Denied 并提示) +// + +#import "KBFullAccessManager.h" +#import +#if __has_include("KBNetworkManager.h") +#import "KBNetworkManager.h" +#endif +#if __has_include("KBKeyboardPermissionManager.h") +#import "KBKeyboardPermissionManager.h" +#endif + +NSNotificationName const KBFullAccessChangedNotification = @"KBFullAccessChangedNotification"; + +@interface KBFullAccessManager () +@property (nonatomic, weak) UIInputViewController *ivc; +@property (nonatomic, assign) KBFullAccessState state; +@end + +@implementation KBFullAccessManager + ++ (instancetype)shared { + static KBFullAccessManager *m; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ m = [KBFullAccessManager new]; }); + return m; +} + +- (instancetype)init { + if (self = [super init]) { + _state = KBFullAccessStateUnknown; + } + return self; +} + +- (void)bindInputController:(UIInputViewController *)ivc { + self.ivc = ivc; + [self refresh]; +} + +- (KBFullAccessState)currentState { return _state; } + +- (BOOL)hasFullAccess { return self.state == KBFullAccessStateGranted; } + +- (void)refresh { + KBFullAccessState newState = [self p_detectFullAccessState]; + if (newState != self.state) { + self.state = newState; + [[NSNotificationCenter defaultCenter] postNotificationName:KBFullAccessChangedNotification object:nil]; + [self p_applySideEffects]; + } +} + +- (BOOL)ensureFullAccessOrGuideInView:(UIView *)parent { + [self refresh]; + if (self.state == KBFullAccessStateGranted) return YES; +#if __has_include("KBFullAccessGuideView.h") + // 动态引入,避免主 App 编译引用 + Class guideCls = NSClassFromString(@"KBFullAccessGuideView"); + if (guideCls && [guideCls respondsToSelector:NSSelectorFromString(@"showInView:")]) { + SEL sel = NSSelectorFromString(@"showInView:"); + ((void (*)(id, SEL, UIView *))objc_msgSend)(guideCls, sel, parent); + } +#endif + return NO; +} + +#pragma mark - Detect + +// 通过反射调用 hasFullAccess(若系统提供),否则返回 Unknown +- (KBFullAccessState)p_detectFullAccessState { + UIInputViewController *ivc = self.ivc; + if (!ivc) return KBFullAccessStateUnknown; + + SEL sel = NSSelectorFromString(@"hasFullAccess"); + if ([ivc respondsToSelector:sel]) { + BOOL granted = ((BOOL (*)(id, SEL))objc_msgSend)(ivc, sel); + return granted ? KBFullAccessStateGranted : KBFullAccessStateDenied; + } + // 无法判断时标记 Unknown(上层可按需处理为未开启) + return KBFullAccessStateUnknown; +} + +#pragma mark - Side Effects + +- (void)p_applySideEffects { +#if __has_include("KBNetworkManager.h") + // 根据完全访问状态切换网络总开关 + [KBNetworkManager shared].enabled = (self.state == KBFullAccessStateGranted); +#endif +#if __has_include("KBKeyboardPermissionManager.h") + // 上报给主 App:记录最近一次“完全访问”状态(App 将据此决定是否展示引导页) + [[KBKeyboardPermissionManager shared] reportFullAccessFromExtension:(self.state == KBFullAccessStateGranted)]; +#endif +} + +@end diff --git a/CustomKeyboard/View/KBFunctionView.m b/CustomKeyboard/View/KBFunctionView.m index 2d8366d..08c11f1 100644 --- a/CustomKeyboard/View/KBFunctionView.m +++ b/CustomKeyboard/View/KBFunctionView.m @@ -12,6 +12,7 @@ #import "Masonry.h" #import #import "KBFullAccessGuideView.h" +#import "KBFullAccessManager.h" static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId"; @@ -184,9 +185,8 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId"; NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"kbkeyboard://login?src=functionView&index=%ld&title=%@", (long)indexPath.item, encodedTitle]]; [ivc.extensionContext openURL:scheme completionHandler:^(BOOL ok2) { if (!ok2) { - // 两条路都失败:大概率未开完全访问或宿主拦截。给出指引层。 -// [KBHUD showWithStatus:@"点击测试"]; - dispatch_async(dispatch_get_main_queue(), ^{ [KBFullAccessGuideView showInView:self]; }); + // 两条路都失败:大概率未开完全访问或宿主拦截。统一交由 Manager 引导。 + dispatch_async(dispatch_get_main_queue(), ^{ [[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self]; }); } }]; }]; diff --git a/Shared/KBConfig.h b/Shared/KBConfig.h index 5813a63..46ab8b2 100644 --- a/Shared/KBConfig.h +++ b/Shared/KBConfig.h @@ -31,3 +31,8 @@ #ifndef KB_KEYCHAIN_ACCESS_GROUP #define KB_KEYCHAIN_ACCESS_GROUP @"TN6HHV45BB.com.keyBoardst.shared" #endif + +// 键盘扩展的 Bundle Identifier(用于 App 侧检测是否已添加该键盘) +#ifndef KB_KEYBOARD_EXTENSION_BUNDLE_ID +#define KB_KEYBOARD_EXTENSION_BUNDLE_ID @"com.keyBoardst.CustomKeyboard" +#endif diff --git a/Shared/KBKeyboardPermissionManager.h b/Shared/KBKeyboardPermissionManager.h new file mode 100644 index 0000000..5699d56 --- /dev/null +++ b/Shared/KBKeyboardPermissionManager.h @@ -0,0 +1,37 @@ +// +// KBKeyboardPermissionManager.h +// 主 App/键盘扩展 共用的“键盘启用 + 完全访问”权限管理 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, KBFARecord) { + KBFARecordUnknown = 0, + KBFARecordDenied = 1, + KBFARecordGranted = 2, +}; + +/// 统一权限管理(App 与扩展均可使用) +@interface KBKeyboardPermissionManager : NSObject + ++ (instancetype)shared; + +/// App 侧:是否已添加并启用了自定义键盘(通过遍历 activeInputModes 粗略判断) +- (BOOL)isKeyboardEnabled; + +/// 最后一次由扩展上报的“完全访问”状态(来源:扩展运行后写入共享钥匙串) +- (KBFARecord)lastKnownFullAccess; + +/// 扩展侧:上报“完全访问”状态(写入共享钥匙串,以便 App 读取) +- (void)reportFullAccessFromExtension:(BOOL)granted; + +/// App 侧:若未满足“已启用键盘 + 完全访问(或未知)”则展示引导页(KBPermissionViewController) +- (void)presentPermissionIfNeededFrom:(UIViewController *)presenting; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Shared/KBKeyboardPermissionManager.m b/Shared/KBKeyboardPermissionManager.m new file mode 100644 index 0000000..b59b23e --- /dev/null +++ b/Shared/KBKeyboardPermissionManager.m @@ -0,0 +1,94 @@ +// +// KBKeyboardPermissionManager.m +// + +#import "KBKeyboardPermissionManager.h" +#import +#import "KBConfig.h" + +// Keychain 存储:记录上次扩展上报的“完全访问”状态 +static NSString * const kKBPermService = @"com.keyBoardst.perm"; +static NSString * const kKBPermAccount = @"full_access"; // 保存一个字节/数字:0/1/2 + +@implementation KBKeyboardPermissionManager + ++ (instancetype)shared { + static KBKeyboardPermissionManager *m; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ m = [KBKeyboardPermissionManager new]; }); + return m; +} + +#pragma mark - App side + +- (BOOL)isKeyboardEnabled { + // 与 AppDelegate 中同思路:遍历 activeInputModes,匹配自家扩展 bundle id + for (UITextInputMode *mode in [UITextInputMode activeInputModes]) { + NSString *identifier = nil; + @try { identifier = [mode valueForKey:@"identifier"]; } @catch (__unused NSException *e) { identifier = nil; } + if ([identifier isKindOfClass:NSString.class] && [identifier rangeOfString:KB_KEYBOARD_EXTENSION_BUNDLE_ID].location != NSNotFound) { + return YES; + } + } + return NO; +} + +- (KBFARecord)lastKnownFullAccess { + NSData *data = [self keychainRead]; + if (data.length == 0) return KBFARecordUnknown; + uint8_t v = 0; [data getBytes:&v length:1]; + if (v > KBFARecordGranted) v = KBFARecordUnknown; + return (KBFARecord)v; +} + +- (void)presentPermissionIfNeededFrom:(UIViewController *)presenting { + BOOL enabled = [self isKeyboardEnabled]; + KBFARecord fa = [self lastKnownFullAccess]; + // 策略: + // - 未启用键盘:一定引导; + // - 已启用键盘:仅当明确知道“完全访问被拒绝”才引导;Unknown 不打扰(等待扩展上报)。 + BOOL needGuide = (!enabled) || (enabled && fa == KBFARecordDenied); + if (!needGuide || !presenting) return; + + Class cls = NSClassFromString(@"KBPermissionViewController"); + if (!cls) return; // 主 App 才存在该类 + UIViewController *guide = [cls new]; + guide.modalPresentationStyle = UIModalPresentationFullScreen; + [presenting presentViewController:guide animated:YES completion:nil]; +} + +#pragma mark - Extension side + +- (void)reportFullAccessFromExtension:(BOOL)granted { + uint8_t v = granted ? KBFARecordGranted : KBFARecordDenied; + NSData *data = [NSData dataWithBytes:&v length:1]; + [self keychainWrite:data]; +} + +#pragma mark - Keychain shared blob + +- (NSMutableDictionary *)baseKCQuery { + NSMutableDictionary *q = [@{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: kKBPermService, + (__bridge id)kSecAttrAccount: kKBPermAccount } mutableCopy]; + q[(__bridge id)kSecAttrAccessGroup] = KB_KEYCHAIN_ACCESS_GROUP; + return q; +} + +- (BOOL)keychainWrite:(NSData *)data { + NSMutableDictionary *query = [self baseKCQuery]; + SecItemDelete((__bridge CFDictionaryRef)query); + query[(__bridge id)kSecValueData] = data ?: [NSData data]; + query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + OSStatus st = SecItemAdd((__bridge CFDictionaryRef)query, NULL); + return (st == errSecSuccess); +} + +- (NSData *)keychainRead { + NSMutableDictionary *query = [self baseKCQuery]; + query[(__bridge id)kSecReturnData] = @YES; + query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; + CFTypeRef dataRef = NULL; OSStatus st = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataRef); + if (st != errSecSuccess || !dataRef) return nil; + return (__bridge_transfer NSData *)dataRef; +} + +@end diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index b973b2a..89fe8e2 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* 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 */; }; + 04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */; }; + 04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */; }; + 04A9FE172EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.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 */; }; @@ -38,8 +41,6 @@ 04FC95F12EB339A7007BD342 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95F02EB339A7007BD342 /* LoginViewController.m */; }; 04FC95F42EB339C1007BD342 /* AppleSignInManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */; }; 04FC96142EB34E00007BD342 /* KBLoginSheetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC96122EB34E00007BD342 /* KBLoginSheetViewController.m */; }; - A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4002EB4A0A100000002 /* KBAuthManager.m */; }; - A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4002EB4A0A100000002 /* KBAuthManager.m */; }; 04FC97002EB30A00007BD342 /* KBGuideTopCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */; }; 04FC97032EB30A00007BD342 /* KBGuideKFCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97022EB30A00007BD342 /* KBGuideKFCell.m */; }; 04FC97062EB30A00007BD342 /* KBGuideUserCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97052EB30A00007BD342 /* KBGuideUserCell.m */; }; @@ -50,6 +51,10 @@ A1B2C3D42EB0A0A100000001 /* KBFunctionTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D32EB0A0A100000001 /* KBFunctionTagCell.m */; }; A1B2C3E22EB0C0A100000001 /* KBNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3E12EB0C0A100000001 /* KBNetworkManager.m */; }; A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3F22EB35A9900000001 /* KBFullAccessGuideView.m */; }; + A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4002EB4A0A100000002 /* KBAuthManager.m */; }; + A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4002EB4A0A100000002 /* KBAuthManager.m */; }; + A1B2C4202EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4222EB4B7A100000001 /* KBKeyboardPermissionManager.m */; }; + A1B2C4212EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C4222EB4B7A100000001 /* KBKeyboardPermissionManager.m */; }; ECC9EE02174D86E8D792472F /* Pods_keyBoard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 967065BB5230E43F293B3AF9 /* Pods_keyBoard.framework */; }; /* End PBXBuildFile section */ @@ -78,6 +83,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = ""; }; + 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = ""; }; + 04A9FE142EB873C80020DB6D /* UIViewController+Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Extension.h"; sourceTree = ""; }; + 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Extension.m"; sourceTree = ""; }; 04C6EAAC2EAF86530089C901 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 04C6EAAD2EAF86530089C901 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 04C6EAAE2EAF86530089C901 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -151,8 +160,6 @@ 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 = ""; }; - A1B2C4002EB4A0A100000001 /* KBAuthManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAuthManager.h; sourceTree = ""; }; - A1B2C4002EB4A0A100000002 /* KBAuthManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAuthManager.m; 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; }; @@ -163,6 +170,10 @@ A1B2C3E12EB0C0A100000001 /* KBNetworkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBNetworkManager.m; sourceTree = ""; }; A1B2C3F12EB35A9900000001 /* KBFullAccessGuideView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessGuideView.h; sourceTree = ""; }; A1B2C3F22EB35A9900000001 /* KBFullAccessGuideView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessGuideView.m; sourceTree = ""; }; + A1B2C4002EB4A0A100000001 /* KBAuthManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAuthManager.h; sourceTree = ""; }; + A1B2C4002EB4A0A100000002 /* KBAuthManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAuthManager.m; sourceTree = ""; }; + A1B2C4222EB4B7A100000001 /* KBKeyboardPermissionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardPermissionManager.m; sourceTree = ""; }; + A1B2C4232EB4B7A100000001 /* KBKeyboardPermissionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBKeyboardPermissionManager.h; sourceTree = ""; }; B12EC429812407B9F0E67565 /* Pods-CustomKeyboard.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CustomKeyboard.release.xcconfig"; path = "Target Support Files/Pods-CustomKeyboard/Pods-CustomKeyboard.release.xcconfig"; sourceTree = ""; }; B8CA018AB878499327504AAD /* Pods-CustomKeyboard.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CustomKeyboard.debug.xcconfig"; path = "Target Support Files/Pods-CustomKeyboard/Pods-CustomKeyboard.debug.xcconfig"; sourceTree = ""; }; F67DDBD716E4E616D8CC2C9C /* Pods-keyBoard.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-keyBoard.debug.xcconfig"; path = "Target Support Files/Pods-keyBoard/Pods-keyBoard.debug.xcconfig"; sourceTree = ""; }; @@ -188,6 +199,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 04A9FE122EB4D0D20020DB6D /* Manager */ = { + isa = PBXGroup; + children = ( + 04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */, + 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */, + ); + path = Manager; + sourceTree = ""; + }; 04C6EAB92EAF86530089C901 /* keyBoard */ = { isa = PBXGroup; children = ( @@ -211,6 +231,7 @@ 04C6EAD72EAF870B0089C901 /* CustomKeyboard */ = { isa = PBXGroup; children = ( + 04A9FE122EB4D0D20020DB6D /* Manager */, 04FC95662EB0546C007BD342 /* Model */, 04C6EADA2EAF8C7B0089C901 /* View */, A1B2C3E52EB0C0A100000001 /* Network */, @@ -469,6 +490,8 @@ 04FC970D2EB334F8007BD342 /* KBWebImageManager.m */, 04FC97072EB31B14007BD342 /* KBHUD.h */, 04FC97082EB31B14007BD342 /* KBHUD.m */, + 04A9FE142EB873C80020DB6D /* UIViewController+Extension.h */, + 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */, ); path = Categories; sourceTree = ""; @@ -532,6 +555,8 @@ 04FC98012EB36AAB007BD342 /* KBConfig.h */, A1B2C4002EB4A0A100000001 /* KBAuthManager.h */, A1B2C4002EB4A0A100000002 /* KBAuthManager.m */, + A1B2C4232EB4B7A100000001 /* KBKeyboardPermissionManager.h */, + A1B2C4222EB4B7A100000001 /* KBKeyboardPermissionManager.m */, ); path = Shared; sourceTree = ""; @@ -771,9 +796,12 @@ 04FC95B22EB0B2CC007BD342 /* KBSettingView.m in Sources */, 04FC95702EB09516007BD342 /* KBFunctionView.m in Sources */, 04FC956D2EB054B7007BD342 /* KBKeyboardView.m in Sources */, + 04A9FE172EB873C80020DB6D /* UIViewController+Extension.m in Sources */, 04FC95672EB0546C007BD342 /* KBKey.m in Sources */, A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */, A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */, + 04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */, + A1B2C4202EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -784,6 +812,7 @@ 04FC95E92EB23B67007BD342 /* KBNetworkManager.m in Sources */, 04FC95D22EB1E7AE007BD342 /* MyVC.m in Sources */, 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */, + 04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */, 04C6EABE2EAF86530089C901 /* AppDelegate.m in Sources */, 04FC95F12EB339A7007BD342 /* LoginViewController.m in Sources */, 04FC96142EB34E00007BD342 /* KBLoginSheetViewController.m in Sources */, @@ -804,6 +833,7 @@ 04FC95F42EB339C1007BD342 /* AppleSignInManager.m in Sources */, 04C6EAC12EAF86530089C901 /* ViewController.m in Sources */, A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */, + A1B2C4212EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -847,10 +877,10 @@ DEVELOPMENT_TEAM = TN6HHV45BB; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREFIX_HEADER = CustomKeyboard/PrefixHeader.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", - ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", + ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CustomKeyboard/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; @@ -880,10 +910,10 @@ DEVELOPMENT_TEAM = TN6HHV45BB; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREFIX_HEADER = CustomKeyboard/PrefixHeader.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", - ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", + ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CustomKeyboard/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; @@ -914,10 +944,10 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = TN6HHV45BB; GCC_PREFIX_HEADER = keyBoard/KeyBoardPrefixHeader.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", - ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", + ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法"; @@ -952,10 +982,10 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = TN6HHV45BB; GCC_PREFIX_HEADER = keyBoard/KeyBoardPrefixHeader.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", - ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "KB_KEYCHAIN_ACCESS_GROUP=@\\\"$(AppIdentifierPrefix)com.keyBoardst.shared\\\"", + ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法"; diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index 95b54c6..77dd1e6 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -13,6 +13,7 @@ #import "LoginViewController.h" #import "KBLoginSheetViewController.h" #import "AppleSignInManager.h" +#import // 注意:用于判断系统已启用本输入法扩展的 bundle id 需与扩展 target 的 // PRODUCT_BUNDLE_IDENTIFIER 完全一致。 @@ -34,9 +35,23 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoardst.CustomKe /// 设置GroupID进行配置 // buglyConfig.applicationGroupIdentifier = @""; [Bugly startWithAppId:BuglyId config:buglyConfig]; - /// 判断获取键盘权限 + /// 判断获取键盘权限(统一管理): dispatch_async(dispatch_get_main_queue(), ^{ - [self kb_presentPermissionIfNeeded]; + UIViewController *top = [self kb_topMostViewController]; + if (top) { + Class mgrCls = NSClassFromString(@"KBKeyboardPermissionManager"); + if (mgrCls && [mgrCls respondsToSelector:@selector(shared)]) { + id mgr = [mgrCls performSelector:@selector(shared)]; + if ([mgr respondsToSelector:@selector(presentPermissionIfNeededFrom:)]) { + // 避免私有 API 静态链接,运行时调用 + void (*func)(id, SEL, UIViewController *) = (void (*)(id, SEL, UIViewController *))objc_msgSend; + func(mgr, @selector(presentPermissionIfNeededFrom:), top); + } + } else { + // 兜底走原有逻辑(仅判断是否启用键盘) + [self kb_presentPermissionIfNeeded]; + } + } }); return YES; } diff --git a/keyBoard/Class/Categories/UIViewController+Extension.h b/keyBoard/Class/Categories/UIViewController+Extension.h new file mode 100644 index 0000000..281f13b --- /dev/null +++ b/keyBoard/Class/Categories/UIViewController+Extension.h @@ -0,0 +1,15 @@ +// +// UIViewController+Extension.h +// keyBoard +// +// Created by Mac on 2025/11/3. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIViewController (Extension) +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Categories/UIViewController+Extension.m b/keyBoard/Class/Categories/UIViewController+Extension.m new file mode 100644 index 0000000..68b4ef2 --- /dev/null +++ b/keyBoard/Class/Categories/UIViewController+Extension.m @@ -0,0 +1,12 @@ +// +// UIViewController+Extension.m +// keyBoard +// +// Created by Mac on 2025/11/3. +// + +#import "UIViewController+Extension.h" + +@implementation UIViewController (Extension) + +@end diff --git a/keyBoard/Class/Home/VC/HomeVC.m b/keyBoard/Class/Home/VC/HomeVC.m index 90f79af..8a54048 100644 --- a/keyBoard/Class/Home/VC/HomeVC.m +++ b/keyBoard/Class/Home/VC/HomeVC.m @@ -35,8 +35,8 @@ // KBGuideVC *vc = [[KBGuideVC alloc] init]; // [self.navigationController pushViewController:vc animated:true]; // [KBHUD showInfo:@"加载中..."]; - [KBHUD show]; - [KBHUD showAllowTapToDismiss:true]; +// [KBHUD show]; +// [KBHUD showAllowTapToDismiss:true]; } /*