@@ -10,6 +10,24 @@
# import "KBSkinManager.h"
# import "KBKeyPreviewView.h"
// UI 常 量 统 一 管 理 , 方 便 后 续 调 试 样 式
static const CGFloat kKBRowVerticalSpacing = 8.0 ;
static const CGFloat kKBRowHorizontalInset = 6.0 ;
static const CGFloat kKBRowHeight = 40.0 ;
static const NSTimeInterval kKBBackspaceLongPressMinDuration = 0.35 ;
static const NSTimeInterval kKBBackspaceRepeatInterval = 0.06 ;
static const NSTimeInterval kKBPreviewShowDuration = 0.08 ;
static const NSTimeInterval kKBPreviewHideDuration = 0.06 ;
static const CGFloat kKBSpecialKeySquareMultiplier = 1.2 ;
static const CGFloat kKBReturnWidthMultiplier = 2.4 ;
static const CGFloat kKBSpaceWidthMultiplier = 3.0 ;
// 第 二 行 字 母 行 的 左 右 占 位 比 例 ( 用 于 居 中 )
static const CGFloat kKBLettersRow2EdgeSpacerMultiplier = 0.5 ;
@ interface KBKeyboardView ( )
@ property ( nonatomic , strong ) UIView * row1 ;
@ property ( nonatomic , strong ) UIView * row2 ;
@@ -44,36 +62,39 @@
}
}
# pragma mark - Base Layout
- ( 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 . top . equalTo ( self . mas_top ) . offset ( kKBRowVerticalSpacing ) ;
make . left . right . equalTo ( self ) ;
make . height . mas_equalTo ( 40 ) ;
make . height . mas_equalTo ( kKBRowHeight ) ;
} ] ;
[ self . row2 mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . top . equalTo ( self . row1 . mas_bottom ) . offset ( v Spacing) ;
make . top . equalTo ( self . row1 . mas_bottom ) . offset ( kKBRowVertical Spacing) ;
make . left . right . equalTo ( self ) ;
make . height . equalTo ( self . row1 ) ;
} ] ;
[ self . row3 mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . top . equalTo ( self . row2 . mas_bottom ) . offset ( v Spacing) ;
make . top . equalTo ( self . row2 . mas_bottom ) . offset ( kKBRowVertical Spacing) ;
make . left . right . equalTo ( self ) ;
make . height . equalTo ( self . row1 ) ;
} ] ;
[ self . row4 mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . top . equalTo ( self . row3 . mas_bottom ) . offset ( v Spacing) ;
make . top . equalTo ( self . row3 . mas_bottom ) . offset ( kKBRowVertical Spacing) ;
make . left . right . equalTo ( self ) ;
make . height . equalTo ( self . row1 ) ;
make . bottom . equalTo ( self . mas_bottom ) . offset ( -6 ) ;
} ] ;
}
# pragma mark - Public
- ( void ) reloadKeys {
// 移 除 旧 按 钮
for ( UIView * row in @ [ self . row1 , self . row2 , self . row3 , self . row4 ] ) {
@@ -81,148 +102,49 @@
}
self . keysForRows = [ self buildKeysForCurrentLayout ] ;
if ( self . keysForRows . count < 4 ) return ;
[ self buildRow : self . row1 withKeys : self . keysForRows [ 0 ] ] ;
// 第 二 行 : 字 母 布 局 时 通 过 左 右 等 宽 占 位 让 整 行 居 中
CGFloat row2Spacer = ( self . layoutStyle = = KBKeyboardLayoutStyleLetters ) ? 0.5 : 0.0 ;
CGFloat row2Spacer = ( self . layoutStyle = = KBKeyboardLayoutStyleLetters )
? kKBLettersRow2EdgeSpacerMultiplier : 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 ] ] ;
}
# pragma mark - Key Model Construction
// 创 建 当 前 布 局 下 各 行 的 KBKey 列 表
- ( NSArray < NSArray < KBKey * > * > * ) buildKeysForCurrentLayout {
if ( self . layoutStyle = = KBKeyboardLayoutStyleNumbers ) {
// 数 字 / 符 号 布 局 : 3 行 主 键 + 底 部 控 制 行
NSArray * r1 = nil ;
NSArray * r2 = nil ;
NSArray * r3 = nil ;
if ( ! self . symbolsMoreOn ) {
// 数 字 第 一 页 ( 123 )
r1 = @ [ [ KBKey keyWithIdentifier : @ "digit_1" title : @ "1" output : @ "1" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_2" title : @ "2" output : @ "2" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_3" title : @ "3" output : @ "3" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_4" title : @ "4" output : @ "4" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_5" title : @ "5" output : @ "5" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_6" title : @ "6" output : @ "6" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_7" title : @ "7" output : @ "7" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_8" title : @ "8" output : @ "8" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_9" title : @ "9" output : @ "9" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_0" title : @ "0" output : @ "0" type : KBKeyTypeCharacter ] ] ;
r2 = @ [ [ KBKey keyWithIdentifier : @ "sym_minus" title : @ "-" output : @ "-" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_slash" title : @ "/" output : @ "/" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_colon" title : @ ":" output : @ ":" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_semicolon" title : @ ";" output : @ ";" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_paren_l" title : @ "(" output : @ "(" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_paren_r" title : @ ")" output : @ ")" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_dollar" title : @ "$" output : @ "$" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_amp" title : @ "&" output : @ "&" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_at" title : @ "@" output : @ "@" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_quote_double" title : @ "\" " output:@" \ "" type : KBKeyTypeCharacter ] ] ;
// 第 三 行 : 左 下 角 是 “ # + = ” 切 换 键 , 右 下 角 是 退 格 键
KBKey * toggle = [ KBKey keyWithIdentifier : @ "symbols_toggle_more"
title : @ "#+="
output : @ ""
type : KBKeyTypeSymbolsToggle ] ;
KBKey * comma = [ KBKey keyWithIdentifier : @ "sym_comma" title : @ "," output : @ "," type : KBKeyTypeCharacter ] ;
KBKey * dot = [ KBKey keyWithIdentifier : @ "sym_dot" title : @ "." output : @ "." type : KBKeyTypeCharacter ] ;
KBKey * q = [ KBKey keyWithIdentifier : @ "sym_question" title : @ "?" output : @ "?" type : KBKeyTypeCharacter ] ;
KBKey * ex = [ KBKey keyWithIdentifier : @ "sym_exclam" title : @ "!" output : @ "!" type : KBKeyTypeCharacter ] ;
KBKey * quote = [ KBKey keyWithIdentifier : @ "sym_quote_single" title : @ "'" output : @ "'" type : KBKeyTypeCharacter ] ;
KBKey * back = [ KBKey keyWithIdentifier : @ "backspace"
title : @ "⌫"
output : @ ""
type : KBKeyTypeBackspace ] ;
r3 = @ [ toggle , comma , dot , q , ex , quote , back ] ;
} else {
// 数 字 第 二 页 ( # + = ) : 前 两 行 替 换 为 更 多 符 号 , 左 下 角 按 钮 文 案 改 为 “ 123 ”
r1 = @ [ [ KBKey keyWithIdentifier : @ "sym_bracket_l" title : @ "[" output : @ "[" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_bracket_r" title : @ "]" output : @ "]" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_brace_l" title : @ "{" output : @ "{" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_brace_r" title : @ "}" output : @ "}" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_hash" title : @ "#" output : @ "#" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_percent" title : @ "%" output : @ "%" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_caret" title : @ "^" output : @ "^" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_asterisk" title : @ "*" output : @ "*" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_plus" title : @ "+" output : @ "+" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_equal" title : @ "=" output : @ "=" type : KBKeyTypeCharacter ] ] ;
r2 = @ [ [ KBKey keyWithIdentifier : @ "sym_underscore" title : @ "_" output : @ "_" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_backslash" title : @ "\\" output : @ "\\" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_pipe" title : @ "|" output : @ "|" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_tilde" title : @ "~" output : @ "~" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_lt" title : @ "<" output : @ "<" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_gt" title : @ ">" output : @ ">" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_dollar" title : @ "$" output : @ "$" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_euro" title : @ "€" output : @ "€" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_pound" title : @ "£" output : @ "£" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_bullet" title : @ "•" output : @ "•" type : KBKeyTypeCharacter ] ] ;
KBKey * toggle = [ KBKey keyWithIdentifier : @ "symbols_toggle_123"
title : @ "123"
output : @ ""
type : KBKeyTypeSymbolsToggle ] ;
KBKey * comma = [ KBKey keyWithIdentifier : @ "sym_comma" title : @ "," output : @ "," type : KBKeyTypeCharacter ] ;
KBKey * dot = [ KBKey keyWithIdentifier : @ "sym_dot" title : @ "." output : @ "." type : KBKeyTypeCharacter ] ;
KBKey * q = [ KBKey keyWithIdentifier : @ "sym_question" title : @ "?" output : @ "?" type : KBKeyTypeCharacter ] ;
KBKey * ex = [ KBKey keyWithIdentifier : @ "sym_exclam" title : @ "!" output : @ "!" type : KBKeyTypeCharacter ] ;
KBKey * quote = [ KBKey keyWithIdentifier : @ "sym_quote_single" title : @ "'" output : @ "'" type : KBKeyTypeCharacter ] ;
KBKey * back = [ KBKey keyWithIdentifier : @ "backspace"
title : @ "⌫"
output : @ ""
type : KBKeyTypeBackspace ] ;
r3 = @ [ toggle , comma , dot , q , ex , quote , back ] ;
}
KBKey * modeABC = [ KBKey keyWithIdentifier : @ "mode_abc"
title : @ "abc"
output : @ ""
type : KBKeyTypeModeChange ] ;
KBKey * customAI = [ KBKey keyWithIdentifier : @ "ai"
title : @ "AI"
output : @ ""
type : KBKeyTypeCustom ] ;
KBKey * space = [ KBKey keyWithIdentifier : @ "space"
title : @ "space"
output : @ " "
type : KBKeyTypeSpace ] ;
KBKey * ret = [ KBKey keyWithIdentifier : @ "return"
title : KBLocalized ( @ "Send" )
output : @ "\n"
type : KBKeyTypeReturn ] ;
NSArray * r4 = @ [ modeABC , customAI , space , ret ] ;
return @ [ r1 , r2 , r3 , r4 ] ;
return [ self buildKeysForNumbersLayout ] ;
} else {
return [ self buildKeysForLettersLayout ] ;
}
}
# pragma mark - Letters Layout
- ( NSArray < NSArray < KBKey * > * > * ) buildKeysForLettersLayout {
// 字 母 布 局 ( 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 * r3cha rs = @ [ @ "Z " , @ "X " , @ "C " , @ "V " , @ "B " , @ "N " , @ "M " ] ;
NSArray * r1Letters = @ [ @ "q " , @ "w " , @ "e " , @ "r " , @ "t " , @ "y " , @ "u " , @ "i " , @ "o " , @ "p " ] ;
NSArray * r2Letters = @ [ @ "a " , @ "s " , @ "d " , @ "f " , @ "g " , @ "h " , @ "j " , @ "k " , @ "l " ] ;
NSArray * r3Lette rs = @ [ @ "z " , @ "x " , @ "c " , @ "v " , @ "b " , @ "n " , @ "m " ] ;
NSMutableArray * row1 = [ NSMutableArray arrayWithCapacity : r1 . count ] ;
// 字 母 键 标 题 与 输 出 同 时 随 Shift 切 换 大 小 写 , 界 面 与 输 入 保 持 一 致
for ( NSString * s in r1 ) {
NSString * shown = self . shiftOn ? s : s . lowercaseString ;
NSString * identifier = [ NSString stringWithFormat : @ "letter_%@" , s . lowercaseString ] ;
KBKey * k = [ KBKey keyWithIdentifier : identifier
title : shown
output : shown
type : KBKeyTypeCharacter ] ;
k . caseVariant = self . shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower ;
[ row1 addObject : k ] ;
NSMutableArray * row1 = [ NSMutableArray arrayWithCapacity : r1Letters . count ] ;
for ( NSString * s in r1Letters ) {
[ row1 addObject : [ self kb_letterKeyWithChar : s ] ] ;
}
NSMutableArray * row2 = [ NSMutableArray arrayWithCapacity : r2 . count ] ;
for ( NSString * s in r2 ) {
NSString * shown = self . shiftOn ? s : s . lowercaseString ;
NSString * identifier = [ NSString stringWithFormat : @ "letter_%@" , s . lowercaseString ] ;
KBKey * k = [ KBKey keyWithIdentifier : identifier
title : shown
output : shown
type : KBKeyTypeCharacter ] ;
k . caseVariant = self . shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower ;
[ row2 addObject : k ] ;
NSMutableArray * row2 = [ NSMutableArray arrayWithCapacity : r2Letters . count ] ;
for ( NSString * s in r2Letters ) {
[ row2 addObject : [ self kb_letterKeyWithChar : s ] ] ;
}
// 第 三 行 : Shift + Z . . . M + Backspace
NSMutableArray * row3 = [ NSMutableArray array ] ;
KBKey * shift = [ KBKey keyWithIdentifier : @ "shift"
title : @ "⇧"
@@ -233,22 +155,126 @@
// - shift . caseVariant = Upper 时 , 使 用 "shift_upper" 对 应 的 短 名 。
shift . caseVariant = self . shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower ;
[ row3 addObject : shift ] ;
for ( NSString * s in r3chars ) {
NSString * shown = self . shiftOn ? s : s . lowercaseString ;
NSString * identifier = [ NSString stringWithFormat : @ "letter_%@" , s . lowercaseString ] ;
KBKey * k = [ KBKey keyWithIdentifier : identifier
title : shown
output : shown
type : KBKeyTypeCharacter ] ;
k . caseVariant = self . shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower ;
[ row3 addObject : k ] ;
for ( NSString * s in r3Letters ) {
[ row3 addObject : [ self kb_letterKeyWithChar : s ] ] ;
}
KBKey * backspace = [ KBKey keyWithIdentifier : @ "backspace"
title : @ "⌫"
output : @ ""
type : KBKeyTypeBackspace ] ;
[ row3 addObject : backspace ] ;
NSArray * row4 = [ self kb_bottomControlRowKeysForLettersLayout ] ;
return @ [ row1 . copy , row2 . copy , row3 . copy , row4 ] ;
}
# pragma mark - Numbers / Symbols Layout
- ( NSArray < NSArray < KBKey * > * > * ) buildKeysForNumbersLayout {
// 数 字 / 符 号 布 局 : 3 行 主 键 + 底 部 控 制 行
NSArray * r1 = nil ;
NSArray * r2 = nil ;
NSArray * r3 = nil ;
if ( ! self . symbolsMoreOn ) {
// 数 字 第 一 页 ( 123 )
r1 = @ [ [ KBKey keyWithIdentifier : @ "digit_1" title : @ "1" output : @ "1" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_2" title : @ "2" output : @ "2" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_3" title : @ "3" output : @ "3" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_4" title : @ "4" output : @ "4" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_5" title : @ "5" output : @ "5" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_6" title : @ "6" output : @ "6" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_7" title : @ "7" output : @ "7" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_8" title : @ "8" output : @ "8" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_9" title : @ "9" output : @ "9" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "digit_0" title : @ "0" output : @ "0" type : KBKeyTypeCharacter ] ] ;
r2 = @ [ [ KBKey keyWithIdentifier : @ "sym_minus" title : @ "-" output : @ "-" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_slash" title : @ "/" output : @ "/" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_colon" title : @ ":" output : @ ":" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_semicolon" title : @ ";" output : @ ";" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_paren_l" title : @ "(" output : @ "(" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_paren_r" title : @ ")" output : @ ")" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_dollar" title : @ "$" output : @ "$" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_amp" title : @ "&" output : @ "&" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_at" title : @ "@" output : @ "@" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_quote_double" title : @ "\" " output:@" \ "" type : KBKeyTypeCharacter ] ] ;
r3 = [ self kb_symbolsCommonThirdRowWithToggleIsMore : NO ] ;
} else {
// 数 字 第 二 页 ( # + = ) : 前 两 行 替 换 为 更 多 符 号 , 左 下 角 按 钮 文 案 改 为 “ 123 ”
r1 = @ [ [ KBKey keyWithIdentifier : @ "sym_bracket_l" title : @ "[" output : @ "[" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_bracket_r" title : @ "]" output : @ "]" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_brace_l" title : @ "{" output : @ "{" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_brace_r" title : @ "}" output : @ "}" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_hash" title : @ "#" output : @ "#" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_percent" title : @ "%" output : @ "%" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_caret" title : @ "^" output : @ "^" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_asterisk" title : @ "*" output : @ "*" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_plus" title : @ "+" output : @ "+" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_equal" title : @ "=" output : @ "=" type : KBKeyTypeCharacter ] ] ;
r2 = @ [ [ KBKey keyWithIdentifier : @ "sym_underscore" title : @ "_" output : @ "_" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_backslash" title : @ "\\" output : @ "\\" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_pipe" title : @ "|" output : @ "|" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_tilde" title : @ "~" output : @ "~" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_lt" title : @ "<" output : @ "<" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_gt" title : @ ">" output : @ ">" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_dollar" title : @ "$" output : @ "$" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_euro" title : @ "€" output : @ "€" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_pound" title : @ "£" output : @ "£" type : KBKeyTypeCharacter ] ,
[ KBKey keyWithIdentifier : @ "sym_bullet" title : @ "•" output : @ "•" type : KBKeyTypeCharacter ] ] ;
r3 = [ self kb_symbolsCommonThirdRowWithToggleIsMore : YES ] ;
}
NSArray * r4 = [ self kb_bottomControlRowKeysForNumbersLayout ] ;
return @ [ r1 , r2 , r3 , r4 ] ;
}
# pragma mark - Key Factories
// 字 母 键 工 厂 : 根 据 shiftOn 决 定 显 示 与 输 出 大 小 写
- ( KBKey * ) kb_letterKeyWithChar : ( NSString * ) charString {
NSParameterAssert ( charString . length = = 1 ) ;
NSString * lower = charString . lowercaseString ;
NSString * upper = charString . uppercaseString ;
NSString * shown = self . shiftOn ? upper : lower ;
NSString * identifier = [ NSString stringWithFormat : @ "letter_%@" , lower ] ;
KBKey * k = [ KBKey keyWithIdentifier : identifier
title : shown
output : shown
type : KBKeyTypeCharacter ] ;
k . caseVariant = self . shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower ;
return k ;
}
// 数 字 布 局 第 三 行 公 共 部 分 ( 左 下 角 是 123 或 # + = )
- ( NSArray < KBKey * > * ) kb_symbolsCommonThirdRowWithToggleIsMore : ( BOOL ) isMorePage {
NSString * identifier = isMorePage ? @ "symbols_toggle_123" : @ "symbols_toggle_more" ;
NSString * title = isMorePage ? @ "123" : @ "#+=" ;
KBKey * toggle = [ KBKey keyWithIdentifier : identifier
title : title
output : @ ""
type : KBKeyTypeSymbolsToggle ] ;
KBKey * comma = [ KBKey keyWithIdentifier : @ "sym_comma" title : @ "," output : @ "," type : KBKeyTypeCharacter ] ;
KBKey * dot = [ KBKey keyWithIdentifier : @ "sym_dot" title : @ "." output : @ "." type : KBKeyTypeCharacter ] ;
KBKey * q = [ KBKey keyWithIdentifier : @ "sym_question" title : @ "?" output : @ "?" type : KBKeyTypeCharacter ] ;
KBKey * ex = [ KBKey keyWithIdentifier : @ "sym_exclam" title : @ "!" output : @ "!" type : KBKeyTypeCharacter ] ;
KBKey * quote = [ KBKey keyWithIdentifier : @ "sym_quote_single" title : @ "'" output : @ "'" type : KBKeyTypeCharacter ] ;
KBKey * back = [ KBKey keyWithIdentifier : @ "backspace"
title : @ "⌫"
output : @ ""
type : KBKeyTypeBackspace ] ;
return @ [ toggle , comma , dot , q , ex , quote , back ] ;
}
// 底 部 控 制 行 ( 字 母 布 局 )
- ( NSArray < KBKey * > * ) kb_bottomControlRowKeysForLettersLayout {
KBKey * mode123 = [ KBKey keyWithIdentifier : @ "mode_123"
title : @ "123"
output : @ ""
@@ -265,26 +291,49 @@
title : KBLocalized ( @ "Send" )
output : @ "\n"
type : KBKeyTypeReturn ] ;
NSArray * row4 = @ [ mode123 , customAI , space , ret ] ;
return @ [ row1 . copy , row2 . copy , row3 . copy , row4 ] ;
return @ [ mode123 , customAI , space , ret ] ;
}
// 底 部 控 制 行 ( 数 字 布 局 )
- ( NSArray < KBKey * > * ) kb_bottomControlRowKeysForNumbersLayout {
KBKey * modeABC = [ KBKey keyWithIdentifier : @ "mode_abc"
title : @ "abc"
output : @ ""
type : KBKeyTypeModeChange ] ;
KBKey * customAI = [ KBKey keyWithIdentifier : @ "ai"
title : @ "AI"
output : @ ""
type : KBKeyTypeCustom ] ;
KBKey * space = [ KBKey keyWithIdentifier : @ "space"
title : @ "space"
output : @ " "
type : KBKeyTypeSpace ] ;
KBKey * ret = [ KBKey keyWithIdentifier : @ "return"
title : KBLocalized ( @ "Send" )
output : @ "\n"
type : KBKeyTypeReturn ] ;
return @ [ modeABC , customAI , space , ret ] ;
}
# pragma mark - Row Building
- ( 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 {
- ( void ) buildRow : ( UIView * ) row
withKeys : ( NSArray < KBKey * > * ) keys
edgeSpacerMultiplier : ( CGFloat ) edgeSpacerMultiplier {
// 第 4 行 ( 底 部 控 制 行 ) 使 用 单 独 的 布 局 规 则 :
// 123 / ABC 、 AI 、 Send 给 定 尺 寸 , Space 自 动 吃 掉 剩 余 宽 度 。
BOOL isBottomControlRow = [ self kb_isBottomControlRowWithKeys : keys ] ;
CGFloat hInset = 6 ; // 行 左 右 内 边 距
CGFloat spacing = 0 ; // 键 与 键 之 间 的 间 距
UIView * previous = nil ;
UIView * leftSpacer = nil ;
UIView * rightSpacer = nil ;
if ( edgeSpacerMultiplier > 0.0 ) {
leftSpacer = [ UIView new ] ;
rightSpacer = [ UIView new ] ;
@@ -293,16 +342,17 @@
[ row addSubview : leftSpacer ] ;
[ row addSubview : rightSpacer ] ;
[ leftSpacer mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . left . equalTo ( row . mas_left ) . offset ( h Inset) ;
make . left . equalTo ( row . mas_left ) . offset ( kKBRowHorizontal Inset) ;
make . centerY . equalTo ( row ) ;
make . height . mas_equalTo ( 1 ) ;
} ] ;
[ rightSpacer mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . right . equalTo ( row . mas_right ) . offset ( - h Inset) ;
make . right . equalTo ( row . mas_right ) . offset ( - kKBRowHorizontal Inset) ;
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 ] ;
@@ -313,12 +363,12 @@
[ btn addTarget : self action : @ selector ( onKeyTapped : ) forControlEvents : UIControlEventTouchUpInside ] ;
[ row addSubview : btn ] ;
// ⌫ 长 按 : 开 始 连 续 逐 个 删 除 ( 无 需 NSTimer ) 。 使 用 UILongPressGestureRecognizer 识 别 长 按 ,
// 在 开 始 态 触 发 递 归 的 轻 量 调 度 , 每 次 删 除 1 个 字 符 , 直 到 松 手 或 无 内 容 。
// ⌫ 长 按 : 开 始 连 续 逐 个 删 除 ( 无 需 NSTimer ) 。 使 用 UILongPressGestureRecognizer 识 别 长 按
if ( key . type = = KBKeyTypeBackspace ) {
UILongPressGestureRecognizer * lp = [ [ UILongPressGestureRecognizer alloc ] initWithTarget : self action : @ selector ( onBackspaceLongPress : ) ] ;
// 稍 短 的 判 定 时 间 , 提 升 响 应 ( 默 认 约 0.5 s ) 。
lp . minimumPressDuration = 0.35 ;
UILongPressGestureRecognizer * lp =
[ [ UILongPressGestureRecognizer alloc ] initWithTarget : self
action : @ selector ( onBackspaceLongPress : ) ] ;
lp . minimumPressDuration = kKBBackspaceLongPressMinDuration ;
lp . cancelsTouchesInView = YES ; // 被 识 别 为 长 按 时 , 取 消 普 通 点 击
[ btn addGestureRecognizer : lp ] ;
}
@@ -336,35 +386,38 @@
if ( leftSpacer ) {
make . left . equalTo ( leftSpacer . mas_right ) . offset ( spacing ) ;
} else {
make . left . equalTo ( row . mas_left ) . offset ( h Inset) ;
make . left . equalTo ( row . mas_left ) . offset ( kKBRowHorizontal Inset) ;
}
}
} ] ;
// 宽 度 规 则 : 字 符 键 等 宽 ; 特 殊 键 按 倍 数 放 大
// 字 符 键 : 等 宽
if ( key . type = = KBKeyTypeCharacter ) {
if ( previous && previous ! = nil ) {
if ( ( ( KBKeyButton * ) previous ) . 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 : give 1.5 x of a character key by deferring constraint equalities after loop
// special keys : 宽 度 在 第 二 遍 统 一 设 置
}
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 ) ;
}
}] ;
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 ( - kKBRowHorizontalInset ) ;
}
} ] ;
}
// 底 部 控 制 行 : 在 第 一 轮 已 完 成 左 右 约 束 的 前 提 下 , 仅 给 123 / ABC 、 AI 、 Send 指 定 宽 度 ,
// Space 不 加 宽 度 约 束 , 让 其 自 动 填 充 剩 余 空 间 。
@@ -376,19 +429,20 @@
// 第 二 遍 : 以 首 个 字 符 键 为 基 准 , 统 一 设 置 特 殊 键 宽 度 倍 数
KBKeyButton * firstChar = nil ;
BOOL hasCharacterInRow = NO ;
for ( KBKeyButton * b in row . subviews ) {
if ( ! [ b isKindOfClass : [ KBKeyButton class ] ] ) continue ;
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 ( KBKeyButton * b in row . subviews ) {
if ( [ b isKindOfClass : [ KBKeyButton class ] ] ) {
firstChar = b ;
for ( UIView * v in row . subviews ) {
if ( [ v isKindOfClass : [ KBKeyButton class ] ] ) {
firstChar = ( KBKeyButton * ) v ;
break ;
}
}
@@ -401,41 +455,40 @@
firstChar . key . type = = KBKeyTypeSymbolsToggle ||
firstChar . key . type = = KBKeyTypeCustom ) ) {
[ firstChar mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . width . equalTo ( firstChar . mas_height ) ;
make . width . equalTo ( firstChar . mas_height ) . multipliedBy ( kKBSpecialKeySquareMultiplier ) ;
} ] ;
}
for ( KBKeyButton * b in row . subviews ) {
if ( ! [ b isKindOfClass : [ KBKeyButton class ] ] ) continue ;
// 当 本 行 没 有 字 符 键 时 , firstChar 可 能 是 一 个 “ 特 殊 键 ” ,
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 ;
// 一 类 键 强 制 宽 高 比 1 : 1 :
// - 第 三 行 : Shift 、 Backspace
// - 第 四 行 : 123 / ABC ( ModeChange ) 、 # + = ( SymbolsToggle ) 、 AI ( Custom )
// ( 真 正 的 底 部 控 制 行 在 上 面 已 单 独 处 理 , 这 里 主 要 服 务 于 第 三 行 的 # + = 等 )
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 ( 1.2 ) ; // 宽 度 = 高 度 , 做 成 正 方 形
make . width . equalTo ( b . mas_height ) . multipliedBy ( kKBSpecialKeySquareMultiplier ) ;
} ] ;
continue ;
}
CGFloat multiplier = 1.5 ;
// Space : 根 据 行 宽 可 压 缩 , 适 当 缩 小 宽度 ( 原 来 是 4.0 , 略 微 调 低 到 3.0 )
// Space : 宽 度 更 大
if ( b . key . type = = KBKeyTypeSpace ) {
multiplier = 3.0 ;
multiplier = kKBSpaceWidthMultiplier ;
}
// Send 按 钮 : 宽 度 为 基 准 键 的 2.4 倍
else if ( b . key . type = = KBKeyTypeReturn ) {
multiplier = 2.4 ;
multiplier = kKBReturnWidthMultiplier ;
}
// 其 它 特 殊 键 ( 如 Globe ) 保 持 适 度 放 大
else if ( b . key . type = = KBKeyTypeGlobe ) {
@@ -446,6 +499,7 @@
make . width . equalTo ( firstChar ) . multipliedBy ( multiplier ) ;
} ] ;
}
// 如 果 有 左 右 占 位 , 则 把 占 位 宽 度 设 置 为 字 符 键 宽 度 的 一 定 倍 数 , 以 实 现 整 体 居 中
if ( leftSpacer && rightSpacer ) {
[ leftSpacer mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
@@ -482,8 +536,8 @@
}
// 为 底 部 控 制 行 设 置 宽 度 :
// - 123 / ABC 、 AI : 正 方 形 ( 宽 = 行 高 )
// - Send : 宽 = 模 式 键 宽 度 的 2.4 倍
// - 123 / ABC 、 AI : 正 方 形 ( 宽 = 行 高 * multiplier )
// - Send : 宽 = 模 式 键 宽 度 的 2 倍
// - Space : 不 加 宽 度 约 束 , 依 靠 左 右 约 束 自 动 填 充 剩 余 空 间 。
- ( void ) kb_applyBottomControlRowWidthInRow : ( UIView * ) row {
KBKeyButton * modeBtn = nil ;
@@ -519,14 +573,14 @@
// 行 高 由 外 部 约 束 为 固 定 值 ( 等 于 row1 高 度 ) , 这 里 用 行 高 作 为 “ 正 方 形 ” 的 边 长 。
[ modeBtn mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . width . equalTo ( row . mas_height ) . multipliedBy ( 1.2 ) ;
make . width . equalTo ( row . mas_height ) . multipliedBy ( kKBSpecialKeySquareMultiplier ) ;
} ] ;
[ aiBtn mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
make . width . equalTo ( row . mas_height ) . multipliedBy ( 1.2 ) ;
make . width . equalTo ( row . mas_height ) . multipliedBy ( kKBSpecialKeySquareMultiplier ) ;
} ] ;
[ retBtn mas_makeConstraints : ^ ( MASConstraintMaker * make ) {
// Send 按 钮 : 宽 度 为 模 式 键 的 2.4 倍
make . width . equalTo ( modeBtn . mas_width ) . multipliedBy ( 2 ) ;
// Send 按 钮 : 宽 度 为 模 式 键 的 2 倍
make . width . equalTo ( modeBtn . mas_width ) . multipliedBy ( 2.0 ) ;
} ] ;
// Space 不 设 置 宽 度 ; 通 过 此 前 已 建 立 的 左 右 约 束 自 动 占 满 剩 余 宽 度 。
}
@@ -571,12 +625,13 @@
CGFloat centerX = CGRectGetMidX ( btnFrameInSelf ) ;
CGFloat centerY = CGRectGetMinY ( btnFrameInSelf ) - previewHeight * 0.6 ;
self . previewView . frame = CGRectMake ( 0 , 0 , 40 , previewHeight ) ;
// 修 复 : 原 来 写 死 40 , 这 里 用 真 正 计 算 出 的 previewWidth
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
[ UIView animateWithDuration : kKBPreviewShowDuration
delay : 0
options : UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut
animations : ^ {
@@ -587,7 +642,7 @@
- ( void ) hidePreview {
if ( ! self . previewView || self . previewView . isHidden ) return ;
[ UIView animateWithDuration : 0.06
[ UIView animateWithDuration : kKBPreviewHideDuration
delay : 0
options : UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations : ^ {
@@ -627,7 +682,9 @@
[ proxy deleteBackward ] ; // 每 次 仅 删 1 个
__weak typeof ( self ) weakSelf = self ;
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 0.06 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW ,
( int64_t ) ( kKBBackspaceRepeatInterval * NSEC_PER _SEC ) ) ,
dispatch_get _main _queue ( ) , ^ {
__strong typeof ( weakSelf ) selfStrong = weakSelf ;
[ selfStrong kb_backspaceStep ] ;
} ) ;