添加按钮文字预览提示
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
@interface KBKeyButton : UIButton
|
||||
|
||||
@property (nonatomic, strong) KBKey *key;
|
||||
@property (nonatomic, strong) UIImageView *iconView;
|
||||
|
||||
/// 配置基础样式(背景、圆角等)。创建按钮时调用。
|
||||
- (void)applyDefaultStyle;
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#import "KBSkinManager.h"
|
||||
|
||||
@interface KBKeyButton ()
|
||||
@property (nonatomic, strong) UIImageView *iconView;
|
||||
// 内部缓存:便于从按钮查找到所属的 KBKeyboardView
|
||||
@property (nonatomic, weak, readonly) UIView *kb_keyboardContainer;
|
||||
@end
|
||||
|
||||
@implementation KBKeyButton
|
||||
@@ -63,10 +64,8 @@
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted {
|
||||
[super setHighlighted:highlighted];
|
||||
|
||||
// 按下时整体做一个等比缩放动画,不改背景色和透明度。
|
||||
// 这样无论是纯文字键还是整块皮肤图,都有统一的“按下”视觉反馈。
|
||||
NSLog(@"来了老弟=====");
|
||||
CGFloat scale = highlighted ? 0.9 : 1.0; // 可根据手感微调 0.9~0.95
|
||||
[UIView animateWithDuration:0.08
|
||||
delay:0
|
||||
@@ -75,6 +74,23 @@
|
||||
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 {
|
||||
@@ -124,3 +140,19 @@
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
21
CustomKeyboard/View/KBKeyPreviewView.h
Normal file
21
CustomKeyboard/View/KBKeyPreviewView.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// KBKeyPreviewView.h
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class KBKey;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 按键按下时显示的气泡预览视图(类似系统键盘上方弹出的放大字母)。
|
||||
@interface KBKeyPreviewView : UIView
|
||||
|
||||
/// 配置预览内容:字符与可选图标。
|
||||
- (void)configureWithKey:(KBKey *)key icon:(nullable UIImage *)icon;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
56
CustomKeyboard/View/KBKeyPreviewView.m
Normal file
56
CustomKeyboard/View/KBKeyPreviewView.m
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// KBKeyPreviewView.m
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import "KBKeyPreviewView.h"
|
||||
#import "KBKey.h"
|
||||
|
||||
@interface KBKeyPreviewView ()
|
||||
@property (nonatomic, strong) UILabel *label;
|
||||
@property (nonatomic, strong) UIImageView *iconView;
|
||||
@end
|
||||
|
||||
@implementation KBKeyPreviewView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor colorWithWhite:1 alpha:1.0];
|
||||
self.layer.cornerRadius = 8.0;
|
||||
self.layer.masksToBounds = YES;
|
||||
self.layer.borderWidth = 0.5;
|
||||
self.layer.borderColor = [UIColor colorWithWhite:0 alpha:0.15].CGColor;
|
||||
self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.3].CGColor;
|
||||
self.layer.shadowOpacity = 0.6;
|
||||
self.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
self.layer.shadowRadius = 4.0;
|
||||
|
||||
// _iconView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
||||
// _iconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
// _iconView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
// [self addSubview:_iconView];
|
||||
|
||||
_label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_label.font = [UIFont systemFontOfSize:28 weight:UIFontWeightSemibold];
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
_label.textColor = [UIColor blackColor];
|
||||
_label.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addSubview:_label];
|
||||
|
||||
// 预览只使用文本:尽量靠近顶部显示,上下留少量安全距离。
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[_label.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:4],
|
||||
[_label.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-4],
|
||||
[_label.topAnchor constraintEqualToAnchor:self.topAnchor constant:2],
|
||||
[_label.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-2],
|
||||
]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureWithKey:(KBKey *)key icon:(UIImage *)icon {
|
||||
NSString *text = key.output.length > 0 ? key.output : key.title;
|
||||
self.label.text = text;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -8,6 +8,7 @@
|
||||
#import "KBKey.h"
|
||||
#import "KBResponderUtils.h" // 封装的响应链工具
|
||||
#import "KBSkinManager.h"
|
||||
#import "KBKeyPreviewView.h"
|
||||
|
||||
@interface KBKeyboardView ()
|
||||
@property (nonatomic, strong) UIView *row1;
|
||||
@@ -17,6 +18,7 @@
|
||||
@property (nonatomic, strong) NSArray<NSArray<KBKey *> *> *keysForRows;
|
||||
// 长按退格的一次次删除控制标记(不使用 NSTimer,仅用 GCD 递归调度)
|
||||
@property (nonatomic, assign) BOOL backspaceHoldActive;
|
||||
@property (nonatomic, strong) KBKeyPreviewView *previewView;
|
||||
@end
|
||||
|
||||
@implementation KBKeyboardView
|
||||
@@ -463,6 +465,53 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 在字符键按下时,显示一个上方气泡预览(类似系统键盘)。
|
||||
- (void)showPreviewForButton:(KBKeyButton *)button {
|
||||
KBKey *key = button.key;
|
||||
if (key.type != KBKeyTypeCharacter) return;
|
||||
|
||||
if (!self.previewView) {
|
||||
self.previewView = [[KBKeyPreviewView alloc] initWithFrame:CGRectZero];
|
||||
self.previewView.hidden = YES;
|
||||
[self addSubview:self.previewView];
|
||||
}
|
||||
|
||||
[self.previewView configureWithKey:key icon:button.iconView.image];
|
||||
|
||||
// 计算预览视图位置:在按钮上方稍微偏上
|
||||
CGRect btnFrameInSelf = [button convertRect:button.bounds toView:self];
|
||||
CGFloat previewWidth = MAX(CGRectGetWidth(btnFrameInSelf) * 1.4, 42.0);
|
||||
CGFloat previewHeight = CGRectGetHeight(btnFrameInSelf) * 1.2;
|
||||
CGFloat centerX = CGRectGetMidX(btnFrameInSelf);
|
||||
CGFloat centerY = CGRectGetMinY(btnFrameInSelf) - previewHeight * 0.6;
|
||||
|
||||
self.previewView.frame = CGRectMake(0, 0, previewWidth, previewHeight);
|
||||
self.previewView.center = CGPointMake(centerX, centerY);
|
||||
self.previewView.alpha = 0.0;
|
||||
self.previewView.hidden = NO;
|
||||
|
||||
[UIView animateWithDuration:0.08
|
||||
delay:0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
self.previewView.alpha = 1.0;
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)hidePreview {
|
||||
if (!self.previewView || self.previewView.isHidden) return;
|
||||
[UIView animateWithDuration:0.06
|
||||
delay:0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
|
||||
animations:^{
|
||||
self.previewView.alpha = 0.0;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
self.previewView.hidden = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
// 长按退格:按住时以小间隔逐个删除;松手停止。(不使用 NSTimer/DisplayLink)
|
||||
- (void)onBackspaceLongPress:(UILongPressGestureRecognizer *)gr {
|
||||
switch (gr.state) {
|
||||
|
||||
Reference in New Issue
Block a user