From 4c57f16058f9e509b1d3f882030ea0aff213a6dc Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Tue, 10 Feb 2026 10:21:21 +0800 Subject: [PATCH] 1 --- CustomKeyboard/KeyboardViewController.m | 204 ++++++------------ keyBoard.xcodeproj/project.pbxproj | 52 +++-- .../xcschemes/CustomKeyboard.xcscheme | 17 +- keyBoard/Class/AiTalk/VC/KBAIHomeVC.m | 23 +- .../VC/FunctionTest/KBKeyboardStressTestVC.h | 9 + .../VC/FunctionTest/KBKeyboardStressTestVC.m | 142 ++++++++++++ 6 files changed, 269 insertions(+), 178 deletions(-) create mode 100644 keyBoard/Class/Home/VC/FunctionTest/KBKeyboardStressTestVC.h create mode 100644 keyBoard/Class/Home/VC/FunctionTest/KBKeyboardStressTestVC.m diff --git a/CustomKeyboard/KeyboardViewController.m b/CustomKeyboard/KeyboardViewController.m index 8717e56..73d4c56 100644 --- a/CustomKeyboard/KeyboardViewController.m +++ b/CustomKeyboard/KeyboardViewController.m @@ -78,10 +78,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, KBFunctionView *functionView; // 功能面板视图(点击工具栏第0个时显示) @property(nonatomic, strong) KBSettingView *settingView; // 设置页 @property(nonatomic, strong) UIImageView *bgImageView; // 背景图(在底层) -@property(nonatomic, strong) UIImageView *personaAvatarImageView; // 语音模式下显示的 persona 小头像 -@property(nonatomic, strong) UIImageView *personaGrayImageView; // 语音模式下显示的 persona 小头像 -@property(nonatomic, strong) UIVisualEffectView *personaBlurView; // 语音模式下头像高斯模糊层 - @property(nonatomic, strong) KBChatPanelView *chatPanelView; @property(nonatomic, strong) KBKeyboardSubscriptionView *subscriptionView; @property(nonatomic, strong) KBSuggestionEngine *suggestionEngine; @@ -96,9 +92,13 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, @property(nonatomic, strong) NSLayoutConstraint *kb_widthConstraint; @property(nonatomic, assign) CGFloat kb_lastPortraitWidth; @property(nonatomic, assign) CGFloat kb_lastKeyboardHeight; +@property(nonatomic, strong) UIImage *kb_cachedGradientImage; +@property(nonatomic, assign) CGSize kb_cachedGradientSize; @property(nonatomic, strong) NSMutableArray *chatMessages; @property(nonatomic, strong) AVAudioPlayer *chatAudioPlayer; @property(nonatomic, assign) BOOL chatPanelVisible; +@property(nonatomic, strong, nullable) id kb_fullAccessObserverToken; +@property(nonatomic, strong, nullable) id kb_skinObserverToken; @end @implementation KeyboardViewController @@ -118,7 +118,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, [KBHUD setContainerView:self.view]; // 绑定完全访问管理器,便于统一感知和联动网络开关 [[KBFullAccessManager shared] bindInputController:self]; - __unused id token = [[NSNotificationCenter defaultCenter] + self.kb_fullAccessObserverToken = [[NSNotificationCenter defaultCenter] addObserverForName:KBFullAccessChangedNotification object:nil queue:[NSOperationQueue mainQueue] @@ -127,11 +127,16 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, }]; // 皮肤变化时,立即应用 - __unused id token2 = [[NSNotificationCenter defaultCenter] + __weak typeof(self) weakSelf = self; + self.kb_skinObserverToken = [[NSNotificationCenter defaultCenter] addObserverForName:KBSkinDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(__unused NSNotification *_Nonnull note) { + __strong typeof(weakSelf) self = weakSelf; + if (!self) { + return; + } [self kb_applyTheme]; }]; [self kb_applyTheme]; @@ -162,8 +167,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [[KBBackspaceUndoManager shared] registerNonClearAction]; - // 清理 persona 头像内存 - [self kb_hidePersonaAvatar]; } - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { @@ -171,6 +174,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, if (@available(iOS 13.0, *)) { if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) { + self.kb_cachedGradientImage = nil; [self kb_applyDefaultSkinIfNeeded]; } } @@ -250,6 +254,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, make.height.mas_equalTo(chatPanelHeight); }]; self.chatPanelView.hidden = YES; + + // 初始隐藏,避免布局完成前闪烁 + self.contentView.hidden = YES; } #pragma mark - Private @@ -425,19 +432,18 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, pageId:@"keyboard_settings" extra:nil completion:nil]; - // if (!self.settingView) { - self.settingView = [[KBSettingView alloc] init]; - self.settingView.hidden = YES; - [self.contentView addSubview:self.settingView]; - [self.settingView mas_makeConstraints:^(MASConstraintMaker *make) { - // 与键盘主视图完全等同的区域,保证高度、宽度一致 - make.edges.equalTo(self.contentView); - }]; - [self.settingView.backButton addTarget:self - action:@selector(onTapSettingsBack) - forControlEvents:UIControlEventTouchUpInside]; - // } - [self.contentView bringSubviewToFront:self.settingView]; + KBSettingView *settingView = self.settingView; + if (!settingView.superview) { + settingView.hidden = YES; + [self.contentView addSubview:settingView]; + [settingView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.contentView); + }]; + [settingView.backButton addTarget:self + action:@selector(onTapSettingsBack) + forControlEvents:UIControlEventTouchUpInside]; + } + [self.contentView bringSubviewToFront:settingView]; // 以 keyBoardMainView 的实际宽度为准,避免首次添加时 self.view 宽度尚未计算 [self.contentView layoutIfNeeded]; CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds); @@ -447,17 +453,18 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, if (w <= 0) { w = [self kb_portraitWidth]; } - self.settingView.transform = CGAffineTransformMakeTranslation(w, 0); - self.settingView.hidden = NO; + settingView.transform = CGAffineTransformMakeTranslation(w, 0); + settingView.hidden = NO; [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ - self.settingView.transform = CGAffineTransformIdentity; + settingView.transform = CGAffineTransformIdentity; } completion:nil]; } else { - if (!self.settingView || self.settingView.hidden) + KBSettingView *settingView = self.settingView; + if (!settingView.superview || settingView.hidden) return; CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds); if (w <= 0) { @@ -470,10 +477,10 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ - self.settingView.transform = CGAffineTransformMakeTranslation(w, 0); + settingView.transform = CGAffineTransformMakeTranslation(w, 0); } completion:^(BOOL finished) { - self.settingView.hidden = YES; + settingView.hidden = YES; }]; } } @@ -663,8 +670,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, if (index == 1) { [self showFunctionPanel:NO]; [self showChatPanel:YES]; - // 显示 persona 头像 - [self kb_showPersonaAvatarOnBgImageView]; return; } [self showFunctionPanel:NO]; @@ -832,8 +837,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, } self.chatAudioPlayer = nil; [self showChatPanel:NO]; - // 隐藏 persona 头像 - [self kb_hidePersonaAvatar]; } #pragma mark - Chat Helpers @@ -1482,108 +1485,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, return _subscriptionView; } -- (UIImageView *)personaAvatarImageView { - if (!_personaAvatarImageView) { - _personaAvatarImageView = [[UIImageView alloc] init]; - _personaAvatarImageView.contentMode = UIViewContentModeScaleAspectFill; - _personaAvatarImageView.clipsToBounds = YES; - _personaAvatarImageView.hidden = YES; - [_personaAvatarImageView addSubview:self.personaBlurView]; - [self.personaBlurView mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(_personaAvatarImageView); - }]; - } - return _personaAvatarImageView; -} -- (UIImageView *)personaGrayImageView{ - if (!_personaGrayImageView) { - _personaGrayImageView = [[UIImageView alloc] init]; - _personaAvatarImageView.contentMode = UIViewContentModeScaleAspectFill; - - } - return _personaGrayImageView; -} - -- (UIVisualEffectView *)personaBlurView { - if (!_personaBlurView) { - UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; - _personaBlurView = [[UIVisualEffectView alloc] initWithEffect:effect]; - _personaBlurView.hidden = YES; - _personaBlurView.userInteractionEnabled = NO; - } - return _personaBlurView; -} - -#pragma mark - Persona Avatar - -/// 从 AppGroup 读取选中的 persona 信息 -- (NSDictionary *)kb_selectedPersonaFromAppGroup { - NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:AppGroup]; - NSDictionary *personaDict = [ud objectForKey:@"AppGroup_SelectedPersona"]; - if ([personaDict isKindOfClass:[NSDictionary class]]) { - return personaDict; - } - return nil; -} - -/// 在 bgImageView 上显示 persona 头像 -- (void)kb_showPersonaAvatarOnBgImageView { - // 检查是否有完全访问权限 - if (![[KBFullAccessManager shared] hasFullAccess]) { - NSLog(@"[Keyboard] 未开启完全访问,无法显示 persona 头像"); - return; - } - - // 从 AppGroup 共享目录读取预处理好的小图片 - NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup]; - if (!containerURL) { - NSLog(@"[Keyboard] 无法获取 AppGroup 容器目录"); - return; - } - - NSString *imagePath = [[containerURL path] stringByAppendingPathComponent:@"persona_cover.jpg"]; - if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) { - NSLog(@"[Keyboard] persona 封面图文件不存在: %@", imagePath); - return; - } - - NSLog(@"[Keyboard] 准备从本地加载 persona 封面图: %@", imagePath); - - // 添加视图到 contentView,与 bgImageView 尺寸一致 - if (!self.personaAvatarImageView.superview) { - [self.contentView insertSubview:self.personaAvatarImageView aboveSubview:self.bgImageView]; - [self.personaAvatarImageView addSubview:self.personaGrayImageView]; - [self.personaAvatarImageView mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(self.bgImageView); - }]; - [self.personaGrayImageView mas_makeConstraints:^(MASConstraintMaker *make) { - make.left.right.bottom.equalTo(self.personaAvatarImageView); - make.height.mas_equalTo(self.keyBoardMainView); - }]; - } - - // 先清理旧图片 - self.personaAvatarImageView.image = nil; - - // 从本地文件加载图片(已经是缩小后的小图片,内存占用很小) - UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; - if (image) { - self.personaAvatarImageView.image = image; - self.personaAvatarImageView.hidden = NO; - self.personaBlurView.hidden = NO; - NSLog(@"[Keyboard] persona 封面图加载成功"); - } else { - NSLog(@"[Keyboard] persona 封面图加载失败"); - } -} - -/// 隐藏 persona 头像 -- (void)kb_hidePersonaAvatar { - self.personaAvatarImageView.hidden = YES; - self.personaAvatarImageView.image = nil; - self.personaBlurView.hidden = YES; -} - #pragma mark - Actions - (void)kb_openRechargeForProduct:(KBKeyboardSubscriptionProduct *)product { @@ -1638,10 +1539,22 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, } - (void)dealloc { + if (self.kb_fullAccessObserverToken) { + [[NSNotificationCenter defaultCenter] + removeObserver:self.kb_fullAccessObserverToken]; + self.kb_fullAccessObserverToken = nil; + } + if (self.kb_skinObserverToken) { + [[NSNotificationCenter defaultCenter] removeObserver:self.kb_skinObserverToken]; + self.kb_skinObserverToken = nil; + } CFNotificationCenterRemoveObserver( CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), (__bridge CFStringRef)KBDarwinSkinInstallRequestNotification, NULL); +#if DEBUG + NSLog(@"[Keyboard] KeyboardViewController dealloc"); +#endif } // 当键盘第一次显示时,尝试唤起主 App 以提示登录(由主 App @@ -1659,7 +1572,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; - [self kb_updateKeyboardLayoutIfNeeded]; +// [self kb_updateKeyboardLayoutIfNeeded]; + + // 首次布局完成后显示,避免闪烁 + if (self.contentView.hidden) { + self.contentView.hidden = NO; + } } - (void)viewWillTransitionToSize:(CGSize)size @@ -1749,9 +1667,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, } UIColor *topColor = [UIColor colorWithHex:0xDEDFE4]; UIColor *bottomColor = [UIColor colorWithHex:0xD1D3DB]; - img = [self kb_defaultGradientImageWithSize:size - topColor:topColor - bottomColor:bottomColor]; +// img = [self kb_defaultGradientImageWithSize:size +// topColor:topColor +// bottomColor:bottomColor]; self.contentView.backgroundColor = [UIColor clearColor]; self.bgImageView.backgroundColor = [UIColor clearColor]; } @@ -1764,7 +1682,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, NSLog(@"⌨️[Keyboard] apply theme id=%@ hasBg=%d", t.skinId, (img != nil)); [self kb_logSkinDiagnosticsWithTheme:t backgroundImage:img]; self.bgImageView.image = img; - self.personaGrayImageView.image = img; // [self.chatPanelView kb_setBackgroundImage:img]; BOOL hasImg = (img != nil); @@ -1819,8 +1736,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, return nil; } - // 将动态颜色解析为当前 trait collection 下的具体颜色值 - // 否则在 UIGraphicsBeginImageContextWithOptions 中渲染时会使用默认的浅色模式 + // 尺寸未变则复用缓存,避免反复创建图片撑爆键盘扩展内存 + if (self.kb_cachedGradientImage && + CGSizeEqualToSize(self.kb_cachedGradientSize, size)) { + return self.kb_cachedGradientImage; + } + UIColor *resolvedTopColor = topColor; UIColor *resolvedBottomColor = bottomColor; if (@available(iOS 13.0, *)) { @@ -1841,6 +1762,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, [layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); + + self.kb_cachedGradientImage = image; + self.kb_cachedGradientSize = size; return image; } diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 4d4ea44..57da70b 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -78,7 +78,6 @@ 046131142ECF454500A6FADF /* KBKeyPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = 046131132ECF454500A6FADF /* KBKeyPreviewView.m */; }; 0477BDF02EBB76E30055D639 /* HomeSheetVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDEF2EBB76E30055D639 /* HomeSheetVC.m */; }; 0477BDF32EBB7B850055D639 /* KBDirectionIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF22EBB7B850055D639 /* KBDirectionIndicatorView.m */; }; - 0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF62EBC63A80055D639 /* KBTestVC.m */; }; 0477BDFA2EBC66340055D639 /* HomeHeadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF92EBC66340055D639 /* HomeHeadView.m */; }; 0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDFC2EBC6A170055D639 /* HomeHotVC.m */; }; 0477BE002EBC6A330055D639 /* HomeRankVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDFF2EBC6A330055D639 /* HomeRankVC.m */; }; @@ -140,14 +139,13 @@ 048FFD112F27432D005D62AE /* KBPersonaPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD0F2F27432D005D62AE /* KBPersonaPageModel.m */; }; 048FFD142F274342005D62AE /* KBPersonaChatCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD132F274342005D62AE /* KBPersonaChatCell.m */; }; 048FFD182F2763A5005D62AE /* KBVoiceInputBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD172F2763A5005D62AE /* KBVoiceInputBar.m */; }; - 048FFD1D2F277486005D62AE /* KBChatHistoryPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1C2F277486005D62AE /* KBChatHistoryPageModel.m */; }; - 048FFD1E2F277486005D62AE /* KBChatHistoryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1A2F277486005D62AE /* KBChatHistoryModel.m */; }; - 048FFD242F28A836005D62AE /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; }; - A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; }; - 048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD262F28C6CF005D62AE /* KBImagePositionButton.m */; }; - 048FFD2A2F28E99A005D62AE /* KBCommentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD292F28E99A005D62AE /* KBCommentModel.m */; }; - 048FFD2D2F29F356005D62AE /* KBAIMessageVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2C2F29F356005D62AE /* KBAIMessageVC.m */; }; - 048FFD302F29F3C3005D62AE /* KBAIMessageZanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */; }; + 048FFD1D2F277486005D62AE /* KBChatHistoryPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1C2F277486005D62AE /* KBChatHistoryPageModel.m */; }; + 048FFD1E2F277486005D62AE /* KBChatHistoryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1A2F277486005D62AE /* KBChatHistoryModel.m */; }; + 048FFD242F28A836005D62AE /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; }; + 048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD262F28C6CF005D62AE /* KBImagePositionButton.m */; }; + 048FFD2A2F28E99A005D62AE /* KBCommentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD292F28E99A005D62AE /* KBCommentModel.m */; }; + 048FFD2D2F29F356005D62AE /* KBAIMessageVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2C2F29F356005D62AE /* KBAIMessageVC.m */; }; + 048FFD302F29F3C3005D62AE /* KBAIMessageZanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */; }; 048FFD332F29F3D2005D62AE /* KBAIMessageChatingVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD322F29F3D2005D62AE /* KBAIMessageChatingVC.m */; }; 048FFD342F29F400005D62AE /* KBAIMessageListVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD362F29F400005D62AE /* KBAIMessageListVC.m */; }; 048FFD362F29F88E005D62AE /* AIMessageVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD352F29F88E005D62AE /* AIMessageVM.m */; }; @@ -209,6 +207,8 @@ 04A9FE202EB893F10020DB6D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04A9FE1E2EB893F10020DB6D /* Localizable.strings */; }; 04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04A9FE1E2EB893F10020DB6D /* Localizable.strings */; }; 04B5A1A22EEFA12300AAAAAA /* KBPayProductModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 04B5A1A12EEFA12300AAAAAA /* KBPayProductModel.m */; }; + 04BBF89D2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */; }; + 04BBF89E2F3ACD8800B1FBB2 /* KBTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.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 */; }; @@ -302,6 +302,7 @@ A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9212FC9000100000001 /* KBChatMessage.m */; }; A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9232FC9000100000001 /* KBChatMessageCell.m */; }; A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9252FC9000100000001 /* KBChatPanelView.m */; }; + A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; }; A1B2D7022EB8C00100000001 /* KBLangTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2D7012EB8C00100000001 /* KBLangTestVC.m */; }; A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0022EBC7AAA00000001 /* KBTopThreeView.m */; }; A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0042EBC7AAA00000001 /* HomeHotCell.m */; }; @@ -450,8 +451,6 @@ 0477BDEF2EBB76E30055D639 /* HomeSheetVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeSheetVC.m; sourceTree = ""; }; 0477BDF12EBB7B850055D639 /* KBDirectionIndicatorView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBDirectionIndicatorView.h; sourceTree = ""; }; 0477BDF22EBB7B850055D639 /* KBDirectionIndicatorView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBDirectionIndicatorView.m; sourceTree = ""; }; - 0477BDF52EBC63A80055D639 /* KBTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBTestVC.h; sourceTree = ""; }; - 0477BDF62EBC63A80055D639 /* KBTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTestVC.m; sourceTree = ""; }; 0477BDF82EBC66340055D639 /* HomeHeadView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeHeadView.h; sourceTree = ""; }; 0477BDF92EBC66340055D639 /* HomeHeadView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeHeadView.m; sourceTree = ""; }; 0477BDFB2EBC6A170055D639 /* HomeHotVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeHotVC.h; sourceTree = ""; }; @@ -685,6 +684,10 @@ 04A9FE1D2EB893F10020DB6D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 04B5A1A02EEFA12300AAAAAA /* KBPayProductModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPayProductModel.h; sourceTree = ""; }; 04B5A1A12EEFA12300AAAAAA /* KBPayProductModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPayProductModel.m; sourceTree = ""; }; + 04BBF8992F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBKeyboardStressTestVC.h; sourceTree = ""; }; + 04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardStressTestVC.m; sourceTree = ""; }; + 04BBF89B2F3ACD8800B1FBB2 /* KBTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBTestVC.h; sourceTree = ""; }; + 04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTestVC.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 = ""; }; @@ -1247,12 +1250,14 @@ 0477BE012EBC6D420055D639 /* FunctionTest */ = { isa = PBXGroup; children = ( - 0477BDF52EBC63A80055D639 /* KBTestVC.h */, - 0477BDF62EBC63A80055D639 /* KBTestVC.m */, A1B2D7002EB8C00100000001 /* KBLangTestVC.h */, A1B2D7012EB8C00100000001 /* KBLangTestVC.m */, 0459D1B22EBA284C00F2D189 /* KBSkinCenterVC.h */, 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */, + 04BBF8992F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.h */, + 04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */, + 04BBF89B2F3ACD8800B1FBB2 /* KBTestVC.h */, + 04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.m */, ); path = FunctionTest; sourceTree = ""; @@ -2386,15 +2391,15 @@ 0498BD862EE1BEC9006CC1D5 /* KBSignUtils.m in Sources */, 04791FFC2ED71D17004E8522 /* UIColor+Extension.m in Sources */, 0450AC4A2EF2C3ED00B6AF06 /* KBKeyboardSubscriptionOptionCell.m in Sources */, - 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */, - 048FFD562F2B9C3D005D62AE /* KBChatAssistantCell.m in Sources */, - 048FFD572F2B9C3D005D62AE /* KBChatUserCell.m in Sources */, - A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */, - 04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */, - A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */, - A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */, - A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */, - A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */, + 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */, + 048FFD562F2B9C3D005D62AE /* KBChatAssistantCell.m in Sources */, + 048FFD572F2B9C3D005D62AE /* KBChatUserCell.m in Sources */, + A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */, + 04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */, + A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */, + A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */, + A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */, + A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */, 0419C9662F2C7693002E86D3 /* KBVM.m in Sources */, 048FFD512F2B68F7005D62AE /* KBPersonaModel.m in Sources */, 04FC95792EB09BC8007BD342 /* KBKeyBoardMainView.m in Sources */, @@ -2550,7 +2555,6 @@ 048908D22EBF611D00FABA60 /* KBHistoryMoreCell.m in Sources */, 04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */, 0498BD852EE1B255006CC1D5 /* KBSignUtils.m in Sources */, - 0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */, 04122F7E2EC5FC5500EF7AB3 /* KBJfPayCell.m in Sources */, 048FFD502F2B52E7005D62AE /* AIReportVC.m in Sources */, 049FB2402EC4B6EF00FAB05D /* KBULBridgeNotification.m in Sources */, @@ -2652,6 +2656,8 @@ A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */, A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */, 048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */, + 04BBF89D2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m in Sources */, + 04BBF89E2F3ACD8800B1FBB2 /* KBTestVC.m in Sources */, 0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */, 04286A002ECAEF2B00CE730C /* KBMoneyBtn.m in Sources */, 048908F52EC0496400FABA60 /* KBShopItemVC.m in Sources */, diff --git a/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme b/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme index 802d93f..926a5ea 100644 --- a/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme +++ b/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme @@ -57,8 +57,12 @@ debugServiceExtension = "internal" allowLocationSimulation = "YES" launchAutomaticallySubstyle = "2"> - + + + - + + + + + + +NS_ASSUME_NONNULL_BEGIN + +@interface KBKeyboardStressTestVC : BaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Home/VC/FunctionTest/KBKeyboardStressTestVC.m b/keyBoard/Class/Home/VC/FunctionTest/KBKeyboardStressTestVC.m new file mode 100644 index 0000000..77fb90e --- /dev/null +++ b/keyBoard/Class/Home/VC/FunctionTest/KBKeyboardStressTestVC.m @@ -0,0 +1,142 @@ +#import "KBKeyboardStressTestVC.h" + +@interface KBKeyboardStressTestVC () +@property(nonatomic, strong) UITextView *textView; +@property(nonatomic, strong) UIButton *startButton; +@property(nonatomic, strong) UIButton *stopButton; +@property(nonatomic, strong) UILabel *statusLabel; +@property(nonatomic, assign) NSInteger currentCycle; +@property(nonatomic, assign) NSInteger totalCycles; +@property(nonatomic, assign) BOOL running; +@end + +@implementation KBKeyboardStressTestVC + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = UIColor.whiteColor; + self.title = @"键盘压力测试"; + self.totalCycles = 200; + [self buildUI]; + [self updateStatus]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [self stop]; +} + +- (void)buildUI { + CGFloat w = UIScreen.mainScreen.bounds.size.width; + + self.statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(16, KB_NAV_TOTAL_HEIGHT + 16, w - 32, 22)]; + self.statusLabel.font = [UIFont systemFontOfSize:13]; + self.statusLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1]; + [self.view addSubview:self.statusLabel]; + + self.textView = [[UITextView alloc] initWithFrame:CGRectMake(16, CGRectGetMaxY(self.statusLabel.frame) + 12, w - 32, 160)]; + self.textView.text = @"把系统输入法切到自定义键盘后,点击开始,会反复显示/隐藏键盘。"; + self.textView.layer.borderColor = [UIColor colorWithWhite:0 alpha:0.15].CGColor; + self.textView.layer.borderWidth = 0.5; + self.textView.layer.cornerRadius = 8; + [self.view addSubview:self.textView]; + + CGFloat btnW = (w - 16 * 2 - 12) / 2.0; + self.startButton = [UIButton buttonWithType:UIButtonTypeSystem]; + self.startButton.frame = CGRectMake(16, CGRectGetMaxY(self.textView.frame) + 16, btnW, 44); + self.startButton.layer.cornerRadius = 10; + self.startButton.backgroundColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1]; + [self.startButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; + [self.startButton setTitle:@"开始" forState:UIControlStateNormal]; + [self.startButton addTarget:self action:@selector(onStart) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:self.startButton]; + + self.stopButton = [UIButton buttonWithType:UIButtonTypeSystem]; + self.stopButton.frame = CGRectMake(CGRectGetMaxX(self.startButton.frame) + 12, CGRectGetMinY(self.startButton.frame), btnW, 44); + self.stopButton.layer.cornerRadius = 10; + self.stopButton.backgroundColor = [UIColor colorWithWhite:0.2 alpha:0.08]; + [self.stopButton setTitleColor:[UIColor colorWithWhite:0.15 alpha:1] forState:UIControlStateNormal]; + [self.stopButton setTitle:@"停止" forState:UIControlStateNormal]; + [self.stopButton addTarget:self action:@selector(onStop) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:self.stopButton]; + + UIButton *oneCycleBtn = [UIButton buttonWithType:UIButtonTypeSystem]; + oneCycleBtn.frame = CGRectMake(16, CGRectGetMaxY(self.startButton.frame) + 12, w - 32, 44); + oneCycleBtn.layer.cornerRadius = 10; + oneCycleBtn.backgroundColor = [UIColor colorWithWhite:0.2 alpha:0.08]; + [oneCycleBtn setTitleColor:[UIColor colorWithWhite:0.15 alpha:1] forState:UIControlStateNormal]; + [oneCycleBtn setTitle:@"执行 10 次" forState:UIControlStateNormal]; + [oneCycleBtn addTarget:self action:@selector(onRunTen) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:oneCycleBtn]; +} + +- (void)onStart { + self.totalCycles = 200; + [self start]; +} + +- (void)onRunTen { + self.totalCycles = 10; + [self start]; +} + +- (void)onStop { + [self stop]; +} + +- (void)start { + if (self.running) { + return; + } + self.running = YES; + self.currentCycle = 0; + [self updateStatus]; + [self runNextCycle]; +} + +- (void)stop { + self.running = NO; + [self.textView resignFirstResponder]; + [self updateStatus]; +} + +- (void)runNextCycle { + if (!self.running) { + return; + } + if (self.currentCycle >= self.totalCycles) { + [self stop]; + return; + } + + self.currentCycle += 1; + [self updateStatus]; + + __weak typeof(self) weakSelf = self; + [self.textView becomeFirstResponder]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.18 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) self = weakSelf; + if (!self || !self.running) { + return; + } + [self.textView resignFirstResponder]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) self = weakSelf; + if (!self) { + return; + } + [self runNextCycle]; + }); + }); +} + +- (void)updateStatus { + NSString *run = self.running ? @"运行中" : @"未运行"; + self.statusLabel.text = [NSString stringWithFormat:@"状态:%@ | 进度:%ld/%ld", run, (long)self.currentCycle, (long)self.totalCycles]; + self.startButton.enabled = !self.running; + self.startButton.alpha = self.running ? 0.5 : 1.0; + self.stopButton.enabled = self.running; + self.stopButton.alpha = self.running ? 1.0 : 0.5; +} + +@end