添加联想
This commit is contained in:
@@ -20,14 +20,15 @@
|
|||||||
#import "KBKeyboardSubscriptionView.h"
|
#import "KBKeyboardSubscriptionView.h"
|
||||||
#import "KBKeyboardSubscriptionProduct.h"
|
#import "KBKeyboardSubscriptionProduct.h"
|
||||||
#import "KBBackspaceUndoManager.h"
|
#import "KBBackspaceUndoManager.h"
|
||||||
|
#import "KBSuggestionEngine.h"
|
||||||
|
|
||||||
// 提前声明一个类别,使编译器在 static 回调中识别 kb_consumePendingShopSkin 方法。
|
// 提前声明一个类别,使编译器在 static 回调中识别 kb_consumePendingShopSkin 方法。
|
||||||
@interface KeyboardViewController (KBSkinShopBridge)
|
@interface KeyboardViewController (KBSkinShopBridge)
|
||||||
- (void)kb_consumePendingShopSkin;
|
- (void)kb_consumePendingShopSkin;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// 以 375 宽设计稿为基准的键盘总高度(包括顶部工具栏)
|
// 以 375 宽设计稿为基准的键盘总高度
|
||||||
static const CGFloat kKBKeyboardDesignHeight = 250.0f;
|
static const CGFloat kKBKeyboardBaseHeight = 250.0f;
|
||||||
|
|
||||||
static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||||
void *observer,
|
void *observer,
|
||||||
@@ -50,6 +51,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
@property (nonatomic, strong) KBSettingView *settingView; // 设置页
|
@property (nonatomic, strong) KBSettingView *settingView; // 设置页
|
||||||
@property (nonatomic, strong) UIImageView *bgImageView; // 背景图(在底层)
|
@property (nonatomic, strong) UIImageView *bgImageView; // 背景图(在底层)
|
||||||
@property (nonatomic, strong) KBKeyboardSubscriptionView *subscriptionView;
|
@property (nonatomic, strong) KBKeyboardSubscriptionView *subscriptionView;
|
||||||
|
@property (nonatomic, strong) KBSuggestionEngine *suggestionEngine;
|
||||||
|
@property (nonatomic, copy) NSString *currentWord;
|
||||||
|
@property (nonatomic, assign) BOOL suppressSuggestions;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation KeyboardViewController
|
@implementation KeyboardViewController
|
||||||
@@ -61,6 +65,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
[self setupUI];
|
[self setupUI];
|
||||||
|
self.suggestionEngine = [KBSuggestionEngine shared];
|
||||||
|
self.currentWord = @"";
|
||||||
// 指定 HUD 的承载视图(扩展里无法取到 App 的 KeyWindow)
|
// 指定 HUD 的承载视图(扩展里无法取到 App 的 KeyWindow)
|
||||||
[KBHUD setContainerView:self.view];
|
[KBHUD setContainerView:self.view];
|
||||||
// 绑定完全访问管理器,便于统一感知和联动网络开关
|
// 绑定完全访问管理器,便于统一感知和联动网络开关
|
||||||
@@ -94,7 +100,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
self.view.translatesAutoresizingMaskIntoConstraints = NO;
|
self.view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||||
|
|
||||||
// 按屏幕宽度对设计值做等比缩放,避免在不同机型上键盘整体高度失真导致皮肤被压缩/拉伸
|
// 按屏幕宽度对设计值做等比缩放,避免在不同机型上键盘整体高度失真导致皮肤被压缩/拉伸
|
||||||
CGFloat keyboardHeight = KBFit(kKBKeyboardDesignHeight);
|
CGFloat keyboardHeight = KBFit(kKBKeyboardBaseHeight);
|
||||||
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
||||||
CGFloat outerVerticalInset = KBFit(4.0f);
|
CGFloat outerVerticalInset = KBFit(4.0f);
|
||||||
|
|
||||||
@@ -137,6 +143,113 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
|
|
||||||
|
// MARK: - Suggestions
|
||||||
|
|
||||||
|
- (void)kb_updateCurrentWordWithInsertedText:(NSString *)text {
|
||||||
|
if (text.length == 0) { return; }
|
||||||
|
if ([self kb_isAlphabeticString:text]) {
|
||||||
|
NSString *current = self.currentWord ?: @"";
|
||||||
|
self.currentWord = [current stringByAppendingString:text];
|
||||||
|
self.suppressSuggestions = NO;
|
||||||
|
[self kb_updateSuggestionsForCurrentWord];
|
||||||
|
} else {
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)kb_clearCurrentWord {
|
||||||
|
self.currentWord = @"";
|
||||||
|
[self.keyBoardMainView kb_setSuggestions:@[]];
|
||||||
|
self.suppressSuggestions = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)kb_scheduleContextRefreshResetSuppression:(BOOL)resetSuppression {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self kb_refreshCurrentWordFromDocumentContextResetSuppression:resetSuppression];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)kb_refreshCurrentWordFromDocumentContextResetSuppression:(BOOL)resetSuppression {
|
||||||
|
NSString *context = self.textDocumentProxy.documentContextBeforeInput ?: @"";
|
||||||
|
NSString *word = [self kb_extractTrailingWordFromContext:context];
|
||||||
|
self.currentWord = word ?: @"";
|
||||||
|
if (resetSuppression) {
|
||||||
|
self.suppressSuggestions = NO;
|
||||||
|
}
|
||||||
|
[self kb_updateSuggestionsForCurrentWord];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)kb_extractTrailingWordFromContext:(NSString *)context {
|
||||||
|
if (context.length == 0) { return @""; }
|
||||||
|
static NSCharacterSet *letters = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
letters = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
|
||||||
|
});
|
||||||
|
|
||||||
|
NSInteger idx = (NSInteger)context.length - 1;
|
||||||
|
while (idx >= 0) {
|
||||||
|
unichar ch = [context characterAtIndex:(NSUInteger)idx];
|
||||||
|
if (![letters characterIsMember:ch]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
NSUInteger start = (NSUInteger)(idx + 1);
|
||||||
|
if (start >= context.length) { return @""; }
|
||||||
|
return [context substringFromIndex:start];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)kb_isAlphabeticString:(NSString *)text {
|
||||||
|
if (text.length == 0) { return NO; }
|
||||||
|
static NSCharacterSet *letters = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
letters = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
|
||||||
|
});
|
||||||
|
for (NSUInteger i = 0; i < text.length; i++) {
|
||||||
|
if (![letters characterIsMember:[text characterAtIndex:i]]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)kb_updateSuggestionsForCurrentWord {
|
||||||
|
NSString *prefix = self.currentWord ?: @"";
|
||||||
|
if (prefix.length == 0) {
|
||||||
|
[self.keyBoardMainView kb_setSuggestions:@[]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self.suppressSuggestions) {
|
||||||
|
[self.keyBoardMainView kb_setSuggestions:@[]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSArray<NSString *> *items = [self.suggestionEngine suggestionsForPrefix:prefix limit:5];
|
||||||
|
NSArray<NSString *> *cased = [self kb_applyCaseToSuggestions:items prefix:prefix];
|
||||||
|
[self.keyBoardMainView kb_setSuggestions:cased];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)kb_applyCaseToSuggestions:(NSArray<NSString *> *)items prefix:(NSString *)prefix {
|
||||||
|
if (items.count == 0 || prefix.length == 0) { return items; }
|
||||||
|
BOOL allUpper = [prefix isEqualToString:prefix.uppercaseString];
|
||||||
|
BOOL firstUpper = [[prefix substringToIndex:1] isEqualToString:[[prefix substringToIndex:1] uppercaseString]];
|
||||||
|
|
||||||
|
if (!allUpper && !firstUpper) { return items; }
|
||||||
|
|
||||||
|
NSMutableArray<NSString *> *result = [NSMutableArray arrayWithCapacity:items.count];
|
||||||
|
for (NSString *word in items) {
|
||||||
|
if (allUpper) {
|
||||||
|
[result addObject:word.uppercaseString];
|
||||||
|
} else {
|
||||||
|
NSString *first = [[word substringToIndex:1] uppercaseString];
|
||||||
|
NSString *rest = (word.length > 1) ? [word substringFromIndex:1] : @"";
|
||||||
|
[result addObject:[first stringByAppendingString:rest]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.copy;
|
||||||
|
}
|
||||||
|
|
||||||
/// 切换显示功能面板/键盘主视图
|
/// 切换显示功能面板/键盘主视图
|
||||||
- (void)showFunctionPanel:(BOOL)show {
|
- (void)showFunctionPanel:(BOOL)show {
|
||||||
// 简单显隐切换,复用相同的布局区域
|
// 简单显隐切换,复用相同的布局区域
|
||||||
@@ -254,19 +367,29 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
||||||
}
|
}
|
||||||
switch (key.type) {
|
switch (key.type) {
|
||||||
case KBKeyTypeCharacter:
|
case KBKeyTypeCharacter: {
|
||||||
[self.textDocumentProxy insertText:key.output ?: key.title ?: @""]; break;
|
NSString *text = key.output ?: key.title ?: @"";
|
||||||
|
[self.textDocumentProxy insertText:text];
|
||||||
|
[self kb_updateCurrentWordWithInsertedText:text];
|
||||||
|
} break;
|
||||||
case KBKeyTypeBackspace:
|
case KBKeyTypeBackspace:
|
||||||
[self.textDocumentProxy deleteBackward]; break;
|
[self.textDocumentProxy deleteBackward];
|
||||||
|
[self kb_scheduleContextRefreshResetSuppression:NO];
|
||||||
|
break;
|
||||||
case KBKeyTypeSpace:
|
case KBKeyTypeSpace:
|
||||||
[self.textDocumentProxy insertText:@" "]; break;
|
[self.textDocumentProxy insertText:@" "];
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
|
break;
|
||||||
case KBKeyTypeReturn:
|
case KBKeyTypeReturn:
|
||||||
[self.textDocumentProxy insertText:@"\n"]; break;
|
[self.textDocumentProxy insertText:@"\n"];
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
|
break;
|
||||||
case KBKeyTypeGlobe:
|
case KBKeyTypeGlobe:
|
||||||
[self advanceToNextInputMode]; break;
|
[self advanceToNextInputMode]; break;
|
||||||
case KBKeyTypeCustom:
|
case KBKeyTypeCustom:
|
||||||
// 点击自定义键切换到功能面板
|
// 点击自定义键切换到功能面板
|
||||||
[self showFunctionPanel:YES];
|
[self showFunctionPanel:YES];
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
break;
|
break;
|
||||||
case KBKeyTypeModeChange:
|
case KBKeyTypeModeChange:
|
||||||
case KBKeyTypeShift:
|
case KBKeyTypeShift:
|
||||||
@@ -278,6 +401,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didTapToolActionAtIndex:(NSInteger)index {
|
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didTapToolActionAtIndex:(NSInteger)index {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
[self showFunctionPanel:YES];
|
[self showFunctionPanel:YES];
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self showFunctionPanel:NO];
|
[self showFunctionPanel:NO];
|
||||||
@@ -291,16 +415,33 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
|||||||
if (emoji.length == 0) { return; }
|
if (emoji.length == 0) { return; }
|
||||||
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
||||||
[self.textDocumentProxy insertText:emoji];
|
[self.textDocumentProxy insertText:emoji];
|
||||||
|
[self kb_clearCurrentWord];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyBoardMainViewDidTapUndo:(KBKeyBoardMainView *)keyBoardMainView {
|
- (void)keyBoardMainViewDidTapUndo:(KBKeyBoardMainView *)keyBoardMainView {
|
||||||
[[KBBackspaceUndoManager shared] performUndoFromResponder:self.view];
|
[[KBBackspaceUndoManager shared] performUndoFromResponder:self.view];
|
||||||
|
[self kb_scheduleContextRefreshResetSuppression:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyBoardMainViewDidTapEmojiSearch:(KBKeyBoardMainView *)keyBoardMainView {
|
- (void)keyBoardMainViewDidTapEmojiSearch:(KBKeyBoardMainView *)keyBoardMainView {
|
||||||
[KBHUD showInfo:KBLocalized(@"Search coming soon")];
|
[KBHUD showInfo:KBLocalized(@"Search coming soon")];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didSelectSuggestion:(NSString *)suggestion {
|
||||||
|
if (suggestion.length == 0) { return; }
|
||||||
|
NSString *current = self.currentWord ?: @"";
|
||||||
|
if (current.length > 0) {
|
||||||
|
for (NSUInteger i = 0; i < current.length; i++) {
|
||||||
|
[self.textDocumentProxy deleteBackward];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[self.textDocumentProxy insertText:suggestion];
|
||||||
|
self.currentWord = suggestion;
|
||||||
|
[self.suggestionEngine recordSelection:suggestion];
|
||||||
|
self.suppressSuggestions = YES;
|
||||||
|
[self.keyBoardMainView kb_setSuggestions:@[]];
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - KBFunctionViewDelegate
|
// MARK: - KBFunctionViewDelegate
|
||||||
- (void)functionView:(KBFunctionView *)functionView didTapToolActionAtIndex:(NSInteger)index {
|
- (void)functionView:(KBFunctionView *)functionView didTapToolActionAtIndex:(NSInteger)index {
|
||||||
// 需求:当 index == 0 时,切回键盘主视图
|
// 需求:当 index == 0 时,切回键盘主视图
|
||||||
|
|||||||
23
CustomKeyboard/Manager/KBSuggestionEngine.h
Normal file
23
CustomKeyboard/Manager/KBSuggestionEngine.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// KBSuggestionEngine.h
|
||||||
|
// CustomKeyboard
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Simple local suggestion engine (prefix match + lightweight ranking).
|
||||||
|
@interface KBSuggestionEngine : NSObject
|
||||||
|
|
||||||
|
+ (instancetype)shared;
|
||||||
|
|
||||||
|
/// Returns suggestions for prefix (lowercase expected), limited by count.
|
||||||
|
- (NSArray<NSString *> *)suggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit;
|
||||||
|
|
||||||
|
/// Record a selection to slightly boost ranking next time.
|
||||||
|
- (void)recordSelection:(NSString *)word;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
167
CustomKeyboard/Manager/KBSuggestionEngine.m
Normal file
167
CustomKeyboard/Manager/KBSuggestionEngine.m
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
//
|
||||||
|
// KBSuggestionEngine.m
|
||||||
|
// CustomKeyboard
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "KBSuggestionEngine.h"
|
||||||
|
#import "KBConfig.h"
|
||||||
|
|
||||||
|
@interface KBSuggestionEngine ()
|
||||||
|
@property (nonatomic, copy) NSArray<NSString *> *words;
|
||||||
|
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *selectionCounts;
|
||||||
|
@property (nonatomic, strong) NSSet<NSString *> *priorityWords;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation KBSuggestionEngine
|
||||||
|
|
||||||
|
+ (instancetype)shared {
|
||||||
|
static KBSuggestionEngine *engine;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
engine = [[KBSuggestionEngine alloc] init];
|
||||||
|
});
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_selectionCounts = [NSMutableDictionary dictionary];
|
||||||
|
NSArray<NSString *> *defaults = [self.class kb_defaultWords];
|
||||||
|
_priorityWords = [NSSet setWithArray:defaults];
|
||||||
|
_words = [self kb_loadWords];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)suggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||||
|
if (prefix.length == 0 || limit == 0) { return @[]; }
|
||||||
|
NSString *lower = prefix.lowercaseString;
|
||||||
|
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||||
|
|
||||||
|
for (NSString *word in self.words) {
|
||||||
|
if ([word hasPrefix:lower]) {
|
||||||
|
[matches addObject:word];
|
||||||
|
if (matches.count >= limit * 3) {
|
||||||
|
// Avoid scanning too many matches for long lists.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.count == 0) { return @[]; }
|
||||||
|
|
||||||
|
[matches sortUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
|
||||||
|
NSInteger ca = self.selectionCounts[a].integerValue;
|
||||||
|
NSInteger cb = self.selectionCounts[b].integerValue;
|
||||||
|
if (ca != cb) {
|
||||||
|
return (cb > ca) ? NSOrderedAscending : NSOrderedDescending;
|
||||||
|
}
|
||||||
|
BOOL pa = [self.priorityWords containsObject:a];
|
||||||
|
BOOL pb = [self.priorityWords containsObject:b];
|
||||||
|
if (pa != pb) {
|
||||||
|
return pa ? NSOrderedAscending : NSOrderedDescending;
|
||||||
|
}
|
||||||
|
return [a compare:b];
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (matches.count > limit) {
|
||||||
|
return [matches subarrayWithRange:NSMakeRange(0, limit)];
|
||||||
|
}
|
||||||
|
return matches.copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)recordSelection:(NSString *)word {
|
||||||
|
if (word.length == 0) { return; }
|
||||||
|
NSString *key = word.lowercaseString;
|
||||||
|
NSInteger count = self.selectionCounts[key].integerValue + 1;
|
||||||
|
self.selectionCounts[key] = @(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Defaults
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)kb_loadWords {
|
||||||
|
NSMutableOrderedSet<NSString *> *set = [[NSMutableOrderedSet alloc] init];
|
||||||
|
[set addObjectsFromArray:[self.class kb_defaultWords]];
|
||||||
|
|
||||||
|
NSArray<NSString *> *paths = [self kb_wordListPaths];
|
||||||
|
for (NSString *path in paths) {
|
||||||
|
if (path.length == 0) { continue; }
|
||||||
|
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
||||||
|
if (content.length == 0) { continue; }
|
||||||
|
NSArray<NSString *> *lines = [content componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||||
|
for (NSString *line in lines) {
|
||||||
|
NSString *word = [self kb_sanitizedWordFromLine:line];
|
||||||
|
if (word.length == 0) { continue; }
|
||||||
|
[set addObject:word];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray<NSString *> *result = set.array ?: @[];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)kb_wordListPaths {
|
||||||
|
NSMutableArray<NSString *> *paths = [NSMutableArray array];
|
||||||
|
// 1) App Group override (allows server-downloaded large list).
|
||||||
|
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||||
|
if (containerURL.path.length > 0) {
|
||||||
|
NSString *groupPath = [[containerURL path] stringByAppendingPathComponent:@"kb_words.txt"];
|
||||||
|
[paths addObject:groupPath];
|
||||||
|
}
|
||||||
|
// 2) Bundle fallback.
|
||||||
|
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"kb_words" ofType:@"txt"];
|
||||||
|
if (bundlePath.length > 0) {
|
||||||
|
[paths addObject:bundlePath];
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)kb_sanitizedWordFromLine:(NSString *)line {
|
||||||
|
NSString *trimmed = [[line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString];
|
||||||
|
if (trimmed.length == 0) { return @""; }
|
||||||
|
static NSCharacterSet *letters = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
letters = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyz"];
|
||||||
|
});
|
||||||
|
for (NSUInteger i = 0; i < trimmed.length; i++) {
|
||||||
|
if (![letters characterIsMember:[trimmed characterAtIndex:i]]) {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSArray<NSString *> *)kb_defaultWords {
|
||||||
|
return @[
|
||||||
|
@"a", @"an", @"and", @"are", @"as", @"at",
|
||||||
|
@"app", @"ap", @"apple", @"apply", @"april", @"application",
|
||||||
|
@"about", @"above", @"after", @"again", @"against", @"all",
|
||||||
|
@"am", @"among", @"amount", @"any", @"around",
|
||||||
|
@"be", @"because", @"been", @"before", @"being", @"below",
|
||||||
|
@"best", @"between", @"both", @"but", @"by",
|
||||||
|
@"can", @"could", @"come", @"common", @"case",
|
||||||
|
@"do", @"does", @"down", @"day",
|
||||||
|
@"each", @"early", @"end", @"even", @"every",
|
||||||
|
@"for", @"from", @"first", @"found", @"free",
|
||||||
|
@"get", @"good", @"great", @"go",
|
||||||
|
@"have", @"has", @"had", @"help", @"how",
|
||||||
|
@"in", @"is", @"it", @"if", @"into",
|
||||||
|
@"just", @"keep", @"kind", @"know",
|
||||||
|
@"like", @"look", @"long", @"last",
|
||||||
|
@"make", @"more", @"most", @"my",
|
||||||
|
@"new", @"no", @"not", @"now",
|
||||||
|
@"of", @"on", @"one", @"or", @"other", @"our", @"out",
|
||||||
|
@"people", @"place", @"please",
|
||||||
|
@"quick", @"quite",
|
||||||
|
@"right", @"read", @"real",
|
||||||
|
@"see", @"say", @"some", @"such", @"so",
|
||||||
|
@"the", @"to", @"this", @"that", @"them", @"then", @"there", @"they", @"these", @"time",
|
||||||
|
@"use", @"up", @"under",
|
||||||
|
@"very",
|
||||||
|
@"we", @"with", @"what", @"when", @"where", @"who", @"why", @"will", @"would",
|
||||||
|
@"you", @"your"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
234454
CustomKeyboard/Resource/kb_words.txt
Normal file
234454
CustomKeyboard/Resource/kb_words.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
/// emoji 面板点击搜索
|
/// emoji 面板点击搜索
|
||||||
- (void)keyBoardMainViewDidTapEmojiSearch:(KBKeyBoardMainView *)keyBoardMainView;
|
- (void)keyBoardMainViewDidTapEmojiSearch:(KBKeyBoardMainView *)keyBoardMainView;
|
||||||
|
|
||||||
|
/// 选择了联想词
|
||||||
|
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didSelectSuggestion:(NSString *)suggestion;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface KBKeyBoardMainView : UIView
|
@interface KBKeyBoardMainView : UIView
|
||||||
@@ -39,6 +42,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/// 应用当前皮肤(会触发键区重载以应用按键颜色)
|
/// 应用当前皮肤(会触发键区重载以应用按键颜色)
|
||||||
- (void)kb_applyTheme;
|
- (void)kb_applyTheme;
|
||||||
|
|
||||||
|
/// 更新联想候选
|
||||||
|
- (void)kb_setSuggestions:(NSArray<NSString *> *)suggestions;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -11,14 +11,17 @@
|
|||||||
#import "KBFunctionView.h"
|
#import "KBFunctionView.h"
|
||||||
#import "KBKey.h"
|
#import "KBKey.h"
|
||||||
#import "KBEmojiPanelView.h"
|
#import "KBEmojiPanelView.h"
|
||||||
|
#import "KBSuggestionBarView.h"
|
||||||
#import "Masonry.h"
|
#import "Masonry.h"
|
||||||
#import "KBSkinManager.h"
|
#import "KBSkinManager.h"
|
||||||
|
|
||||||
@interface KBKeyBoardMainView ()<KBToolBarDelegate, KBKeyboardViewDelegate, KBEmojiPanelViewDelegate>
|
@interface KBKeyBoardMainView ()<KBToolBarDelegate, KBKeyboardViewDelegate, KBEmojiPanelViewDelegate, KBSuggestionBarViewDelegate>
|
||||||
@property (nonatomic, strong) KBToolBar *topBar;
|
@property (nonatomic, strong) KBToolBar *topBar;
|
||||||
|
@property (nonatomic, strong) KBSuggestionBarView *suggestionBar;
|
||||||
@property (nonatomic, strong) KBKeyboardView *keyboardView;
|
@property (nonatomic, strong) KBKeyboardView *keyboardView;
|
||||||
@property (nonatomic, strong) KBEmojiPanelView *emojiView;
|
@property (nonatomic, strong) KBEmojiPanelView *emojiView;
|
||||||
@property (nonatomic, assign) BOOL emojiPanelVisible;
|
@property (nonatomic, assign) BOOL emojiPanelVisible;
|
||||||
|
@property (nonatomic, assign) BOOL suggestionBarHasItems;
|
||||||
// 注意:功能面板的展示/隐藏由外部控制器决定,此处不再直接管理显隐
|
// 注意:功能面板的展示/隐藏由外部控制器决定,此处不再直接管理显隐
|
||||||
@end
|
@end
|
||||||
@implementation KBKeyBoardMainView
|
@implementation KBKeyBoardMainView
|
||||||
@@ -31,9 +34,17 @@
|
|||||||
self.topBar = [[KBToolBar alloc] init];
|
self.topBar = [[KBToolBar alloc] init];
|
||||||
self.topBar.delegate = self;
|
self.topBar.delegate = self;
|
||||||
[self addSubview:self.topBar];
|
[self addSubview:self.topBar];
|
||||||
|
|
||||||
|
// 联想栏
|
||||||
|
self.suggestionBar = [[KBSuggestionBarView alloc] init];
|
||||||
|
self.suggestionBar.delegate = self;
|
||||||
|
self.suggestionBar.hidden = YES;
|
||||||
|
[self addSubview:self.suggestionBar];
|
||||||
|
|
||||||
// 键盘区域(高度按照设计值做等比缩放,避免不同机型上按键被压缩/拉伸)
|
// 键盘区域(高度按照设计值做等比缩放,避免不同机型上按键被压缩/拉伸)
|
||||||
CGFloat keyboardAreaHeight = KBFit(200.0f);
|
CGFloat keyboardAreaHeight = KBFit(200.0f);
|
||||||
CGFloat bottomInset = KBFit(4.0f);
|
CGFloat bottomInset = KBFit(4.0f);
|
||||||
|
CGFloat topBarHeight = KBFit(40.0f);
|
||||||
CGFloat barSpacing = KBFit(6.0f);
|
CGFloat barSpacing = KBFit(6.0f);
|
||||||
|
|
||||||
self.keyboardView = [[KBKeyboardView alloc] init];
|
self.keyboardView = [[KBKeyboardView alloc] init];
|
||||||
@@ -57,7 +68,17 @@
|
|||||||
[self.topBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.topBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.right.equalTo(self);
|
make.left.right.equalTo(self);
|
||||||
make.top.equalTo(self.mas_top).offset(0);
|
make.top.equalTo(self.mas_top).offset(0);
|
||||||
make.bottom.equalTo(self.keyboardView.mas_top).offset(0);
|
make.height.mas_equalTo(topBarHeight);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.suggestionBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.left.right.equalTo(self);
|
||||||
|
make.top.equalTo(self.topBar);
|
||||||
|
make.bottom.equalTo(self.topBar);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.keyboardView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.topBar.mas_bottom).offset(barSpacing);
|
||||||
}];
|
}];
|
||||||
// 功能面板切换交由外部控制器处理;此处不直接创建/管理
|
// 功能面板切换交由外部控制器处理;此处不直接创建/管理
|
||||||
}
|
}
|
||||||
@@ -74,17 +95,24 @@
|
|||||||
} else {
|
} else {
|
||||||
self.keyboardView.hidden = NO;
|
self.keyboardView.hidden = NO;
|
||||||
self.topBar.hidden = NO;
|
self.topBar.hidden = NO;
|
||||||
|
self.suggestionBar.hidden = !self.suggestionBarHasItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
void (^changes)(void) = ^{
|
void (^changes)(void) = ^{
|
||||||
self.emojiView.alpha = visible ? 1.0 : 0.0;
|
self.emojiView.alpha = visible ? 1.0 : 0.0;
|
||||||
self.keyboardView.alpha = visible ? 0.0 : 1.0;
|
self.keyboardView.alpha = visible ? 0.0 : 1.0;
|
||||||
self.topBar.alpha = visible ? 0.0 : 1.0;
|
self.topBar.alpha = visible ? 0.0 : 1.0;
|
||||||
|
self.suggestionBar.alpha = visible ? 0.0 : (self.suggestionBarHasItems ? 1.0 : 0.0);
|
||||||
};
|
};
|
||||||
void (^completion)(BOOL) = ^(BOOL finished) {
|
void (^completion)(BOOL) = ^(BOOL finished) {
|
||||||
self.emojiView.hidden = !visible;
|
self.emojiView.hidden = !visible;
|
||||||
self.keyboardView.hidden = visible;
|
self.keyboardView.hidden = visible;
|
||||||
self.topBar.hidden = visible;
|
self.topBar.hidden = visible;
|
||||||
|
if (visible) {
|
||||||
|
self.suggestionBar.hidden = YES;
|
||||||
|
} else {
|
||||||
|
self.suggestionBar.hidden = !self.suggestionBarHasItems;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (animated) {
|
if (animated) {
|
||||||
@@ -211,10 +239,34 @@
|
|||||||
if ([self.topBar respondsToSelector:@selector(kb_applyTheme)]) {
|
if ([self.topBar respondsToSelector:@selector(kb_applyTheme)]) {
|
||||||
[self.topBar kb_applyTheme];
|
[self.topBar kb_applyTheme];
|
||||||
}
|
}
|
||||||
|
[self.suggestionBar applyTheme:mgr.current];
|
||||||
[self.keyboardView reloadKeys];
|
[self.keyboardView reloadKeys];
|
||||||
if (self.emojiView) {
|
if (self.emojiView) {
|
||||||
[self.emojiView applyTheme:mgr.current];
|
[self.emojiView applyTheme:mgr.current];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Suggestions
|
||||||
|
|
||||||
|
- (void)kb_setSuggestions:(NSArray<NSString *> *)suggestions {
|
||||||
|
self.suggestionBarHasItems = (suggestions.count > 0);
|
||||||
|
[self.suggestionBar updateSuggestions:suggestions];
|
||||||
|
|
||||||
|
if (self.emojiPanelVisible) {
|
||||||
|
self.suggestionBar.hidden = YES;
|
||||||
|
self.suggestionBar.alpha = 0.0;
|
||||||
|
} else {
|
||||||
|
self.suggestionBar.hidden = !self.suggestionBarHasItems;
|
||||||
|
self.suggestionBar.alpha = self.suggestionBarHasItems ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - KBSuggestionBarViewDelegate
|
||||||
|
|
||||||
|
- (void)suggestionBarView:(KBSuggestionBarView *)view didSelectSuggestion:(NSString *)suggestion {
|
||||||
|
if ([self.delegate respondsToSelector:@selector(keyBoardMainView:didSelectSuggestion:)]) {
|
||||||
|
[self.delegate keyBoardMainView:self didSelectSuggestion:suggestion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
26
CustomKeyboard/View/KBSuggestionBarView.h
Normal file
26
CustomKeyboard/View/KBSuggestionBarView.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// KBSuggestionBarView.h
|
||||||
|
// CustomKeyboard
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class KBSuggestionBarView;
|
||||||
|
@class KBSkinTheme;
|
||||||
|
|
||||||
|
@protocol KBSuggestionBarViewDelegate <NSObject>
|
||||||
|
- (void)suggestionBarView:(KBSuggestionBarView *)view didSelectSuggestion:(NSString *)suggestion;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface KBSuggestionBarView : UIView
|
||||||
|
|
||||||
|
@property (nonatomic, weak) id<KBSuggestionBarViewDelegate> delegate;
|
||||||
|
|
||||||
|
- (void)updateSuggestions:(NSArray<NSString *> *)suggestions;
|
||||||
|
- (void)applyTheme:(KBSkinTheme *)theme;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
114
CustomKeyboard/View/KBSuggestionBarView.m
Normal file
114
CustomKeyboard/View/KBSuggestionBarView.m
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
//
|
||||||
|
// KBSuggestionBarView.m
|
||||||
|
// CustomKeyboard
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "KBSuggestionBarView.h"
|
||||||
|
#import "Masonry.h"
|
||||||
|
#import "KBSkinManager.h"
|
||||||
|
|
||||||
|
@interface KBSuggestionBarView ()
|
||||||
|
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||||
|
@property (nonatomic, strong) UIStackView *stackView;
|
||||||
|
@property (nonatomic, copy) NSArray<NSString *> *items;
|
||||||
|
@property (nonatomic, strong) UIColor *pillColor;
|
||||||
|
@property (nonatomic, strong) UIColor *textColor;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation KBSuggestionBarView
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame {
|
||||||
|
if (self = [super initWithFrame:frame]) {
|
||||||
|
self.backgroundColor = [UIColor clearColor];
|
||||||
|
[self setupUI];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupUI {
|
||||||
|
[self addSubview:self.scrollView];
|
||||||
|
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.edges.equalTo(self);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.scrollView addSubview:self.stackView];
|
||||||
|
[self.stackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.edges.equalTo(self.scrollView).insets(UIEdgeInsetsMake(0, 8, 0, 8));
|
||||||
|
make.height.equalTo(self.scrollView);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self applyTheme:[KBSkinManager shared].current];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateSuggestions:(NSArray<NSString *> *)suggestions {
|
||||||
|
self.items = suggestions ?: @[];
|
||||||
|
|
||||||
|
for (UIView *view in self.stackView.arrangedSubviews) {
|
||||||
|
[self.stackView removeArrangedSubview:view];
|
||||||
|
[view removeFromSuperview];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSString *item in self.items) {
|
||||||
|
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||||
|
btn.layer.cornerRadius = 12.0;
|
||||||
|
btn.layer.masksToBounds = YES;
|
||||||
|
btn.backgroundColor = self.pillColor ?: [UIColor colorWithWhite:1 alpha:0.9];
|
||||||
|
btn.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||||||
|
[btn setTitle:item forState:UIControlStateNormal];
|
||||||
|
[btn setTitleColor:self.textColor ?: [UIColor blackColor] forState:UIControlStateNormal];
|
||||||
|
btn.contentEdgeInsets = UIEdgeInsetsMake(4, 10, 4, 10);
|
||||||
|
[btn addTarget:self action:@selector(onTapSuggestion:) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
[self.stackView addArrangedSubview:btn];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hidden = (self.items.count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applyTheme:(KBSkinTheme *)theme {
|
||||||
|
UIColor *bg = theme.keyBackground ?: [UIColor whiteColor];
|
||||||
|
UIColor *text = theme.keyTextColor ?: [UIColor blackColor];
|
||||||
|
UIColor *barBg = theme.keyboardBackground ?: [UIColor colorWithWhite:0.95 alpha:1.0];
|
||||||
|
self.backgroundColor = barBg;
|
||||||
|
self.pillColor = bg;
|
||||||
|
self.textColor = text;
|
||||||
|
|
||||||
|
for (UIView *view in self.stackView.arrangedSubviews) {
|
||||||
|
if (![view isKindOfClass:[UIButton class]]) { continue; }
|
||||||
|
UIButton *btn = (UIButton *)view;
|
||||||
|
btn.backgroundColor = self.pillColor;
|
||||||
|
[btn setTitleColor:self.textColor forState:UIControlStateNormal];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
- (void)onTapSuggestion:(UIButton *)sender {
|
||||||
|
NSString *title = sender.currentTitle ?: @"";
|
||||||
|
if (title.length == 0) { return; }
|
||||||
|
if ([self.delegate respondsToSelector:@selector(suggestionBarView:didSelectSuggestion:)]) {
|
||||||
|
[self.delegate suggestionBarView:self didSelectSuggestion:title];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Lazy
|
||||||
|
|
||||||
|
- (UIScrollView *)scrollView {
|
||||||
|
if (!_scrollView) {
|
||||||
|
_scrollView = [[UIScrollView alloc] init];
|
||||||
|
_scrollView.showsHorizontalScrollIndicator = NO;
|
||||||
|
_scrollView.alwaysBounceHorizontal = YES;
|
||||||
|
}
|
||||||
|
return _scrollView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIStackView *)stackView {
|
||||||
|
if (!_stackView) {
|
||||||
|
_stackView = [[UIStackView alloc] init];
|
||||||
|
_stackView.axis = UILayoutConstraintAxisHorizontal;
|
||||||
|
_stackView.alignment = UIStackViewAlignmentCenter;
|
||||||
|
_stackView.spacing = 8.0;
|
||||||
|
}
|
||||||
|
return _stackView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -172,6 +172,9 @@
|
|||||||
04C6EACE2EAF87020089C901 /* CustomKeyboard.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 04C6EAC62EAF87020089C901 /* CustomKeyboard.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
04C6EACE2EAF87020089C901 /* CustomKeyboard.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 04C6EAC62EAF87020089C901 /* CustomKeyboard.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
04C6EAD82EAF870B0089C901 /* KeyboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAD62EAF870B0089C901 /* KeyboardViewController.m */; };
|
04C6EAD82EAF870B0089C901 /* KeyboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAD62EAF870B0089C901 /* KeyboardViewController.m */; };
|
||||||
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */; };
|
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */; };
|
||||||
|
A1B2C3ED2F20000000000001 /* kb_words.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3EC2F20000000000001 /* kb_words.txt */; };
|
||||||
|
A1B2C3EA2F20000000000001 /* KBSuggestionEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3E72F20000000000001 /* KBSuggestionEngine.m */; };
|
||||||
|
A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3E92F20000000000001 /* KBSuggestionBarView.m */; };
|
||||||
04D1F6B22EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; };
|
04D1F6B22EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; };
|
||||||
04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; };
|
04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; };
|
||||||
04FC95672EB0546C007BD342 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95652EB0546C007BD342 /* KBKey.m */; };
|
04FC95672EB0546C007BD342 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95652EB0546C007BD342 /* KBKey.m */; };
|
||||||
@@ -503,6 +506,10 @@
|
|||||||
04A9A67D2EB9E1690023B8F4 /* KBResponderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBResponderUtils.h; sourceTree = "<group>"; };
|
04A9A67D2EB9E1690023B8F4 /* KBResponderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBResponderUtils.h; sourceTree = "<group>"; };
|
||||||
04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = "<group>"; };
|
04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = "<group>"; };
|
||||||
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = "<group>"; };
|
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = "<group>"; };
|
||||||
|
A1B2C3E62F20000000000001 /* KBSuggestionEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSuggestionEngine.h; sourceTree = "<group>"; };
|
||||||
|
A1B2C3E72F20000000000001 /* KBSuggestionEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSuggestionEngine.m; sourceTree = "<group>"; };
|
||||||
|
A1B2C3E82F20000000000001 /* KBSuggestionBarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSuggestionBarView.h; sourceTree = "<group>"; };
|
||||||
|
A1B2C3E92F20000000000001 /* KBSuggestionBarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSuggestionBarView.m; sourceTree = "<group>"; };
|
||||||
04A9FE142EB873C80020DB6D /* UIViewController+Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Extension.h"; sourceTree = "<group>"; };
|
04A9FE142EB873C80020DB6D /* UIViewController+Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Extension.h"; sourceTree = "<group>"; };
|
||||||
04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Extension.m"; sourceTree = "<group>"; };
|
04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Extension.m"; sourceTree = "<group>"; };
|
||||||
04A9FE182EB892460020DB6D /* KBLocalizationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBLocalizationManager.h; sourceTree = "<group>"; };
|
04A9FE182EB892460020DB6D /* KBLocalizationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBLocalizationManager.h; sourceTree = "<group>"; };
|
||||||
@@ -524,6 +531,7 @@
|
|||||||
04C6EAD42EAF870B0089C901 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
04C6EAD42EAF870B0089C901 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
04C6EAD52EAF870B0089C901 /* KeyboardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyboardViewController.h; sourceTree = "<group>"; };
|
04C6EAD52EAF870B0089C901 /* KeyboardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyboardViewController.h; sourceTree = "<group>"; };
|
||||||
04C6EAD62EAF870B0089C901 /* KeyboardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KeyboardViewController.m; sourceTree = "<group>"; };
|
04C6EAD62EAF870B0089C901 /* KeyboardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KeyboardViewController.m; sourceTree = "<group>"; };
|
||||||
|
A1B2C3EC2F20000000000001 /* kb_words.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = kb_words.txt; sourceTree = "<group>"; };
|
||||||
04C6EADB2EAF8CEB0089C901 /* KBToolBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBToolBar.h; sourceTree = "<group>"; };
|
04C6EADB2EAF8CEB0089C901 /* KBToolBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBToolBar.h; sourceTree = "<group>"; };
|
||||||
04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBToolBar.m; sourceTree = "<group>"; };
|
04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBToolBar.m; sourceTree = "<group>"; };
|
||||||
04C6EADE2EAF8D680089C901 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = "<group>"; };
|
04C6EADE2EAF8D680089C901 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = "<group>"; };
|
||||||
@@ -657,6 +665,7 @@
|
|||||||
041007D02ECE010100D203BB /* Resource */ = {
|
041007D02ECE010100D203BB /* Resource */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A1B2C3EC2F20000000000001 /* kb_words.txt */,
|
||||||
0498BDF42EEC50EE006CC1D5 /* emoji_categories.json */,
|
0498BDF42EEC50EE006CC1D5 /* emoji_categories.json */,
|
||||||
041007D12ECE012000D203BB /* KBSkinIconMap.strings */,
|
041007D12ECE012000D203BB /* KBSkinIconMap.strings */,
|
||||||
041007D32ECE012500D203BB /* 002.zip */,
|
041007D32ECE012500D203BB /* 002.zip */,
|
||||||
@@ -1086,6 +1095,8 @@
|
|||||||
children = (
|
children = (
|
||||||
04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */,
|
04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */,
|
||||||
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */,
|
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */,
|
||||||
|
A1B2C3E62F20000000000001 /* KBSuggestionEngine.h */,
|
||||||
|
A1B2C3E72F20000000000001 /* KBSuggestionEngine.m */,
|
||||||
04FEDA9F2EEDB00100123456 /* KBEmojiDataProvider.h */,
|
04FEDA9F2EEDB00100123456 /* KBEmojiDataProvider.h */,
|
||||||
04FEDAA02EEDB00100123456 /* KBEmojiDataProvider.m */,
|
04FEDAA02EEDB00100123456 /* KBEmojiDataProvider.m */,
|
||||||
);
|
);
|
||||||
@@ -1154,6 +1165,8 @@
|
|||||||
046131132ECF454500A6FADF /* KBKeyPreviewView.m */,
|
046131132ECF454500A6FADF /* KBKeyPreviewView.m */,
|
||||||
04FC95772EB09BC8007BD342 /* KBKeyBoardMainView.h */,
|
04FC95772EB09BC8007BD342 /* KBKeyBoardMainView.h */,
|
||||||
04FC95782EB09BC8007BD342 /* KBKeyBoardMainView.m */,
|
04FC95782EB09BC8007BD342 /* KBKeyBoardMainView.m */,
|
||||||
|
A1B2C3E82F20000000000001 /* KBSuggestionBarView.h */,
|
||||||
|
A1B2C3E92F20000000000001 /* KBSuggestionBarView.m */,
|
||||||
04FC956E2EB09516007BD342 /* KBFunctionView.h */,
|
04FC956E2EB09516007BD342 /* KBFunctionView.h */,
|
||||||
04FC956F2EB09516007BD342 /* KBFunctionView.m */,
|
04FC956F2EB09516007BD342 /* KBFunctionView.m */,
|
||||||
04FC95712EB09570007BD342 /* KBFunctionBarView.h */,
|
04FC95712EB09570007BD342 /* KBFunctionBarView.h */,
|
||||||
@@ -1773,6 +1786,7 @@
|
|||||||
04A9FE202EB893F10020DB6D /* Localizable.strings in Resources */,
|
04A9FE202EB893F10020DB6D /* Localizable.strings in Resources */,
|
||||||
041007D42ECE012500D203BB /* 002.zip in Resources */,
|
041007D42ECE012500D203BB /* 002.zip in Resources */,
|
||||||
041007D22ECE012000D203BB /* KBSkinIconMap.strings in Resources */,
|
041007D22ECE012000D203BB /* KBSkinIconMap.strings in Resources */,
|
||||||
|
A1B2C3ED2F20000000000001 /* kb_words.txt in Resources */,
|
||||||
04791FFB2ED5EAB8004E8522 /* fense.zip in Resources */,
|
04791FFB2ED5EAB8004E8522 /* fense.zip in Resources */,
|
||||||
0498BDF52EEC50EE006CC1D5 /* emoji_categories.json in Resources */,
|
0498BDF52EEC50EE006CC1D5 /* emoji_categories.json in Resources */,
|
||||||
04791FF72ED5B985004E8522 /* Christmas.zip in Resources */,
|
04791FF72ED5B985004E8522 /* Christmas.zip in Resources */,
|
||||||
@@ -1877,6 +1891,7 @@
|
|||||||
0450AC4A2EF2C3ED00B6AF06 /* KBKeyboardSubscriptionOptionCell.m in Sources */,
|
0450AC4A2EF2C3ED00B6AF06 /* KBKeyboardSubscriptionOptionCell.m in Sources */,
|
||||||
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */,
|
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */,
|
||||||
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */,
|
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */,
|
||||||
|
A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */,
|
||||||
04FC95792EB09BC8007BD342 /* KBKeyBoardMainView.m in Sources */,
|
04FC95792EB09BC8007BD342 /* KBKeyBoardMainView.m in Sources */,
|
||||||
04FEDAB32EEDB05000123456 /* KBEmojiPanelView.m in Sources */,
|
04FEDAB32EEDB05000123456 /* KBEmojiPanelView.m in Sources */,
|
||||||
04FEDB032EFE000000123456 /* KBEmojiBottomBarView.m in Sources */,
|
04FEDB032EFE000000123456 /* KBEmojiBottomBarView.m in Sources */,
|
||||||
@@ -1889,6 +1904,7 @@
|
|||||||
04FC95762EB095DE007BD342 /* KBFunctionPasteView.m in Sources */,
|
04FC95762EB095DE007BD342 /* KBFunctionPasteView.m in Sources */,
|
||||||
A1B2C3D42EB0A0A100000001 /* KBFunctionTagCell.m in Sources */,
|
A1B2C3D42EB0A0A100000001 /* KBFunctionTagCell.m in Sources */,
|
||||||
04A9FE1A2EB892460020DB6D /* KBLocalizationManager.m in Sources */,
|
04A9FE1A2EB892460020DB6D /* KBLocalizationManager.m in Sources */,
|
||||||
|
A1B2C3EA2F20000000000001 /* KBSuggestionEngine.m in Sources */,
|
||||||
A1B2C3E22EB0C0A100000001 /* KBNetworkManager.m in Sources */,
|
A1B2C3E22EB0C0A100000001 /* KBNetworkManager.m in Sources */,
|
||||||
049FB2352EC45C6A00FAB05D /* NetworkStreamHandler.m in Sources */,
|
049FB2352EC45C6A00FAB05D /* NetworkStreamHandler.m in Sources */,
|
||||||
04FC956A2EB05497007BD342 /* KBKeyButton.m in Sources */,
|
04FC956A2EB05497007BD342 /* KBKeyButton.m in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user