2025-10-27 19:42:27 +08:00
|
|
|
|
//
|
|
|
|
|
|
// KeyboardViewController.m
|
|
|
|
|
|
// CustomKeyboard
|
|
|
|
|
|
//
|
|
|
|
|
|
// Created by Mac on 2025/10/27.
|
|
|
|
|
|
//
|
|
|
|
|
|
|
2026-02-24 13:38:51 +08:00
|
|
|
|
#import "KeyboardViewController+Private.h"
|
2025-10-28 15:10:38 +08:00
|
|
|
|
|
2026-01-09 19:13:35 +08:00
|
|
|
|
#import "KBBackspaceUndoManager.h"
|
2026-02-24 13:38:51 +08:00
|
|
|
|
#import "KBChatLimitPopView.h"
|
|
|
|
|
|
#import "KBChatPanelView.h"
|
2025-11-03 13:25:41 +08:00
|
|
|
|
#import "KBFullAccessManager.h"
|
2026-01-09 19:13:35 +08:00
|
|
|
|
#import "KBFunctionView.h"
|
2025-12-26 11:47:44 +08:00
|
|
|
|
#import "KBInputBufferManager.h"
|
2026-02-24 13:38:51 +08:00
|
|
|
|
#import "KBKeyBoardMainView.h"
|
2026-01-09 19:13:35 +08:00
|
|
|
|
#import "KBKeyboardSubscriptionView.h"
|
2026-02-24 13:38:51 +08:00
|
|
|
|
#import "KBLocalizationManager.h"
|
2026-03-05 17:42:50 +08:00
|
|
|
|
#import "KBSettingView.h"
|
2026-01-09 19:13:35 +08:00
|
|
|
|
#import "KBSkinManager.h"
|
2026-03-02 20:20:28 +08:00
|
|
|
|
#import "KBSkinInstallBridge.h"
|
2025-12-22 12:54:28 +08:00
|
|
|
|
#import "KBSuggestionEngine.h"
|
2026-03-02 09:19:06 +08:00
|
|
|
|
#import "KBKeyboardLayoutResolver.h"
|
2026-01-29 19:18:38 +08:00
|
|
|
|
#import <SDWebImage/SDWebImage.h>
|
2026-02-24 13:38:51 +08:00
|
|
|
|
|
2026-02-10 13:22:19 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
#import <mach/mach.h>
|
|
|
|
|
|
#endif
|
2026-01-09 19:13:35 +08:00
|
|
|
|
|
2026-02-10 13:22:19 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
static NSInteger sKBKeyboardVCAliveCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static uint64_t KBPhysFootprintBytes(void) {
|
|
|
|
|
|
task_vm_info_data_t vmInfo;
|
|
|
|
|
|
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
|
|
|
|
|
|
kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO,
|
|
|
|
|
|
(task_info_t)&vmInfo, &count);
|
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (uint64_t)vmInfo.phys_footprint;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static NSString *KBFormatMB(uint64_t bytes) {
|
|
|
|
|
|
double mb = (double)bytes / 1024.0 / 1024.0;
|
|
|
|
|
|
return [NSString stringWithFormat:@"%.1fMB", mb];
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-10-27 19:42:27 +08:00
|
|
|
|
@implementation KeyboardViewController
|
|
|
|
|
|
|
2025-10-30 20:23:34 +08:00
|
|
|
|
{
|
2026-01-09 19:13:35 +08:00
|
|
|
|
BOOL _kb_didTriggerLoginDeepLinkOnce;
|
2026-03-02 09:19:06 +08:00
|
|
|
|
NSString *_kb_lastLoadedProfileId; // 记录上次加载的 profileId
|
2026-02-10 13:22:19 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
BOOL _kb_debugDidCountAlive;
|
|
|
|
|
|
#endif
|
2025-10-30 20:23:34 +08:00
|
|
|
|
}
|
2025-10-27 19:42:27 +08:00
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super viewDidLoad];
|
2026-02-10 13:22:19 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
if (!_kb_debugDidCountAlive) {
|
|
|
|
|
|
_kb_debugDidCountAlive = YES;
|
|
|
|
|
|
sKBKeyboardVCAliveCount += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
NSLog(@"[Keyboard] KeyboardViewController viewDidLoad alive=%ld self=%p mem=%@",
|
|
|
|
|
|
(long)sKBKeyboardVCAliveCount, self, KBFormatMB(KBPhysFootprintBytes()));
|
|
|
|
|
|
#endif
|
2026-01-09 19:13:35 +08:00
|
|
|
|
// 撤销删除是“上一段删除操作”的临时状态;键盘被系统回收/重建或跨页面回来时应当清空,避免误显示。
|
|
|
|
|
|
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
|
|
|
|
|
[self setupUI];
|
|
|
|
|
|
self.suggestionEngine = [KBSuggestionEngine shared];
|
|
|
|
|
|
self.currentWord = @"";
|
|
|
|
|
|
// 指定 HUD 的承载视图(扩展里无法取到 App 的 KeyWindow)
|
|
|
|
|
|
[KBHUD setContainerView:self.view];
|
|
|
|
|
|
// 绑定完全访问管理器,便于统一感知和联动网络开关
|
|
|
|
|
|
[[KBFullAccessManager shared] bindInputController:self];
|
2026-02-10 10:21:21 +08:00
|
|
|
|
self.kb_fullAccessObserverToken = [[NSNotificationCenter defaultCenter]
|
2026-01-09 19:13:35 +08:00
|
|
|
|
addObserverForName:KBFullAccessChangedNotification
|
|
|
|
|
|
object:nil
|
|
|
|
|
|
queue:[NSOperationQueue mainQueue]
|
|
|
|
|
|
usingBlock:^(__unused NSNotification *_Nonnull note){
|
|
|
|
|
|
// 如需,可在此刷新与完全访问相关的 UI
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
// 皮肤变化时,立即应用
|
2026-02-10 10:21:21 +08:00
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
self.kb_skinObserverToken = [[NSNotificationCenter defaultCenter]
|
2026-01-09 19:13:35 +08:00
|
|
|
|
addObserverForName:KBSkinDidChangeNotification
|
|
|
|
|
|
object:nil
|
|
|
|
|
|
queue:[NSOperationQueue mainQueue]
|
|
|
|
|
|
usingBlock:^(__unused NSNotification *_Nonnull note) {
|
2026-02-10 10:21:21 +08:00
|
|
|
|
__strong typeof(weakSelf) self = weakSelf;
|
|
|
|
|
|
if (!self) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[self kb_applyTheme];
|
|
|
|
|
|
}];
|
2026-03-05 17:42:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 语言变化时,重建键盘 UI(保证“App 语言=键盘语言”,并支持 App 内切换语言后键盘即时刷新)
|
|
|
|
|
|
self.kb_localizationObserverToken = [[NSNotificationCenter defaultCenter]
|
|
|
|
|
|
addObserverForName:KBLocalizationDidChangeNotification
|
|
|
|
|
|
object:nil
|
|
|
|
|
|
queue:[NSOperationQueue mainQueue]
|
|
|
|
|
|
usingBlock:^(__unused NSNotification *_Nonnull note) {
|
|
|
|
|
|
__strong typeof(weakSelf) self = weakSelf;
|
|
|
|
|
|
if (!self) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
[self kb_reloadUIForLocalizationChange];
|
|
|
|
|
|
}];
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[self kb_applyTheme];
|
2026-02-24 13:38:51 +08:00
|
|
|
|
[self kb_registerDarwinSkinInstallObserver];
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[self kb_consumePendingShopSkin];
|
|
|
|
|
|
[self kb_applyDefaultSkinIfNeeded];
|
2026-03-02 09:19:06 +08:00
|
|
|
|
|
2026-03-02 14:39:47 +08:00
|
|
|
|
[self kb_startObservingAppGroupChanges];
|
|
|
|
|
|
|
2026-03-02 09:19:06 +08:00
|
|
|
|
// 监听 App Group 配置变化,动态切换键盘布局
|
|
|
|
|
|
[self kb_checkAndApplyLayoutIfNeeded];
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}
|
2025-11-04 21:01:46 +08:00
|
|
|
|
|
2026-02-10 13:22:19 +08:00
|
|
|
|
- (void)didReceiveMemoryWarning {
|
|
|
|
|
|
[super didReceiveMemoryWarning];
|
|
|
|
|
|
// 扩展进程内存上限较小:在系统发出内存警告时主动清理可重建的缓存,降低被系统杀死概率。
|
|
|
|
|
|
self.kb_cachedGradientImage = nil;
|
|
|
|
|
|
[self.kb_defaultGradientLayer removeFromSuperlayer];
|
|
|
|
|
|
self.kb_defaultGradientLayer = nil;
|
|
|
|
|
|
[[KBSkinManager shared] clearRuntimeImageCaches];
|
|
|
|
|
|
[[SDImageCache sharedImageCache] clearMemory];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 19:13:35 +08:00
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
|
|
|
|
[super viewWillAppear:animated];
|
2026-02-10 18:53:31 +08:00
|
|
|
|
// FIX: iOS 26 键盘闪烁问题 —— 恢复键盘正确高度
|
|
|
|
|
|
// setupUI 中高度初始为 0(防止系统预渲染快照闪烁),此处恢复为实际键盘高度。
|
|
|
|
|
|
// 此时系统已准备好键盘滑入动画,恢复高度后键盘将正常从底部滑入。
|
|
|
|
|
|
CGFloat portraitWidth = [self kb_portraitWidth];
|
|
|
|
|
|
CGFloat keyboardHeight = [self kb_keyboardHeightForWidth:portraitWidth];
|
|
|
|
|
|
if (self.kb_heightConstraint) {
|
|
|
|
|
|
self.kb_heightConstraint.constant = keyboardHeight;
|
|
|
|
|
|
}
|
2026-01-09 19:13:35 +08:00
|
|
|
|
// 进入/重新进入输入界面时,清理上一次会话残留的撤销状态与缓存,避免显示“撤销删除”但实际上已不可撤销。
|
|
|
|
|
|
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
|
|
|
|
|
[[KBInputBufferManager shared] resetWithText:@""];
|
|
|
|
|
|
[[KBLocalizationManager shared] reloadFromSharedStorageIfNeeded];
|
2026-02-10 13:22:19 +08:00
|
|
|
|
// 键盘再次出现时,恢复 HUD 容器与主题(viewDidDisappear 里可能已清理图片/缓存)。
|
|
|
|
|
|
[KBHUD setContainerView:self.view];
|
|
|
|
|
|
[self kb_ensureKeyBoardMainViewIfNeeded];
|
|
|
|
|
|
[self kb_applyTheme];
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
NSLog(@"[Keyboard] viewWillAppear self=%p mem=%@",
|
|
|
|
|
|
self, KBFormatMB(KBPhysFootprintBytes()));
|
|
|
|
|
|
#endif
|
2026-01-09 19:13:35 +08:00
|
|
|
|
// 注意:微信/QQ 等宿主的 documentContext 可能是“截断窗口”,这里只更新
|
|
|
|
|
|
// liveText,不要把它当作全文 manualSnapshot。
|
|
|
|
|
|
[[KBInputBufferManager shared]
|
|
|
|
|
|
updateFromExternalContextBefore:self.textDocumentProxy
|
|
|
|
|
|
.documentContextBeforeInput
|
|
|
|
|
|
after:self.textDocumentProxy
|
|
|
|
|
|
.documentContextAfterInput];
|
2025-12-26 13:55:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super viewWillDisappear:animated];
|
|
|
|
|
|
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
2026-02-10 13:22:19 +08:00
|
|
|
|
[self kb_releaseMemoryWhenKeyboardHidden];
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
NSLog(@"[Keyboard] viewWillDisappear self=%p mem=%@",
|
|
|
|
|
|
self, KBFormatMB(KBPhysFootprintBytes()));
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidDisappear:(BOOL)animated {
|
|
|
|
|
|
[super viewDidDisappear:animated];
|
|
|
|
|
|
// 再兜底一次,防止某些宿主只触发 willDisappear 而未触发 didDisappear。
|
|
|
|
|
|
[self kb_releaseMemoryWhenKeyboardHidden];
|
2025-12-26 11:47:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 13:07:11 +08:00
|
|
|
|
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super traitCollectionDidChange:previousTraitCollection];
|
|
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
|
|
if (previousTraitCollection.userInterfaceStyle !=
|
|
|
|
|
|
self.traitCollection.userInterfaceStyle) {
|
2026-02-10 10:21:21 +08:00
|
|
|
|
self.kb_cachedGradientImage = nil;
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[self kb_applyDefaultSkinIfNeeded];
|
2026-01-09 13:07:11 +08:00
|
|
|
|
}
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}
|
2026-01-09 13:07:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 11:47:44 +08:00
|
|
|
|
- (void)textDidChange:(id<UITextInput>)textInput {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super textDidChange:textInput];
|
|
|
|
|
|
[[KBInputBufferManager shared]
|
|
|
|
|
|
updateFromExternalContextBefore:self.textDocumentProxy
|
|
|
|
|
|
.documentContextBeforeInput
|
|
|
|
|
|
after:self.textDocumentProxy
|
|
|
|
|
|
.documentContextAfterInput];
|
2025-11-27 15:34:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 20:23:34 +08:00
|
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super viewDidAppear:animated];
|
|
|
|
|
|
// if (!_kb_didTriggerLoginDeepLinkOnce) {
|
|
|
|
|
|
// _kb_didTriggerLoginDeepLinkOnce = YES;
|
|
|
|
|
|
// // 仅在未登录时尝试拉起主App登录
|
|
|
|
|
|
// if (!KBAuthManager.shared.isLoggedIn) {
|
|
|
|
|
|
// [self kb_tryOpenContainerForLoginIfNeeded];
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2025-10-30 20:23:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-07 13:11:23 +08:00
|
|
|
|
- (void)viewDidLayoutSubviews {
|
2026-01-09 19:13:35 +08:00
|
|
|
|
[super viewDidLayoutSubviews];
|
2026-02-24 13:38:51 +08:00
|
|
|
|
// [self kb_updateKeyboardLayoutIfNeeded];
|
2026-02-10 10:21:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 首次布局完成后显示,避免闪烁
|
|
|
|
|
|
if (self.contentView.hidden) {
|
|
|
|
|
|
self.contentView.hidden = NO;
|
|
|
|
|
|
}
|
2026-02-10 13:22:19 +08:00
|
|
|
|
if (self.kb_defaultGradientLayer) {
|
|
|
|
|
|
self.kb_defaultGradientLayer.frame = self.bgImageView.bounds;
|
|
|
|
|
|
}
|
2026-03-02 09:19:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 每次布局时检查是否需要切换键盘布局
|
|
|
|
|
|
[self kb_checkAndApplyLayoutIfNeeded];
|
2026-01-07 13:11:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 19:13:35 +08:00
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size
|
|
|
|
|
|
withTransitionCoordinator:
|
|
|
|
|
|
(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
|
|
|
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
[coordinator
|
|
|
|
|
|
animateAlongsideTransition:^(
|
|
|
|
|
|
id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
2026-01-07 13:11:23 +08:00
|
|
|
|
[weakSelf kb_updateKeyboardLayoutIfNeeded];
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
completion:^(
|
|
|
|
|
|
__unused id<
|
|
|
|
|
|
UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
2026-01-07 13:11:23 +08:00
|
|
|
|
[weakSelf kb_updateKeyboardLayoutIfNeeded];
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}];
|
2026-01-07 13:11:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-24 13:38:51 +08:00
|
|
|
|
- (void)dealloc {
|
|
|
|
|
|
if (self.kb_fullAccessObserverToken) {
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
|
removeObserver:self.kb_fullAccessObserverToken];
|
|
|
|
|
|
self.kb_fullAccessObserverToken = nil;
|
2026-02-10 10:21:21 +08:00
|
|
|
|
}
|
2026-02-24 13:38:51 +08:00
|
|
|
|
if (self.kb_skinObserverToken) {
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
|
removeObserver:self.kb_skinObserverToken];
|
|
|
|
|
|
self.kb_skinObserverToken = nil;
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}
|
2026-03-05 17:42:50 +08:00
|
|
|
|
if (self.kb_localizationObserverToken) {
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
|
removeObserver:self.kb_localizationObserverToken];
|
|
|
|
|
|
self.kb_localizationObserverToken = nil;
|
|
|
|
|
|
}
|
2026-03-02 14:39:47 +08:00
|
|
|
|
[self kb_stopObservingAppGroupChanges];
|
2026-02-24 13:38:51 +08:00
|
|
|
|
[self kb_unregisterDarwinSkinInstallObserver];
|
2026-01-08 18:57:17 +08:00
|
|
|
|
#if DEBUG
|
2026-02-24 13:38:51 +08:00
|
|
|
|
if (_kb_debugDidCountAlive) {
|
|
|
|
|
|
sKBKeyboardVCAliveCount -= 1;
|
2026-01-09 19:13:35 +08:00
|
|
|
|
}
|
2026-02-24 13:38:51 +08:00
|
|
|
|
NSLog(@"[Keyboard] KeyboardViewController dealloc alive=%ld self=%p mem=%@",
|
|
|
|
|
|
(long)sKBKeyboardVCAliveCount, self, KBFormatMB(KBPhysFootprintBytes()));
|
2026-01-08 18:57:17 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 17:42:50 +08:00
|
|
|
|
#pragma mark - Localization
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_reloadUIForLocalizationChange {
|
|
|
|
|
|
if (![NSThread isMainThread]) {
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
[weakSelf kb_reloadUIForLocalizationChange];
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录当前面板状态,重建后尽量恢复。
|
|
|
|
|
|
KBKeyboardPanelMode targetMode = self.kb_panelMode;
|
|
|
|
|
|
// 强制下次布局刷新:即使 profileId 未变,也需要让新建的主视图应用一次当前 profile。
|
|
|
|
|
|
_kb_lastLoadedProfileId = nil;
|
|
|
|
|
|
|
|
|
|
|
|
// 主键盘/面板里有大量静态文案(init 时设置),语言变化后需要重建才能刷新。
|
|
|
|
|
|
if (_keyBoardMainView) {
|
|
|
|
|
|
[_keyBoardMainView removeFromSuperview];
|
|
|
|
|
|
_keyBoardMainView = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.keyBoardMainHeightConstraint = nil;
|
|
|
|
|
|
|
|
|
|
|
|
if (_functionView) {
|
|
|
|
|
|
[_functionView removeFromSuperview];
|
|
|
|
|
|
_functionView = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_settingView) {
|
|
|
|
|
|
[_settingView removeFromSuperview];
|
|
|
|
|
|
_settingView = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_subscriptionView) {
|
|
|
|
|
|
[_subscriptionView removeFromSuperview];
|
|
|
|
|
|
_subscriptionView = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_chatPanelView) {
|
|
|
|
|
|
[_chatPanelView removeFromSuperview];
|
|
|
|
|
|
_chatPanelView = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.chatPanelVisible = NO;
|
|
|
|
|
|
self.chatPanelHeightConstraint = nil;
|
|
|
|
|
|
|
|
|
|
|
|
// 强制触发面板刷新:先回到 Main,再切回目标面板(避免 kb_setPanelMode 直接 return)。
|
|
|
|
|
|
self.kb_panelMode = KBKeyboardPanelModeMain;
|
|
|
|
|
|
[self kb_setPanelMode:targetMode animated:NO];
|
|
|
|
|
|
// 语言变化后,键盘布局/profile 也可能需要同步更新(未手动选择键盘配置时会随 App 语言变化)
|
|
|
|
|
|
[self kb_checkAndApplyLayoutIfNeeded];
|
|
|
|
|
|
[KBHUD setContainerView:self.view];
|
|
|
|
|
|
[self kb_applyTheme];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:19:06 +08:00
|
|
|
|
#pragma mark - Layout Switching
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_checkAndApplyLayoutIfNeeded {
|
|
|
|
|
|
NSString *currentProfileId = [[KBKeyboardLayoutResolver sharedResolver] currentProfileId];
|
|
|
|
|
|
if (currentProfileId.length == 0) {
|
2026-03-02 20:20:28 +08:00
|
|
|
|
currentProfileId = @"en_US_qwerty";
|
2026-03-02 09:19:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ([currentProfileId isEqualToString:_kb_lastLoadedProfileId]) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSLog(@"[KeyboardViewController] Detected profileId change: %@ -> %@", _kb_lastLoadedProfileId, currentProfileId);
|
|
|
|
|
|
_kb_lastLoadedProfileId = currentProfileId;
|
|
|
|
|
|
|
|
|
|
|
|
if (self.keyBoardMainView && [self.keyBoardMainView respondsToSelector:@selector(reloadLayoutWithProfileId:)]) {
|
|
|
|
|
|
[self.keyBoardMainView performSelector:@selector(reloadLayoutWithProfileId:) withObject:currentProfileId];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSString *suggestionEngine = [[KBKeyboardLayoutResolver sharedResolver] suggestionEngineForProfileId:currentProfileId];
|
|
|
|
|
|
if (suggestionEngine.length > 0) {
|
|
|
|
|
|
[self kb_updateSuggestionEngineType:suggestionEngine];
|
|
|
|
|
|
}
|
2026-03-02 20:20:28 +08:00
|
|
|
|
|
|
|
|
|
|
NSString *languageCode = [[KBKeyboardLayoutResolver sharedResolver] currentLanguageCode];
|
|
|
|
|
|
if (languageCode.length > 0) {
|
|
|
|
|
|
NSLog(@"[KeyboardViewController] Reloading skin icon map for language: %@", languageCode);
|
|
|
|
|
|
[KBSkinInstallBridge reloadCurrentSkinIconMapForLanguageCode:languageCode];
|
|
|
|
|
|
}
|
2026-03-02 09:19:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_updateSuggestionEngineType:(NSString *)engineType {
|
|
|
|
|
|
NSLog(@"[KeyboardViewController] Switching suggestion engine to: %@", engineType);
|
|
|
|
|
|
[[KBSuggestionEngine shared] setEngineTypeFromString:engineType];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 14:39:47 +08:00
|
|
|
|
#pragma mark - App Group KVO
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_startObservingAppGroupChanges {
|
|
|
|
|
|
NSUserDefaults *appGroup = [[NSUserDefaults alloc] initWithSuiteName:AppGroup];
|
|
|
|
|
|
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
self.kb_appGroupObserverToken = [[NSNotificationCenter defaultCenter]
|
|
|
|
|
|
addObserverForName:NSUserDefaultsDidChangeNotification
|
|
|
|
|
|
object:appGroup
|
|
|
|
|
|
queue:[NSOperationQueue mainQueue]
|
|
|
|
|
|
usingBlock:^(__unused NSNotification *_Nonnull note) {
|
|
|
|
|
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
|
|
|
|
if (!strongSelf) { return; }
|
|
|
|
|
|
[strongSelf kb_checkAndApplyLayoutIfNeeded];
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
NSLog(@"[KeyboardViewController] Started observing App Group changes");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_stopObservingAppGroupChanges {
|
|
|
|
|
|
if (self.kb_appGroupObserverToken) {
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self.kb_appGroupObserverToken];
|
|
|
|
|
|
self.kb_appGroupObserverToken = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-28 15:18:12 +08:00
|
|
|
|
@end
|