// // KBKeyButton.m // CustomKeyboard // #import "KBKeyButton.h" #import "KBKey.h" #import "KBSkinManager.h" @interface KBKeyButton () // 内部缓存:便于从按钮查找到所属的 KBKeyboardView @property (nonatomic, weak, readonly) UIView *kb_keyboardContainer; @end @implementation KBKeyButton - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self applyDefaultStyle]; } return self; } - (void)applyDefaultStyle { self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold]; KBSkinTheme *t = [KBSkinManager shared].current; [self setTitleColor:t.keyTextColor forState:UIControlStateNormal]; [self setTitleColor:t.keyTextColor forState:UIControlStateHighlighted]; self.backgroundColor = t.keyBackground; self.layer.cornerRadius = 6.0; // 圆角 self.layer.masksToBounds = NO; self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor; // 阴影效果 self.layer.shadowOpacity = 1.0; self.layer.shadowOffset = CGSizeMake(0, 1); self.layer.shadowRadius = 1.5; [self refreshStateAppearance]; // 懒创建图标视图,用于后续皮肤按键小图标展示 if (!self.iconView) { UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectZero]; // 作为按键的整块皮肤背景,铺满整个按钮区域 iv.contentMode = UIViewContentModeScaleToFill; iv.clipsToBounds = YES; iv.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:iv]; // 让皮肤图片撑满整个按钮 [NSLayoutConstraint activateConstraints:@[ [iv.topAnchor constraintEqualToAnchor:self.topAnchor], [iv.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], [iv.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:2], [iv.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-2], ]]; self.iconView = iv; // 文字保持居中;若需要显示文字,则覆盖在皮肤图片之上 self.titleEdgeInsets = UIEdgeInsetsZero; [self bringSubviewToFront:self.titleLabel]; } } - (void)setKey:(KBKey *)key { _key = key; } - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; // 按下时整体做一个等比缩放动画,不改背景色和透明度。 // 这样无论是纯文字键还是整块皮肤图,都有统一的“按下”视觉反馈。 CGFloat scale = highlighted ? 0.9 : 1.0; // 可根据手感微调 0.9~0.95 [UIView animateWithDuration:0.08 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:^{ self.transform = CGAffineTransformMakeScale(scale, scale); } completion:nil]; // 将“按下/抬起”事件转发给键盘视图,用于显示/隐藏顶部预览气泡。 UIView *container = self.kb_keyboardContainer; if ([container respondsToSelector:@selector(showPreviewForButton:)] && [container respondsToSelector:@selector(hidePreview)]) { if (highlighted) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [container performSelector:@selector(showPreviewForButton:) withObject:self]; #pragma clang diagnostic pop } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [container performSelector:@selector(hidePreview)]; #pragma clang diagnostic pop } } } - (void)setSelected:(BOOL)selected { [super setSelected:selected]; [self refreshStateAppearance]; } - (void)refreshStateAppearance { // 选中态用于 Shift/CapsLock 等特殊按键的高亮显示 KBSkinTheme *t = [KBSkinManager shared].current; if (self.isSelected) { self.backgroundColor = t.keyHighlightBackground ?: t.keyBackground; } else { self.backgroundColor = t.keyBackground; } } - (void)applyThemeForCurrentKey { // 根据皮肤映射加载图标(若有),支持大小写变体: // - identifier: 逻辑按键标识(如 letter_q) // - caseVariant: 0/1/2 => 无变体/小写/大写 NSString *identifier = self.key.identifier; NSInteger variant = (NSInteger)self.key.caseVariant; UIImage *iconImg = [[KBSkinManager shared] iconImageForKeyIdentifier:identifier caseVariant:variant]; // 设置整块按键背景图(若有) self.iconView.image = iconImg; self.iconView.hidden = (iconImg == nil); BOOL hasIcon = (iconImg != nil); if (hasIcon) { // 有图标:仅显示图片,完全隐藏文字 [self setTitle:@"" forState:UIControlStateNormal]; [self setTitle:@"" forState:UIControlStateHighlighted]; [self setTitle:@"" forState:UIControlStateSelected]; self.titleLabel.hidden = YES; KBSkinTheme *t = [KBSkinManager shared].current; t.keyBackground = [UIColor clearColor]; } else { // 无图标:按键标题正常显示(使用 key.title),并根据 hidden_keys 决定要不要隐藏 [self setTitle:self.key.title forState:UIControlStateNormal]; BOOL hideTextBySkin = [[KBSkinManager shared] shouldHideKeyTextForIdentifier:identifier]; self.titleLabel.hidden = hideTextBySkin; } } @end @implementation KBKeyButton (KBKeyboardContainer) - (UIView *)kb_keyboardContainer { UIView *v = self.superview; while (v) { // KBKeyboardView 是当前容器类型,这里用类名字符串避免直接引用头文件死循环 if ([NSStringFromClass(v.class) isEqualToString:@"KBKeyboardView"]) { return v; } v = v.superview; } return nil; } @end