Files
keyboard/CustomKeyboard/View/KBKeyboardView.m
2025-10-28 10:18:10 +08:00

256 lines
11 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBKeyboardView.m
// CustomKeyboard
//
#import "KBKeyboardView.h"
#import "KBKeyButton.h"
#import "KBKey.h"
@interface KBKeyboardView ()
@property (nonatomic, strong) UIView *row1;
@property (nonatomic, strong) UIView *row2;
@property (nonatomic, strong) UIView *row3;
@property (nonatomic, strong) UIView *row4;
@property (nonatomic, strong) NSArray<NSArray<KBKey *> *> *keysForRows;
@end
@implementation KBKeyboardView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
_layoutStyle = KBKeyboardLayoutStyleLetters;
_shiftOn = YES; // 初始使用大写,贴近截图效果
[self buildBase];
[self reloadKeys];
}
return self;
}
- (void)buildBase {
[self addSubview:self.row1];
[self addSubview:self.row2];
[self addSubview:self.row3];
[self addSubview:self.row4];
CGFloat vSpacing = 8;
[self.row1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_top).offset(8);
make.left.right.equalTo(self);
make.height.mas_equalTo(44);
}];
[self.row2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.row1.mas_bottom).offset(vSpacing);
make.left.right.equalTo(self);
make.height.equalTo(self.row1);
}];
[self.row3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.row2.mas_bottom).offset(vSpacing);
make.left.right.equalTo(self);
make.height.equalTo(self.row1);
}];
[self.row4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.row3.mas_bottom).offset(vSpacing);
make.left.right.equalTo(self);
make.height.equalTo(self.row1);
make.bottom.equalTo(self.mas_bottom).offset(-6);
}];
}
- (void)reloadKeys {
// 移除旧按钮
for (UIView *row in @[self.row1, self.row2, self.row3, self.row4]) {
[row.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}
self.keysForRows = [self buildKeysForCurrentLayout];
[self buildRow:self.row1 withKeys:self.keysForRows[0]];
// 第二行:字母布局时通过左右等宽占位让整行居中
CGFloat row2Spacer = (self.layoutStyle == KBKeyboardLayoutStyleLetters) ? 0.5 : 0.0;
[self buildRow:self.row2 withKeys:self.keysForRows[1] edgeSpacerMultiplier:row2Spacer];
[self buildRow:self.row3 withKeys:self.keysForRows[2]];
[self buildRow:self.row4 withKeys:self.keysForRows[3]];
}
- (NSArray<NSArray<KBKey *> *> *)buildKeysForCurrentLayout {
if (self.layoutStyle == KBKeyboardLayoutStyleNumbers) {
// 数字/符号布局3 行主键 + 底部控制行
NSArray *r1 = @[
[KBKey keyWithTitle:@"1" output:@"1"], [KBKey keyWithTitle:@"2" output:@"2"], [KBKey keyWithTitle:@"3" output:@"3"],
[KBKey keyWithTitle:@"4" output:@"4"], [KBKey keyWithTitle:@"5" output:@"5"], [KBKey keyWithTitle:@"6" output:@"6"],
[KBKey keyWithTitle:@"7" output:@"7"], [KBKey keyWithTitle:@"8" output:@"8"], [KBKey keyWithTitle:@"9" output:@"9"], [KBKey keyWithTitle:@"0" output:@"0"],
];
NSArray *r2 = @[
[KBKey keyWithTitle:@"-" output:@"-"], [KBKey keyWithTitle:@"/" output:@"/"], [KBKey keyWithTitle:@":" output:@":"],
[KBKey keyWithTitle:@";" output:@";"], [KBKey keyWithTitle:@"(" output:@"("], [KBKey keyWithTitle:@")" output:@")"],
[KBKey keyWithTitle:@"$" output:@"$"], [KBKey keyWithTitle:@"&" output:@"&"], [KBKey keyWithTitle:@"@" output:@"@"], [KBKey keyWithTitle:@"\"" output:@"\""],
];
NSArray *r3 = @[
[KBKey keyWithTitle:@"#+=" type:KBKeyTypeModeChange],
[KBKey keyWithTitle:@"," output:@","], [KBKey keyWithTitle:@"." output:@"."], [KBKey keyWithTitle:@"?" output:@"?"],
[KBKey keyWithTitle:@"!" output:@"!"], [KBKey keyWithTitle:@"'" output:@"'"],
[KBKey keyWithTitle:@"" type:KBKeyTypeBackspace],
];
NSArray *r4 = @[
[KBKey keyWithTitle:@"ABC" type:KBKeyTypeModeChange],
[KBKey keyWithTitle:@"," output:@","],
[KBKey keyWithTitle:@"space" type:KBKeyTypeSpace],
[KBKey keyWithTitle:@"中/英" type:KBKeyTypeCustom],
[KBKey keyWithTitle:@"发送" type:KBKeyTypeReturn],
];
return @[r1, r2, r3, r4];
}
// 字母布局QWERTY
NSArray *r1 = @[ @"Q", @"W", @"E", @"R", @"T", @"Y", @"U", @"I", @"O", @"P" ];
NSArray *r2 = @[ @"A", @"S", @"D", @"F", @"G", @"H", @"J", @"K", @"L" ];
NSArray *r3chars = @[ @"Z", @"X", @"C", @"V", @"B", @"N", @"M" ];
NSMutableArray *row1 = [NSMutableArray arrayWithCapacity:r1.count];
for (NSString *s in r1) { [row1 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
NSMutableArray *row2 = [NSMutableArray arrayWithCapacity:r2.count];
for (NSString *s in r2) { [row2 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
NSMutableArray *row3 = [NSMutableArray array];
[row3 addObject:[KBKey keyWithTitle:@"" type:KBKeyTypeShift]];
for (NSString *s in r3chars) { [row3 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
[row3 addObject:[KBKey keyWithTitle:@"" type:KBKeyTypeBackspace]];
NSArray *row4 = @[ [KBKey keyWithTitle:@"123" type:KBKeyTypeModeChange],
[KBKey keyWithTitle:@"," output:@","],
[KBKey keyWithTitle:@"space" type:KBKeyTypeSpace],
[KBKey keyWithTitle:@"中/英" type:KBKeyTypeCustom],
[KBKey keyWithTitle:@"发送" type:KBKeyTypeReturn] ];
return @[row1.copy, row2.copy, row3.copy, row4];
}
- (void)buildRow:(UIView *)row withKeys:(NSArray<KBKey *> *)keys {
[self buildRow:row withKeys:keys edgeSpacerMultiplier:0.0];
}
- (void)buildRow:(UIView *)row withKeys:(NSArray<KBKey *> *)keys edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
CGFloat hInset = 6; // 行左右内边距
CGFloat spacing = 6; // 键与键之间的间距
UIView *previous = nil;
UIView *leftSpacer = nil;
UIView *rightSpacer = nil;
if (edgeSpacerMultiplier > 0.0) {
leftSpacer = [UIView new];
rightSpacer = [UIView new];
leftSpacer.backgroundColor = [UIColor clearColor];
rightSpacer.backgroundColor = [UIColor clearColor];
[row addSubview:leftSpacer];
[row addSubview:rightSpacer];
[leftSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(row.mas_left).offset(hInset);
make.centerY.equalTo(row);
make.height.mas_equalTo(1);
}];
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(row.mas_right).offset(-hInset);
make.centerY.equalTo(row);
make.height.mas_equalTo(1);
}];
}
for (NSInteger i = 0; i < keys.count; i++) {
KBKey *key = keys[i];
KBKeyButton *btn = [[KBKeyButton alloc] init];
btn.key = key;
[btn setTitle:key.title forState:UIControlStateNormal];
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchUpInside];
[row addSubview:btn];
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(row);
if (previous) {
make.left.equalTo(previous.mas_right).offset(spacing);
} else {
if (leftSpacer) {
make.left.equalTo(leftSpacer.mas_right).offset(spacing);
} else {
make.left.equalTo(row.mas_left).offset(hInset);
}
}
}];
// 宽度规则:字符键等宽;特殊键按倍数放大
if (key.type == KBKeyTypeCharacter) {
if (previous && previous != nil) {
if (((KBKeyButton *)previous).key.type == KBKeyTypeCharacter) {
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(previous);
}];
}
}
} else {
// special keys: give 1.5x of a character key by deferring constraint equalities after loop
}
previous = btn;
}
// 右侧使用内边距或右占位
[previous mas_makeConstraints:^(MASConstraintMaker *make) {
if (rightSpacer) {
make.right.equalTo(rightSpacer.mas_left).offset(-spacing);
} else {
make.right.equalTo(row.mas_right).offset(-hInset);
}
}];
// 第二遍:以首个字符键为基准,统一设置特殊键宽度倍数
KBKeyButton *firstChar = nil;
for (KBKeyButton *b in row.subviews) {
if ([b isKindOfClass:[KBKeyButton class]] && b.key.type == KBKeyTypeCharacter) { firstChar = b; break; }
}
if (firstChar) {
for (KBKeyButton *b in row.subviews) {
if (![b isKindOfClass:[KBKeyButton class]]) continue;
if (b.key.type == KBKeyTypeCharacter) continue;
CGFloat multiplier = 1.5;
if (b.key.type == KBKeyTypeSpace) multiplier = 4.0;
if (b.key.type == KBKeyTypeReturn) multiplier = 1.8;
if (b.key.type == KBKeyTypeModeChange || b.key.type == KBKeyTypeGlobe || b.key.type == KBKeyTypeShift || b.key.type == KBKeyTypeBackspace) {
multiplier = 1.5;
}
[b mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(firstChar).multipliedBy(multiplier);
}];
}
// 如果有左右占位,则把占位宽度设置为字符键宽度的一定倍数,以实现整体居中
if (leftSpacer && rightSpacer) {
[leftSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(firstChar).multipliedBy(edgeSpacerMultiplier);
}];
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(firstChar).multipliedBy(edgeSpacerMultiplier);
}];
}
}
}
#pragma mark - Actions
- (void)onKeyTapped:(KBKeyButton *)sender {
KBKey *key = sender.key;
if (key.type == KBKeyTypeShift) {
self.shiftOn = !self.shiftOn;
[self reloadKeys];
return;
}
if ([self.delegate respondsToSelector:@selector(keyboardView:didTapKey:)]) {
[self.delegate keyboardView:self didTapKey:key];
}
}
#pragma mark - Lazy
- (UIView *)row1 { if (!_row1) _row1 = [UIView new]; return _row1; }
- (UIView *)row2 { if (!_row2) _row2 = [UIView new]; return _row2; }
- (UIView *)row3 { if (!_row3) _row3 = [UIView new]; return _row3; }
- (UIView *)row4 { if (!_row4) _row4 = [UIView new]; return _row4; }
@end