重构
This commit is contained in:
291
CustomKeyboard/View/KBKeyboardView/KBKeyboardLegacyBuilder.m
Normal file
291
CustomKeyboard/View/KBKeyboardView/KBKeyboardLegacyBuilder.m
Normal file
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// KBKeyboardLegacyBuilder.m
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import "KBKeyboardLegacyBuilder.h"
|
||||
#import "KBKey.h"
|
||||
#import "KBKeyButton.h"
|
||||
#import "KBBackspaceLongPressHandler.h"
|
||||
#import "KBConfig.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
static const CGFloat kKBSpecialKeySquareMultiplier = 1.2;
|
||||
static const CGFloat kKBReturnWidthMultiplier = 2.4;
|
||||
static const CGFloat kKBSpaceWidthMultiplier = 3.0;
|
||||
static inline CGFloat KBLegacyRowHorizontalInset(void) {
|
||||
return KBFit(6.0f);
|
||||
}
|
||||
|
||||
@implementation KBKeyboardLegacyBuilder
|
||||
|
||||
- (void)buildRow:(UIView *)row
|
||||
withKeys:(NSArray<KBKey *> *)keys
|
||||
edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier
|
||||
shiftOn:(BOOL)shiftOn
|
||||
backspaceHandler:(KBBackspaceLongPressHandler *)backspaceHandler
|
||||
target:(id)target
|
||||
action:(SEL)action {
|
||||
if (!row || keys.count == 0) { return; }
|
||||
|
||||
// 第 4 行(底部控制行)使用单独的布局规则:
|
||||
// 123/ABC、Emoji、Send 给定尺寸,Space 自动吃掉剩余宽度。
|
||||
BOOL isBottomControlRow = [self kb_isBottomControlRowWithKeys:keys];
|
||||
|
||||
CGFloat spacing = 0; // 键与键之间的间距
|
||||
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(KBLegacyRowHorizontalInset());
|
||||
make.centerY.equalTo(row);
|
||||
make.height.mas_equalTo(1);
|
||||
}];
|
||||
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(row.mas_right).offset(-KBLegacyRowHorizontalInset());
|
||||
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 applyThemeForCurrentKey];
|
||||
if (target && action) {
|
||||
[btn addTarget:target action:action forControlEvents:UIControlEventTouchDown];
|
||||
}
|
||||
[row addSubview:btn];
|
||||
|
||||
if (key.type == KBKeyTypeBackspace) {
|
||||
[backspaceHandler bindDeleteButton:btn showClearLabel:YES];
|
||||
}
|
||||
|
||||
// Shift 按钮选中态随大小写状态变化
|
||||
if (key.type == KBKeyTypeShift) {
|
||||
btn.selected = shiftOn;
|
||||
}
|
||||
|
||||
[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(KBLegacyRowHorizontalInset());
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// 字符键:等宽
|
||||
if (key.type == KBKeyTypeCharacter) {
|
||||
if (previous && [previous isKindOfClass:[KBKeyButton class]]) {
|
||||
KBKeyButton *prevBtn = (KBKeyButton *)previous;
|
||||
if (prevBtn.key.type == KBKeyTypeCharacter) {
|
||||
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(previous);
|
||||
}];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// special keys: 宽度在第二遍统一设置
|
||||
}
|
||||
|
||||
previous = btn;
|
||||
}
|
||||
|
||||
// 右侧使用内边距或右占位
|
||||
if (previous) {
|
||||
[previous mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
if (rightSpacer) {
|
||||
make.right.equalTo(rightSpacer.mas_left).offset(-spacing);
|
||||
} else {
|
||||
make.right.equalTo(row.mas_right).offset(-KBLegacyRowHorizontalInset());
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// 底部控制行:在第一轮已完成左右约束的前提下,仅给 123/ABC、Emoji、Send 指定宽度,
|
||||
// Space 不加宽度约束,让其自动填充剩余空间。
|
||||
if (isBottomControlRow) {
|
||||
[self kb_applyBottomControlRowWidthInRow:row];
|
||||
return;
|
||||
}
|
||||
|
||||
// 第二遍:以首个字符键为基准,统一设置特殊键宽度倍数
|
||||
KBKeyButton *firstChar = nil;
|
||||
BOOL hasCharacterInRow = NO;
|
||||
for (UIView *v in row.subviews) {
|
||||
if (![v isKindOfClass:[KBKeyButton class]]) continue;
|
||||
KBKeyButton *b = (KBKeyButton *)v;
|
||||
if (b.key.type == KBKeyTypeCharacter) {
|
||||
firstChar = b;
|
||||
hasCharacterInRow = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 若该行没有字符键(例如底部控制行之外的特殊行),则使用行内第一个按钮作为基准宽度
|
||||
if (!firstChar) {
|
||||
for (UIView *v in row.subviews) {
|
||||
if ([v isKindOfClass:[KBKeyButton class]]) {
|
||||
firstChar = (KBKeyButton *)v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstChar) {
|
||||
// 如果该行本身没有字符键(如底部控制行),且基准按钮是 123/ABC/#+= 等,
|
||||
// 也将其约束为 1:1,避免 123/ABC 不是正方形。
|
||||
if (!hasCharacterInRow &&
|
||||
(firstChar.key.type == KBKeyTypeModeChange ||
|
||||
firstChar.key.type == KBKeyTypeSymbolsToggle ||
|
||||
firstChar.key.type == KBKeyTypeCustom)) {
|
||||
[firstChar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar.mas_height).multipliedBy(kKBSpecialKeySquareMultiplier);
|
||||
}];
|
||||
}
|
||||
|
||||
for (UIView *v in row.subviews) {
|
||||
if (![v isKindOfClass:[KBKeyButton class]]) continue;
|
||||
KBKeyButton *b = (KBKeyButton *)v;
|
||||
|
||||
// 避免对基准按钮自身添加 self == self * k 的无效约束
|
||||
if (b == firstChar) continue;
|
||||
if (b.key.type == KBKeyTypeCharacter) continue;
|
||||
|
||||
BOOL isBottomModeKey = (b.key.type == KBKeyTypeModeChange) ||
|
||||
(b.key.type == KBKeyTypeSymbolsToggle) ||
|
||||
(b.key.type == KBKeyTypeCustom);
|
||||
// 一类键强制近似正方形(宽 ~ 高)
|
||||
if (b.key.type == KBKeyTypeShift ||
|
||||
b.key.type == KBKeyTypeBackspace ||
|
||||
isBottomModeKey) {
|
||||
[b mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(b.mas_height).multipliedBy(kKBSpecialKeySquareMultiplier);
|
||||
}];
|
||||
continue;
|
||||
}
|
||||
|
||||
CGFloat multiplier = 1.5;
|
||||
|
||||
// Space:宽度更大
|
||||
if (b.key.type == KBKeyTypeSpace) {
|
||||
multiplier = kKBSpaceWidthMultiplier;
|
||||
}
|
||||
// Send 按钮:宽度为基准键的 2.4 倍
|
||||
else if (b.key.type == KBKeyTypeReturn) {
|
||||
multiplier = kKBReturnWidthMultiplier;
|
||||
}
|
||||
// 其它特殊键(如 Globe)保持适度放大
|
||||
else if (b.key.type == KBKeyTypeGlobe) {
|
||||
multiplier = 1.5;
|
||||
}
|
||||
|
||||
[b mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar).multipliedBy(multiplier);
|
||||
}];
|
||||
}
|
||||
|
||||
// 如果有左右占位,则把占位宽度设置为字符键宽度的一定倍数,以实现整体居中;
|
||||
// 同时强约束左右占位宽度相等,避免在某些系统上由于布局冲突导致只压缩一侧,
|
||||
// 出现“左侧有空隙,右侧无空隙”的情况。
|
||||
if (leftSpacer && rightSpacer) {
|
||||
// 1) 左右占位宽度必须相等
|
||||
[leftSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(rightSpacer);
|
||||
}];
|
||||
// 2) 同时都接近字符键宽度的 edgeSpacerMultiplier 倍数
|
||||
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar).multipliedBy(edgeSpacerMultiplier);
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Row Helpers (Bottom Control Row)
|
||||
|
||||
// 判断是否为底部控制行:包含 Space + Return,且有 ModeChange/SymbolsToggle,
|
||||
// 并且不再含字符键。
|
||||
- (BOOL)kb_isBottomControlRowWithKeys:(NSArray<KBKey *> *)keys {
|
||||
BOOL hasSpace = NO;
|
||||
BOOL hasReturn = NO;
|
||||
BOOL hasModeOrSymbols = NO;
|
||||
BOOL hasCharacters = NO;
|
||||
|
||||
for (KBKey *k in keys) {
|
||||
if (k.type == KBKeyTypeSpace) {
|
||||
hasSpace = YES;
|
||||
} else if (k.type == KBKeyTypeReturn) {
|
||||
hasReturn = YES;
|
||||
} else if (k.type == KBKeyTypeModeChange || k.type == KBKeyTypeSymbolsToggle) {
|
||||
hasModeOrSymbols = YES;
|
||||
} else if (k.type == KBKeyTypeCharacter) {
|
||||
hasCharacters = YES;
|
||||
}
|
||||
}
|
||||
return (hasSpace && hasReturn && hasModeOrSymbols && !hasCharacters);
|
||||
}
|
||||
|
||||
- (void)kb_applyBottomControlRowWidthInRow:(UIView *)row {
|
||||
if (!row) { return; }
|
||||
|
||||
KBKeyButton *modeBtn = nil;
|
||||
KBKeyButton *spaceBtn = nil;
|
||||
KBKeyButton *retBtn = nil;
|
||||
NSMutableArray<KBKeyButton *> *customButtons = [NSMutableArray array];
|
||||
|
||||
for (UIView *v in row.subviews) {
|
||||
if (![v isKindOfClass:[KBKeyButton class]]) continue;
|
||||
KBKeyButton *b = (KBKeyButton *)v;
|
||||
switch (b.key.type) {
|
||||
case KBKeyTypeModeChange:
|
||||
case KBKeyTypeSymbolsToggle:
|
||||
modeBtn = b;
|
||||
break;
|
||||
case KBKeyTypeCustom:
|
||||
[customButtons addObject:b];
|
||||
break;
|
||||
case KBKeyTypeSpace:
|
||||
spaceBtn = b;
|
||||
break;
|
||||
case KBKeyTypeReturn:
|
||||
retBtn = b;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modeBtn || customButtons.count == 0 || !spaceBtn || !retBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
[modeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(row.mas_height).multipliedBy(kKBSpecialKeySquareMultiplier);
|
||||
}];
|
||||
for (KBKeyButton *custom in customButtons) {
|
||||
[custom mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(row.mas_height).multipliedBy(kKBSpecialKeySquareMultiplier);
|
||||
}];
|
||||
}
|
||||
[retBtn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(modeBtn.mas_width).multipliedBy(2.0);
|
||||
}];
|
||||
// Space 不设置宽度;通过此前已建立的左右约束自动占满剩余宽度。
|
||||
(void)spaceBtn;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user