diff --git a/Shared/Localization/en.lproj/Localizable.strings b/Shared/Localization/en.lproj/Localizable.strings index ac2d787..dd69c3e 100644 --- a/Shared/Localization/en.lproj/Localizable.strings +++ b/Shared/Localization/en.lproj/Localizable.strings @@ -12,7 +12,9 @@ "home_title" = "Home"; "home_input_placeholder" = "Type here to test the keyboard"; "home_item_lang_test" = "Language Test"; +"home_item_keyboard_permission" = "Keyboard Permission Guide"; "lang_test_title" = "Language Test"; "lang_toggle" = "Toggle Language"; "current_lang" = "Current: %@"; +"common_back" = "Back"; diff --git a/Shared/Localization/zh-Hans.lproj/Localizable.strings b/Shared/Localization/zh-Hans.lproj/Localizable.strings index 2b89438..00a3f7a 100644 --- a/Shared/Localization/zh-Hans.lproj/Localizable.strings +++ b/Shared/Localization/zh-Hans.lproj/Localizable.strings @@ -12,7 +12,9 @@ "home_title" = "首页"; "home_input_placeholder" = "在此输入,测试键盘"; "home_item_lang_test" = "多语言测试"; +"home_item_keyboard_permission" = "键盘权限引导"; "lang_test_title" = "多语言测试"; "lang_toggle" = "切换语言"; "current_lang" = "当前:%@"; +"common_back" = "返回"; diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index 1b7ebd8..6e94da7 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -35,35 +35,11 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoardst.CustomKe /// 设置GroupID进行配置 // buglyConfig.applicationGroupIdentifier = @""; [Bugly startWithAppId:BuglyId config:buglyConfig]; - /// 判断获取键盘权限(统一管理): - dispatch_async(dispatch_get_main_queue(), ^{ - UIViewController *top = [UIViewController 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]; - } - } - }); + // 键盘权限引导改由 KBGuideVC 内部负责;此处不主动弹出。 return YES; } -- (void)applicationDidBecomeActive:(UIApplication *)application{ - // When returning from Settings, re-check the keyboard enable state - // and hide the guide if the user has enabled our keyboard. - // Also shows the guide again if still not enabled. - dispatch_async(dispatch_get_main_queue(), ^{ - [self kb_presentPermissionIfNeeded]; - }); -} +- (void)applicationDidBecomeActive:(UIApplication *)application{} - (void)setupRootVC{ @@ -120,20 +96,7 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoardst.CustomKe - (void)kb_presentPermissionIfNeeded { - BOOL enabled = KBIsKeyboardEnabled(); - UIViewController *top = [UIViewController kb_topMostViewController]; - if (!top) return; - if ([top isKindOfClass:[KBPermissionViewController class]]) { - if (enabled) { - [top dismissViewControllerAnimated:YES completion:nil]; - } - return; - } - if (!enabled) { - KBPermissionViewController *guide = [KBPermissionViewController new]; - guide.modalPresentationStyle = UIModalPresentationFullScreen; - [top presentViewController:guide animated:YES completion:nil]; - } + // 该逻辑已迁移到 KBGuideVC,保留占位以兼容旧调用,但不再执行任何操作 } diff --git a/keyBoard/Class/Guard/VC/KBGuideVC.m b/keyBoard/Class/Guard/VC/KBGuideVC.m index 6d1a552..df000a8 100644 --- a/keyBoard/Class/Guard/VC/KBGuideVC.m +++ b/keyBoard/Class/Guard/VC/KBGuideVC.m @@ -9,6 +9,8 @@ #import "KBGuideTopCell.h" #import "KBGuideKFCell.h" #import "KBGuideUserCell.h" +#import "KBPermissionViewController.h" +#import "KBKeyboardPermissionManager.h" typedef NS_ENUM(NSInteger, KBGuideItemType) { KBGuideItemTypeTop = 0, // 顶部固定卡片 @@ -75,12 +77,49 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) { self.bgTap.cancelsTouchesInView = NO; self.bgTap.delegate = self; [self.tableView addGestureRecognizer:self.bgTap]; + + // 监听应用回到前台/变为活跃:用于从设置返回时再次校验权限 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_checkKeyboardPermission) name:UIApplicationDidBecomeActiveNotification object:nil]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + // 每次进入页面都校验一次(包括从其它页面返回) + [self kb_checkKeyboardPermission]; +} + +/// 校验键盘权限: +/// - 未启用或已启用但拒绝完全访问 => 弹出引导页 +/// - 已满足条件且正在展示引导页 => 关闭引导页 +- (void)kb_checkKeyboardPermission { + KBKeyboardPermissionManager *mgr = [KBKeyboardPermissionManager shared]; + BOOL enabled = [mgr isKeyboardEnabled]; + KBFARecord fa = [mgr lastKnownFullAccess]; + BOOL needGuide = (!enabled) || (enabled && fa == KBFARecordDenied); + + UIViewController *top = [UIViewController kb_topMostViewController]; + if (needGuide) { + if (![top isKindOfClass:[KBPermissionViewController class]]) { + KBPermissionViewController *guide = [KBPermissionViewController new]; + guide.modalPresentationStyle = UIModalPresentationFullScreen; + __weak typeof(self) weakSelf = self; + guide.onBack = ^{ + // 用户主动点击“返回”时,同步退出引导页 + [weakSelf.navigationController popViewControllerAnimated:YES]; + }; + [top presentViewController:guide animated:YES completion:nil]; + } + } else { + if ([top isKindOfClass:[KBPermissionViewController class]]) { + [top dismissViewControllerAnimated:YES completion:nil]; + } + } +} + - (void)kb_didTapBackground { // 结束编辑,隐藏键盘 [self.view endEditing:YES]; diff --git a/keyBoard/Class/Home/VC/HomeVC.m b/keyBoard/Class/Home/VC/HomeVC.m index e24d760..dee9fac 100644 --- a/keyBoard/Class/Home/VC/HomeVC.m +++ b/keyBoard/Class/Home/VC/HomeVC.m @@ -39,8 +39,8 @@ self.tableView.tableHeaderView = header; [self.view addSubview:self.tableView]; - // 数据 - self.items = @[ KBLocalized(@"home_item_lang_test") ]; + // 数据(第一个:语言测试;第二个:键盘权限引导) + self.items = @[ KBLocalized(@"home_item_lang_test"), KBLocalized(@"home_item_keyboard_permission") ]; // 首次进入,聚焦到输入框方便测试键盘 dispatch_async(dispatch_get_main_queue(), ^{ [self.textView becomeFirstResponder]; }); @@ -56,7 +56,7 @@ // 刷新本页涉及的多语言文案 self.title = KBLocalized(@"home_title"); // 重建 items 以更新本地化的 cell 标题 - self.items = @[ KBLocalized(@"home_item_lang_test") ]; + self.items = @[ KBLocalized(@"home_item_lang_test"), KBLocalized(@"home_item_keyboard_permission") ]; [self.tableView reloadData]; } @@ -87,6 +87,10 @@ // 多语言测试页 KBLangTestVC *vc = [KBLangTestVC new]; [self.navigationController pushViewController:vc animated:YES]; + } else if (indexPath.row == 1) { + // 键盘权限引导页 + KBGuideVC *vc = [KBGuideVC new]; + [self.navigationController pushViewController:vc animated:YES]; } } diff --git a/keyBoard/VC/KBPermissionViewController.h b/keyBoard/VC/KBPermissionViewController.h index e2a77a1..c425135 100644 --- a/keyBoard/VC/KBPermissionViewController.h +++ b/keyBoard/VC/KBPermissionViewController.h @@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN @interface KBPermissionViewController : UIViewController +/// 点击页面左上角“返回”时回调。用于让调用方(如 KBGuideVC)一起退出等自定义行为。 +@property (nonatomic, copy, nullable) void (^onBack)(void); + @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/VC/KBPermissionViewController.m b/keyBoard/VC/KBPermissionViewController.m index 2868970..aec5744 100644 --- a/keyBoard/VC/KBPermissionViewController.m +++ b/keyBoard/VC/KBPermissionViewController.m @@ -9,7 +9,12 @@ #import @interface KBPermissionViewController () - +@property (nonatomic, strong) UIButton *backButton; // 左上角返回 +@property (nonatomic, strong) UILabel *titleLabel; // 标题 +@property (nonatomic, strong) UILabel *tipsLabel; // 步骤提示 +@property (nonatomic, strong) UIView *cardView; // 中部卡片 +@property (nonatomic, strong) UIButton *openButton; // 去设置 +@property (nonatomic, strong) UILabel *helpLabel; // 底部帮助 @end @implementation KBPermissionViewController @@ -18,67 +23,66 @@ [super viewDidLoad]; self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0]; - UIButton *close = [UIButton buttonWithType:UIButtonTypeSystem]; - [close setTitle:@"X" forState:UIControlStateNormal]; - close.titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold]; - close.tintColor = [UIColor darkTextColor]; - close.frame = CGRectMake(self.view.bounds.size.width - 44, 44, 28, 28); - close.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; - [close addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside]; - [self.view addSubview:close]; + // 懒加载控件 + 添加到视图 + [self.view addSubview:self.backButton]; + [self.view addSubview:self.titleLabel]; + [self.view addSubview:self.tipsLabel]; + [self.view addSubview:self.cardView]; + [self.view addSubview:self.openButton]; + [self.view addSubview:self.helpLabel]; - UILabel *title = [[UILabel alloc] init]; - title.text = [[KBLocalizationManager shared] localizedStringForKey:@"perm_title_enable" table:nil value:@"启用输入法"]; - title.font = [UIFont systemFontOfSize:22 weight:UIFontWeightSemibold]; - title.textColor = [UIColor blackColor]; - title.textAlignment = NSTextAlignmentCenter; - title.frame = CGRectMake(24, 100, self.view.bounds.size.width - 48, 28); - title.autoresizingMask = UIViewAutoresizingFlexibleWidth; - [self.view addSubview:title]; + // Masonry 约束 + [self.backButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.view).offset(16); + make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(8); + + make.width.mas_equalTo(60); + make.height.mas_equalTo(32); + }]; - UILabel *tips = [[UILabel alloc] init]; - // 保留简体中文为默认值;正式多语言请在 Localizable.strings 中提供 - tips.text = [[KBLocalizationManager shared] localizedStringForKey:@"perm_steps" table:nil value:@"1 开启键盘 > 2 允许完全访问"]; - tips.font = [UIFont systemFontOfSize:14]; - tips.textColor = [UIColor darkGrayColor]; - tips.textAlignment = NSTextAlignmentCenter; - tips.frame = CGRectMake(24, CGRectGetMaxY(title.frame) + 8, self.view.bounds.size.width - 48, 20); - tips.autoresizingMask = UIViewAutoresizingFlexibleWidth; - [self.view addSubview:tips]; + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(48); + + make.left.equalTo(self.view).offset(24); + make.right.equalTo(self.view).offset(-24); + make.height.mas_equalTo(28); + }]; - UIView *card = [[UIView alloc] initWithFrame:CGRectMake(32, CGRectGetMaxY(tips.frame) + 28, self.view.bounds.size.width - 64, 260)]; - card.backgroundColor = [UIColor whiteColor]; - card.layer.cornerRadius = 16; - card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor; - card.layer.shadowOpacity = 1; - card.layer.shadowRadius = 12; - [self.view addSubview:card]; + [self.tipsLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.titleLabel.mas_bottom).offset(8); + make.left.equalTo(self.view).offset(24); + make.right.equalTo(self.view).offset(-24); + }]; - UIButton *open = [UIButton buttonWithType:UIButtonTypeSystem]; - [open setTitle:[[KBLocalizationManager shared] localizedStringForKey:@"perm_open_settings" table:nil value:@"去设置中开启"] forState:UIControlStateNormal]; - open.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold]; - [open setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - open.backgroundColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0]; - open.layer.cornerRadius = 8; - CGFloat btnW = self.view.bounds.size.width - 64; - open.frame = CGRectMake(32, CGRectGetMaxY(card.frame) + 32, btnW, 48); - open.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; - [open addTarget:self action:@selector(openSettings) forControlEvents:UIControlEventTouchUpInside]; - [self.view addSubview:open]; + [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.tipsLabel.mas_bottom).offset(28); + make.left.equalTo(self.view).offset(32); + make.right.equalTo(self.view).offset(-32); + make.height.mas_equalTo(260); + }]; - UILabel *help = [[UILabel alloc] init]; - help.text = [[KBLocalizationManager shared] localizedStringForKey:@"perm_help" table:nil value:@"没有找到键盘? 请前往 设置 > 通用 > 键盘 > 键盘 > 添加新键盘"]; - help.font = [UIFont systemFontOfSize:12]; - help.textColor = [UIColor grayColor]; - help.textAlignment = NSTextAlignmentCenter; - help.numberOfLines = 2; - help.frame = CGRectMake(24, CGRectGetMaxY(open.frame) + 12, self.view.bounds.size.width - 48, 36); - help.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; - [self.view addSubview:help]; + [self.openButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.cardView.mas_bottom).offset(32); + make.left.equalTo(self.view).offset(32); + make.right.equalTo(self.view).offset(-32); + make.height.mas_equalTo(48); + }]; + + [self.helpLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.openButton.mas_bottom).offset(12); + make.left.equalTo(self.view).offset(24); + make.right.equalTo(self.view).offset(-24); + }]; } -- (void)dismissSelf { - [self dismissViewControllerAnimated:YES completion:nil]; +#pragma mark - Actions + +- (void)onBack { + // 先关闭自身,再让调用方按需处理(例如同时退出引导页) + void (^handler)(void) = self.onBack; + [self dismissViewControllerAnimated:YES completion:^{ + if (handler) handler(); + }]; } - (void)openSettings { @@ -93,4 +97,76 @@ } } +#pragma mark - Lazy Subviews + +- (UIButton *)backButton { + if (!_backButton) { + _backButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_backButton setTitle:KBLocalized(@"common_back") forState:UIControlStateNormal]; + _backButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; + [_backButton setTitleColor:[UIColor darkTextColor] forState:UIControlStateNormal]; + [_backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside]; + } + return _backButton; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.text = KBLocalized(@"perm_title_enable"); + _titleLabel.font = [UIFont systemFontOfSize:22 weight:UIFontWeightSemibold]; + _titleLabel.textColor = [UIColor blackColor]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +- (UILabel *)tipsLabel { + if (!_tipsLabel) { + _tipsLabel = [UILabel new]; + _tipsLabel.text = KBLocalized(@"perm_steps"); + _tipsLabel.font = [UIFont systemFontOfSize:14]; + _tipsLabel.textColor = [UIColor darkGrayColor]; + _tipsLabel.textAlignment = NSTextAlignmentCenter; + } + return _tipsLabel; +} + +- (UIView *)cardView { + if (!_cardView) { + _cardView = [UIView new]; + _cardView.backgroundColor = [UIColor whiteColor]; + _cardView.layer.cornerRadius = 16; + _cardView.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor; + _cardView.layer.shadowOpacity = 1; + _cardView.layer.shadowRadius = 12; + } + return _cardView; +} + +- (UIButton *)openButton { + if (!_openButton) { + _openButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_openButton setTitle:KBLocalized(@"perm_open_settings") forState:UIControlStateNormal]; + _openButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold]; + [_openButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + _openButton.backgroundColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0]; + _openButton.layer.cornerRadius = 8; + [_openButton addTarget:self action:@selector(openSettings) forControlEvents:UIControlEventTouchUpInside]; + } + return _openButton; +} + +- (UILabel *)helpLabel { + if (!_helpLabel) { + _helpLabel = [UILabel new]; + _helpLabel.text = KBLocalized(@"perm_help"); + _helpLabel.font = [UIFont systemFontOfSize:12]; + _helpLabel.textColor = [UIColor grayColor]; + _helpLabel.textAlignment = NSTextAlignmentCenter; + _helpLabel.numberOfLines = 2; + } + return _helpLabel; +} + @end