1
This commit is contained in:
@@ -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<KBChatMessage *> *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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user