diff --git a/CustomKeyboard/Model/KBKey.h b/CustomKeyboard/Model/KBKey.h index 44bcdda..ba4b7a4 100644 --- a/CustomKeyboard/Model/KBKey.h +++ b/CustomKeyboard/Model/KBKey.h @@ -20,13 +20,29 @@ typedef NS_ENUM(NSInteger, KBKeyType) { KBKeyTypeSymbolsToggle // 数字面板内的“#+=/123”切换 }; +/// 字母键的大小写变体标记(非字母键使用 KBKeyCaseVariantNone) +typedef NS_ENUM(NSInteger, KBKeyCaseVariant) { + KBKeyCaseVariantNone = 0, + KBKeyCaseVariantLower = 1, + KBKeyCaseVariantUpper = 2, +}; + @interface KBKey : NSObject @property (nonatomic, assign) KBKeyType type; @property (nonatomic, copy) NSString *title; // 显示标题 @property (nonatomic, copy) NSString *output; // 字符键插入的文本 +/// 逻辑按键标识,用于皮肤映射(如 @"letter_q" @"space" @"backspace") +@property (nonatomic, copy, nullable) NSString *identifier; +/// 字母键的大小写变体(便于皮肤为大小写准备不同图) +@property (nonatomic, assign) KBKeyCaseVariant caseVariant; + (instancetype)keyWithTitle:(NSString *)title output:(NSString *)output; + (instancetype)keyWithTitle:(NSString *)title type:(KBKeyType)type; +/// 通用构造方法:用于指定 identifier,便于皮肤做精细控制 ++ (instancetype)keyWithIdentifier:(nullable NSString *)identifier + title:(NSString *)title + output:(NSString *)output + type:(KBKeyType)type; @end diff --git a/CustomKeyboard/Model/KBKey.m b/CustomKeyboard/Model/KBKey.m index 92d2625..d49a622 100644 --- a/CustomKeyboard/Model/KBKey.m +++ b/CustomKeyboard/Model/KBKey.m @@ -12,6 +12,7 @@ k.type = KBKeyTypeCharacter; k.title = title ?: @""; k.output = output ?: title ?: @""; + k.caseVariant = KBKeyCaseVariantNone; return k; } @@ -20,8 +21,21 @@ k.type = type; k.title = title ?: @""; k.output = @""; + k.caseVariant = KBKeyCaseVariantNone; + return k; +} + ++ (instancetype)keyWithIdentifier:(NSString *)identifier + title:(NSString *)title + output:(NSString *)output + type:(KBKeyType)type { + KBKey *k = [[KBKey alloc] init]; + k.type = type; + k.identifier = identifier; + k.title = title ?: @""; + k.output = output ?: @""; + k.caseVariant = KBKeyCaseVariantNone; return k; } @end - diff --git a/CustomKeyboard/View/KBKeyButton.h b/CustomKeyboard/View/KBKeyButton.h index c9a9b8a..7c214a9 100644 --- a/CustomKeyboard/View/KBKeyButton.h +++ b/CustomKeyboard/View/KBKeyButton.h @@ -17,4 +17,7 @@ /// 根据选中/高亮等状态刷新外观 - (void)refreshStateAppearance; +/// 根据当前皮肤与按键标识,应用图标和文字显隐等细节 +- (void)applyThemeForCurrentKey; + @end diff --git a/CustomKeyboard/View/KBKeyButton.m b/CustomKeyboard/View/KBKeyButton.m index 4de0114..ba827da 100644 --- a/CustomKeyboard/View/KBKeyButton.m +++ b/CustomKeyboard/View/KBKeyButton.m @@ -7,6 +7,10 @@ #import "KBKey.h" #import "KBSkinManager.h" +@interface KBKeyButton () +@property (nonatomic, strong) UIImageView *iconView; +@end + @implementation KBKeyButton - (instancetype)initWithFrame:(CGRect)frame { @@ -29,6 +33,34 @@ self.layer.shadowOffset = CGSizeMake(0, 1); self.layer.shadowRadius = 1.5; [self refreshStateAppearance]; + + // 懒创建图标视图,用于后续皮肤按键小图标展示 + if (!self.iconView) { + UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectZero]; + // 作为按键的整块皮肤背景,铺满整个按钮区域 + iv.contentMode = UIViewContentModeScaleAspectFill; + iv.clipsToBounds = YES; + iv.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:iv]; + // 让皮肤图片撑满整个按钮 + [NSLayoutConstraint activateConstraints:@[ + [iv.topAnchor constraintEqualToAnchor:self.topAnchor], + [iv.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], + [iv.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], + [iv.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], + ]]; + self.iconView = iv; + + // 文字保持居中;若需要显示文字,则覆盖在皮肤图片之上 + self.titleEdgeInsets = UIEdgeInsetsZero; + [self bringSubviewToFront:self.titleLabel]; + } +} + +- (void)setKey:(KBKey *)key { + _key = key; + // 每次切换按键模型时,根据皮肤刷新一次图标和文字显隐 + [self applyThemeForCurrentKey]; } - (void)setHighlighted:(BOOL)highlighted { @@ -56,4 +88,19 @@ } } +- (void)applyThemeForCurrentKey { + // 依据皮肤决定是否显示文字 + NSString *identifier = self.key.identifier; + BOOL hideText = [[KBSkinManager shared] shouldHideKeyTextForIdentifier:identifier]; + self.titleLabel.hidden = hideText; + + // 根据皮肤映射加载图标(若有),支持大小写变体: + // - identifier: 逻辑按键标识(如 letter_q) + // - caseVariant: 0/1/2 => 无变体/小写/大写 + NSInteger variant = (NSInteger)self.key.caseVariant; + UIImage *iconImg = [[KBSkinManager shared] iconImageForKeyIdentifier:identifier caseVariant:variant]; + self.iconView.image = iconImg; + self.iconView.hidden = (iconImg == nil); +} + @end diff --git a/CustomKeyboard/View/KBKeyboardView.m b/CustomKeyboard/View/KBKeyboardView.m index 3438ba1..607f501 100644 --- a/CustomKeyboard/View/KBKeyboardView.m +++ b/CustomKeyboard/View/KBKeyboardView.m @@ -96,16 +96,41 @@ if (!self.symbolsMoreOn) { // 数字第一页(123) - 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"] ]; - 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:@"\""] ]; - r3 = @[ [KBKey keyWithTitle:@"#+=" type:KBKeyTypeSymbolsToggle], - [KBKey keyWithTitle:@"," output:@","], [KBKey keyWithTitle:@"." output:@"."], [KBKey keyWithTitle:@"?" output:@"?"], - [KBKey keyWithTitle:@"!" output:@"!"], [KBKey keyWithTitle:@"'" output:@"'"], - [KBKey keyWithTitle:@"⌫" type:KBKeyTypeBackspace] ]; + 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"] ]; + 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:@"\""] ]; + // 第三行:左下角是“#+=”切换键,右下角是退格键 + KBKey *toggle = [KBKey keyWithIdentifier:@"symbols_toggle_more" + title:@"#+=" + output:@"" + type:KBKeyTypeSymbolsToggle]; + KBKey *comma = [KBKey keyWithTitle:@"," output:@","]; + KBKey *dot = [KBKey keyWithTitle:@"." output:@"."]; + KBKey *q = [KBKey keyWithTitle:@"?" output:@"?"]; + KBKey *ex = [KBKey keyWithTitle:@"!" output:@"!"]; + KBKey *quote = [KBKey keyWithTitle:@"'" output:@"'"]; + KBKey *back = [KBKey keyWithIdentifier:@"backspace" + title:@"⌫" + output:@"" + type:KBKeyTypeBackspace]; + r3 = @[ toggle, comma, dot, q, ex, quote, back ]; } else { // 数字第二页(#+=):前两行替换为更多符号,左下角按钮文案改为“123” r1 = @[ [KBKey keyWithTitle:@"[" output:@"["], [KBKey keyWithTitle:@"]" output:@"]"], [KBKey keyWithTitle:@"{" output:@"{"], @@ -116,16 +141,40 @@ [KBKey keyWithTitle:@"~" output:@"~"], [KBKey keyWithTitle:@"<" output:@"<"], [KBKey keyWithTitle:@">" output:@">"], [KBKey keyWithTitle:@"$" output:@"$"], [KBKey keyWithTitle:@"€" output:@"€"], [KBKey keyWithTitle:@"£" output:@"£"], [KBKey keyWithTitle:@"•" output:@"•"] ]; - r3 = @[ [KBKey keyWithTitle:@"123" type:KBKeyTypeSymbolsToggle], - [KBKey keyWithTitle:@"," output:@","], [KBKey keyWithTitle:@"." output:@"."], [KBKey keyWithTitle:@"?" output:@"?"], - [KBKey keyWithTitle:@"!" output:@"!"], [KBKey keyWithTitle:@"'" output:@"'"], - [KBKey keyWithTitle:@"⌫" type:KBKeyTypeBackspace] ]; + KBKey *toggle = [KBKey keyWithIdentifier:@"symbols_toggle_123" + title:@"123" + output:@"" + type:KBKeyTypeSymbolsToggle]; + KBKey *comma = [KBKey keyWithTitle:@"," output:@","]; + KBKey *dot = [KBKey keyWithTitle:@"." output:@"."]; + KBKey *q = [KBKey keyWithTitle:@"?" output:@"?"]; + KBKey *ex = [KBKey keyWithTitle:@"!" output:@"!"]; + KBKey *quote = [KBKey keyWithTitle:@"'" output:@"'"]; + KBKey *back = [KBKey keyWithIdentifier:@"backspace" + title:@"⌫" + output:@"" + type:KBKeyTypeBackspace]; + r3 = @[ toggle, comma, dot, q, ex, quote, back ]; } - NSArray *r4 = @[ [KBKey keyWithTitle:@"abc" type:KBKeyTypeModeChange], - [KBKey keyWithTitle:@"AI" type:KBKeyTypeCustom], - [KBKey keyWithTitle:@"space" type:KBKeyTypeSpace], - [KBKey keyWithTitle:KBLocalized(@"Send") type:KBKeyTypeReturn] ]; + 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]; } @@ -139,27 +188,67 @@ // 字母键标题与输出同时随 Shift 切换大小写,界面与输入保持一致 for (NSString *s in r1) { NSString *shown = self.shiftOn ? s : s.lowercaseString; - [row1 addObject:[KBKey keyWithTitle:shown output:shown]]; + 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 *row2 = [NSMutableArray arrayWithCapacity:r2.count]; for (NSString *s in r2) { NSString *shown = self.shiftOn ? s : s.lowercaseString; - [row2 addObject:[KBKey keyWithTitle:shown output:shown]]; + 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 *row3 = [NSMutableArray array]; - [row3 addObject:[KBKey keyWithTitle:@"⇧" type:KBKeyTypeShift]]; + KBKey *shift = [KBKey keyWithIdentifier:@"shift" + title:@"⇧" + output:@"" + type:KBKeyTypeShift]; + [row3 addObject:shift]; for (NSString *s in r3chars) { NSString *shown = self.shiftOn ? s : s.lowercaseString; - [row3 addObject:[KBKey keyWithTitle:shown output:shown]]; + 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]; } - [row3 addObject:[KBKey keyWithTitle:@"⌫" type:KBKeyTypeBackspace]]; + KBKey *backspace = [KBKey keyWithIdentifier:@"backspace" + title:@"⌫" + output:@"" + type:KBKeyTypeBackspace]; + [row3 addObject:backspace]; - NSArray *row4 = @[ [KBKey keyWithTitle:@"123" type:KBKeyTypeModeChange], - [KBKey keyWithTitle:@"AI" type:KBKeyTypeCustom], - [KBKey keyWithTitle:@"space" type:KBKeyTypeSpace], - [KBKey keyWithTitle:KBLocalized(@"Send") type:KBKeyTypeReturn] ]; + KBKey *mode123 = [KBKey keyWithIdentifier:@"mode_123" + title:@"123" + 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 *row4 = @[ mode123, customAI, space, ret ]; return @[row1.copy, row2.copy, row3.copy, row4]; } diff --git a/Shared/KBConfig.h b/Shared/KBConfig.h index c242718..b4db3b4 100644 --- a/Shared/KBConfig.h +++ b/Shared/KBConfig.h @@ -19,6 +19,13 @@ /// APP Groups #define AppGroup @"group.com.loveKey.nyx" +/// 皮肤图标加载模式: +/// 0 = 使用本地 Assets 图片名(key_icons 的 value 写成图片名,例如 "kb_q_melon") +/// 1 = 使用远程 URL 下载到 App Group 目录,再由键盘扩展从文件系统加载 +#ifndef KB_SKIN_ICON_USE_REMOTE +#define KB_SKIN_ICON_USE_REMOTE 0 +#endif + // 基础baseUrl #ifndef KB_BASE_URL @@ -84,4 +91,3 @@ static inline CGFloat KBFit(CGFloat designValue) { #ifndef KBWeakSelf #define KBWeakSelf __weak __typeof(self) weakSelf = self; #endif - diff --git a/Shared/KBSkinManager.h b/Shared/KBSkinManager.h index b82350a..5eb398f 100644 --- a/Shared/KBSkinManager.h +++ b/Shared/KBSkinManager.h @@ -26,6 +26,10 @@ extern NSString * const KBDarwinSkinChanged; // cross-process @property (nonatomic, strong) UIColor *accentColor; // function view accents /// 可选:键盘背景图片的 PNG/JPEG 数据(若存在,优先显示图片) @property (nonatomic, strong, nullable) NSData *backgroundImageData; +/// 可选:需要隐藏文字的按键标识集合(例如 @"letter_q" @"space" 等) +@property (nonatomic, strong, nullable) NSArray *hiddenKeyTextIdentifiers; +/// 可选:按键图标映射(按键标识 -> 图标名或路径),具体加载策略由上层决定 +@property (nonatomic, strong, nullable) NSDictionary *keyIconMap; @end /// Shared skin manager (Keychain Sharing based) @@ -50,6 +54,23 @@ extern NSString * const KBDarwinSkinChanged; // cross-process /// 当前背景图片(若存在) - (nullable UIImage *)currentBackgroundImage; +/// 当前主题下,指定按键标识的文字是否应被隐藏(例如图标里已包含字母) +- (BOOL)shouldHideKeyTextForIdentifier:(nullable NSString *)identifier; + +/// 当前主题下,指定按键标识对应的图标名(可作为 imageName 或文件相对路径),若未配置则为 nil +- (nullable NSString *)iconNameForKeyIdentifier:(nullable NSString *)identifier; + +/// 当前主题下,返回指定按键的图标图片。 +/// 若 keyIconMap 中的 value 不包含 "/",则按本地 Assets 名称加载; +/// 若包含 "/",则视为相对 App Group 根目录的文件路径,从文件系统加载。 +- (nullable UIImage *)iconImageForKeyIdentifier:(nullable NSString *)identifier; + +/// 带大小写变体的按键图标获取接口。 +/// @param identifier 逻辑按键标识(如 @"letter_q" @"space") +/// @param caseVariant 0=无变体/非字母,1=小写,2=大写(与 KBKeyCaseVariant 对应) +- (nullable UIImage *)iconImageForKeyIdentifier:(nullable NSString *)identifier + caseVariant:(NSInteger)caseVariant; + /// Parse a hex color string like "#RRGGBB"/"#RRGGBBAA" + (UIColor *)colorFromHexString:(NSString *)hex defaultColor:(UIColor *)fallback; diff --git a/Shared/KBSkinManager.m b/Shared/KBSkinManager.m index 6497ff4..7e527b3 100644 --- a/Shared/KBSkinManager.m +++ b/Shared/KBSkinManager.m @@ -27,6 +27,12 @@ static NSString * const kKBSkinAccount = @"current"; // Keychain ac if (self.backgroundImageData) { [coder encodeObject:self.backgroundImageData forKey:@"backgroundImageData"]; } + if (self.hiddenKeyTextIdentifiers.count > 0) { + [coder encodeObject:self.hiddenKeyTextIdentifiers forKey:@"hiddenKeyTextIdentifiers"]; + } + if (self.keyIconMap.count > 0) { + [coder encodeObject:self.keyIconMap forKey:@"keyIconMap"]; + } } - (instancetype)initWithCoder:(NSCoder *)coder { @@ -39,6 +45,9 @@ static NSString * const kKBSkinAccount = @"current"; // Keychain ac _keyHighlightBackground = [coder decodeObjectOfClass:UIColor.class forKey:@"keyHighlightBackground"] ?: [UIColor colorWithWhite:0.85 alpha:1.0]; _accentColor = [coder decodeObjectOfClass:UIColor.class forKey:@"accentColor"] ?: [UIColor colorWithRed:0.77 green:0.93 blue:0.82 alpha:1.0]; _backgroundImageData = [coder decodeObjectOfClass:NSData.class forKey:@"backgroundImageData"]; + // 这两个字段是新增的,旧数据没有也没关系 + _hiddenKeyTextIdentifiers = [coder decodeObjectOfClass:NSArray.class forKey:@"hiddenKeyTextIdentifiers"]; + _keyIconMap = [coder decodeObjectOfClass:NSDictionary.class forKey:@"keyIconMap"]; } return self; } @@ -91,6 +100,16 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer, t.keyTextColor = [self.class colorFromHexString:json[@"key_text"] defaultColor:[self.class defaultTheme].keyTextColor]; t.keyHighlightBackground = [self.class colorFromHexString:json[@"key_highlight"] defaultColor:[self.class defaultTheme].keyHighlightBackground]; t.accentColor = [self.class colorFromHexString:json[@"accent"] defaultColor:[self.class defaultTheme].accentColor]; + // 可选:hidden_keys 为需要隐藏文本的按键标识数组 + id hidden = json[@"hidden_keys"]; + if ([hidden isKindOfClass:NSArray.class]) { + t.hiddenKeyTextIdentifiers = hidden; + } + // 可选:key_icons 为 按键标识 -> 图标名/文件名 的字典 + id icons = json[@"key_icons"]; + if ([icons isKindOfClass:NSDictionary.class]) { + t.keyIconMap = icons; + } return [self applyTheme:t]; } @@ -121,6 +140,9 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer, t.keyTextColor = base.keyTextColor ?: [self.class defaultTheme].keyTextColor; t.keyHighlightBackground = base.keyHighlightBackground ?: [self.class defaultTheme].keyHighlightBackground; t.accentColor = base.accentColor ?: [self.class defaultTheme].accentColor; + // 继承按键文本隐藏配置和图标映射,避免仅切换背景时丢失这些设置 + t.hiddenKeyTextIdentifiers = base.hiddenKeyTextIdentifiers; + t.keyIconMap = base.keyIconMap; t.backgroundImageData = imageData; return [self applyTheme:t]; } @@ -131,6 +153,103 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer, return [UIImage imageWithData:d scale:[UIScreen mainScreen].scale] ?: nil; } +- (BOOL)shouldHideKeyTextForIdentifier:(NSString *)identifier { + if (identifier.length == 0) return NO; + NSArray *list = self.current.hiddenKeyTextIdentifiers; + if (list.count == 0) return NO; + // 简单线性查找,数量有限足够;如需可改为 NSSet 缓存。 + for (NSString *s in list) { + if ([s isKindOfClass:NSString.class] && [s isEqualToString:identifier]) { + return YES; + } + } + return NO; +} + +- (NSString *)iconNameForKeyIdentifier:(NSString *)identifier { + if (identifier.length == 0) return nil; + NSDictionary *map = self.current.keyIconMap; + if (map.count == 0) return nil; + NSString *name = map[identifier]; + if (![name isKindOfClass:NSString.class] || name.length == 0) return nil; + return name; +} + +- (UIImage *)iconImageForKeyIdentifier:(NSString *)identifier { + return [self iconImageForKeyIdentifier:identifier caseVariant:0]; +} + +- (UIImage *)iconImageForKeyIdentifier:(NSString *)identifier caseVariant:(NSInteger)caseVariant { + NSDictionary *map = self.current.keyIconMap; + NSString *value = nil; + + if (identifier.length > 0 && map.count > 0) { + // 1) 大小写变体优先:letter_q_upper / letter_q_lower + if (caseVariant == 2) { // upper + NSString *keyUpper = [identifier stringByAppendingString:@"_upper"]; + NSString *candidate = map[keyUpper]; + if ([candidate isKindOfClass:NSString.class] && candidate.length > 0) { + value = candidate; + } + } else if (caseVariant == 1) { // lower + NSString *keyLower = [identifier stringByAppendingString:@"_lower"]; + NSString *candidate = map[keyLower]; + if ([candidate isKindOfClass:NSString.class] && candidate.length > 0) { + value = candidate; + } + } + + // 2) 若未配置大小写专用图标,则回退到基础 id(兼容旧数据:letter_q) + if (value.length == 0) { + NSString *candidate = map[identifier]; + if ([candidate isKindOfClass:NSString.class] && candidate.length > 0) { + value = candidate; + } + } + } + + // 若在 keyIconMap 中找到了 value,按约定加载 + if (value.length > 0) { + if ([value containsString:@"/"]) { + // 视为相对 App Group 根目录的文件路径 + NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup]; + if (!containerURL) return nil; + NSString *fullPath = [[containerURL.path stringByAppendingPathComponent:value] stringByStandardizingPath]; + if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) return nil; + return [UIImage imageWithContentsOfFile:fullPath]; + } + // 否则按本地 Assets 名称加载(兼容旧实现) + return [UIImage imageNamed:value]; + } + + // 兜底:若 keyIconMap 中没有该键,则按照约定的命名规则直接从 App Group 读取: + // Skins//icons/(identifier[_upper/_lower]).png + NSString *skinId = self.current.skinId; + if (skinId.length == 0 || identifier.length == 0) return nil; + NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup]; + if (!containerURL) return nil; + // 先尝试大小写后缀 + if (caseVariant == 2) { + NSString *relativeUpper = [NSString stringWithFormat:@"Skins/%@/icons/%@_upper.png", skinId, identifier]; + NSString *fullUpper = [[containerURL.path stringByAppendingPathComponent:relativeUpper] stringByStandardizingPath]; + if ([[NSFileManager defaultManager] fileExistsAtPath:fullUpper]) { + return [UIImage imageWithContentsOfFile:fullUpper]; + } + } else if (caseVariant == 1) { + NSString *relativeLower = [NSString stringWithFormat:@"Skins/%@/icons/%@_lower.png", skinId, identifier]; + NSString *fullLower = [[containerURL.path stringByAppendingPathComponent:relativeLower] stringByStandardizingPath]; + if ([[NSFileManager defaultManager] fileExistsAtPath:fullLower]) { + return [UIImage imageWithContentsOfFile:fullLower]; + } + } + + // 最后回退到基础 id:Skins//icons/.png + NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@.png", skinId, identifier]; + NSString *fullPath = [[containerURL.path stringByAppendingPathComponent:relative] stringByStandardizingPath]; + if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) return nil; + return [UIImage imageWithContentsOfFile:fullPath]; +} + + (UIColor *)colorFromHexString:(NSString *)hex defaultColor:(UIColor *)fallback { if (![hex isKindOfClass:NSString.class] || hex.length == 0) return fallback; NSString *s = [[hex stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString]; diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 0b7d6cf..d251b87 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 04122FB32EC73C0100EF7AB3 /* KBVipReviewListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */; }; 04286A002ECAEF2B00CE730C /* KBMoneyBtn.m in Sources */ = {isa = PBXBuildFile; fileRef = 042869FE2ECAEF2B00CE730C /* KBMoneyBtn.m */; }; 04286A032ECB0A1600CE730C /* KBSexSelVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04286A022ECB0A1600CE730C /* KBSexSelVC.m */; }; + 04286A062ECC81B200CE730C /* KBSkinService.m in Sources */ = {isa = PBXBuildFile; fileRef = 04286A052ECC81B200CE730C /* KBSkinService.m */; }; 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; }; 0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */; }; 0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; }; @@ -234,6 +235,8 @@ 042869FE2ECAEF2B00CE730C /* KBMoneyBtn.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMoneyBtn.m; sourceTree = ""; }; 04286A012ECB0A1600CE730C /* KBSexSelVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSexSelVC.h; sourceTree = ""; }; 04286A022ECB0A1600CE730C /* KBSexSelVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSexSelVC.m; sourceTree = ""; }; + 04286A042ECC81B200CE730C /* KBSkinService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinService.h; sourceTree = ""; }; + 04286A052ECC81B200CE730C /* KBSkinService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinService.m; sourceTree = ""; }; 0459D1B22EBA284C00F2D189 /* KBSkinCenterVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinCenterVC.h; sourceTree = ""; }; 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinCenterVC.m; sourceTree = ""; }; 0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = ""; }; @@ -1297,6 +1300,8 @@ children = ( 04FC95F22EB339C1007BD342 /* AppleSignInManager.h */, 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */, + 04286A042ECC81B200CE730C /* KBSkinService.h */, + 04286A052ECC81B200CE730C /* KBSkinService.m */, ); path = Manager; sourceTree = ""; @@ -1621,6 +1626,7 @@ 04122F882EC6F07F00EF7AB3 /* KBFullAccessManager.m in Sources */, 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */, 04122F8B2EC6F7C800EF7AB3 /* IAPVerifyTransactionObj.m in Sources */, + 04286A062ECC81B200CE730C /* KBSkinService.m in Sources */, 04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */, 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */, 048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */, diff --git a/keyBoard/Assets.xcassets/Test/Contents.json b/keyBoard/Assets.xcassets/Test/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_123.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_123.imageset/Contents.json new file mode 100644 index 0000000..1682122 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_123.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_123@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_123@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@2x.png b/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@2x.png new file mode 100644 index 0000000..7ab9828 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@3x.png b/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@3x.png new file mode 100644 index 0000000..cdc2457 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_123.imageset/key_123@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_a.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_a.imageset/Contents.json new file mode 100644 index 0000000..0e11b46 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_a.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_a@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_a@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@2x.png b/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@2x.png new file mode 100644 index 0000000..00812a2 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@3x.png b/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@3x.png new file mode 100644 index 0000000..2f6c4ba Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_a.imageset/key_a@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_ai.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_ai.imageset/Contents.json new file mode 100644 index 0000000..e5b8629 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_ai.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_ai@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_ai@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@2x.png b/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@2x.png new file mode 100644 index 0000000..6bf1f1b Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@3x.png b/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@3x.png new file mode 100644 index 0000000..f39eed4 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_ai.imageset/key_ai@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_b.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_b.imageset/Contents.json new file mode 100644 index 0000000..1b3ba7b --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_b.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_b@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_b@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@2x.png b/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@2x.png new file mode 100644 index 0000000..1125f96 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@3x.png b/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@3x.png new file mode 100644 index 0000000..aceada8 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_b.imageset/key_b@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_c.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_c.imageset/Contents.json new file mode 100644 index 0000000..0abce3d --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_c.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_c@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_c@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@2x.png b/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@2x.png new file mode 100644 index 0000000..4d71dd7 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@3x.png b/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@3x.png new file mode 100644 index 0000000..8b0a193 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_c.imageset/key_c@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_d.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_d.imageset/Contents.json new file mode 100644 index 0000000..866f853 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_d.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_d@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_d@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@2x.png b/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@2x.png new file mode 100644 index 0000000..1e0a4be Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@3x.png b/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@3x.png new file mode 100644 index 0000000..6d0ffb1 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_d.imageset/key_d@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_del.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_del.imageset/Contents.json new file mode 100644 index 0000000..0c83674 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_del.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_del@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_del@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@2x.png b/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@2x.png new file mode 100644 index 0000000..d182447 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@3x.png b/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@3x.png new file mode 100644 index 0000000..6c9fd3b Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_del.imageset/key_del@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_e.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_e.imageset/Contents.json new file mode 100644 index 0000000..3c37790 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_e.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_e@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_e@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@2x.png b/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@2x.png new file mode 100644 index 0000000..e93b8b9 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@3x.png b/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@3x.png new file mode 100644 index 0000000..1791c62 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_e.imageset/key_e@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_f.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_f.imageset/Contents.json new file mode 100644 index 0000000..e5f8f11 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_f.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_f@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_f@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@2x.png b/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@2x.png new file mode 100644 index 0000000..69a87ae Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@3x.png b/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@3x.png new file mode 100644 index 0000000..41e40f8 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_f.imageset/key_f@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_g.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_g.imageset/Contents.json new file mode 100644 index 0000000..306eb06 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_g.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_g@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_g@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@2x.png b/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@2x.png new file mode 100644 index 0000000..97f3707 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@3x.png b/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@3x.png new file mode 100644 index 0000000..9aa3558 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_g.imageset/key_g@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_h.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_h.imageset/Contents.json new file mode 100644 index 0000000..60e7887 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_h.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_h@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_h@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@2x.png b/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@2x.png new file mode 100644 index 0000000..ee00347 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@3x.png b/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@3x.png new file mode 100644 index 0000000..d3cad35 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_h.imageset/key_h@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_i.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_i.imageset/Contents.json new file mode 100644 index 0000000..d5a74bc --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_i.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_i@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_i@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@2x.png b/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@2x.png new file mode 100644 index 0000000..eb7b0e8 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@3x.png b/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@3x.png new file mode 100644 index 0000000..2f35e38 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_i.imageset/key_i@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_j.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_j.imageset/Contents.json new file mode 100644 index 0000000..791c24f --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_j.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_j@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_j@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@2x.png b/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@2x.png new file mode 100644 index 0000000..4c2924e Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@3x.png b/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@3x.png new file mode 100644 index 0000000..5c53b5d Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_j.imageset/key_j@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_k.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_k.imageset/Contents.json new file mode 100644 index 0000000..6c4f1ef --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_k.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_k@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_k@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@2x.png b/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@2x.png new file mode 100644 index 0000000..df4c7b8 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@3x.png b/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@3x.png new file mode 100644 index 0000000..d0ce1ad Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_k.imageset/key_k@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_l.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_l.imageset/Contents.json new file mode 100644 index 0000000..f1dd999 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_l.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_l@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_l@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@2x.png b/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@2x.png new file mode 100644 index 0000000..c51401c Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@3x.png b/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@3x.png new file mode 100644 index 0000000..6f10e27 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_l.imageset/key_l@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_m.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_m.imageset/Contents.json new file mode 100644 index 0000000..eeaef94 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_m.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_m@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_m@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@2x.png b/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@2x.png new file mode 100644 index 0000000..5992e4f Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@3x.png b/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@3x.png new file mode 100644 index 0000000..3d4d336 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_m.imageset/key_m@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_n.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_n.imageset/Contents.json new file mode 100644 index 0000000..f2860dc --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_n.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_n@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_n@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@2x.png b/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@2x.png new file mode 100644 index 0000000..8df5b38 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@3x.png b/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@3x.png new file mode 100644 index 0000000..a736c70 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_n.imageset/key_n@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_o.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_o.imageset/Contents.json new file mode 100644 index 0000000..d0985ad --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_o.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_o@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_o@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@2x.png b/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@2x.png new file mode 100644 index 0000000..48ce37f Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@3x.png b/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@3x.png new file mode 100644 index 0000000..a490e9c Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_o.imageset/key_o@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_p.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_p.imageset/Contents.json new file mode 100644 index 0000000..91534c8 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_p.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_p@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_p@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@2x.png b/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@2x.png new file mode 100644 index 0000000..50155eb Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@3x.png b/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@3x.png new file mode 100644 index 0000000..cdb3857 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_p.imageset/key_p@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_q.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_q.imageset/Contents.json new file mode 100644 index 0000000..d177b1f --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_q.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_q@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_q@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@2x.png b/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@2x.png new file mode 100644 index 0000000..d6be01b Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@3x.png b/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@3x.png new file mode 100644 index 0000000..1e4def7 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_q.imageset/key_q@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_r.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_r.imageset/Contents.json new file mode 100644 index 0000000..fc2cf4f --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_r.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_r@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_r@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@2x.png b/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@2x.png new file mode 100644 index 0000000..d850b99 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@3x.png b/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@3x.png new file mode 100644 index 0000000..ac49f1a Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_r.imageset/key_r@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_s.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_s.imageset/Contents.json new file mode 100644 index 0000000..0d9df51 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_s.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_s@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_s@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@2x.png b/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@2x.png new file mode 100644 index 0000000..6b6c31d Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@3x.png b/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@3x.png new file mode 100644 index 0000000..e2a9b06 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_s.imageset/key_s@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_send.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_send.imageset/Contents.json new file mode 100644 index 0000000..b4f2e28 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_send.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_send@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_send@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@2x.png b/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@2x.png new file mode 100644 index 0000000..10ba33b Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@3x.png b/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@3x.png new file mode 100644 index 0000000..db50cb1 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_send.imageset/key_send@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_space.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_space.imageset/Contents.json new file mode 100644 index 0000000..5ef0204 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_space.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_space@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_space@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@2x.png b/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@2x.png new file mode 100644 index 0000000..ca3ec73 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@3x.png b/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@3x.png new file mode 100644 index 0000000..d07ae5a Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_space.imageset/key_space@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_t.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_t.imageset/Contents.json new file mode 100644 index 0000000..3e5a801 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_t.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_t@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_t@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@2x.png b/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@2x.png new file mode 100644 index 0000000..c627336 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@3x.png b/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@3x.png new file mode 100644 index 0000000..03491a2 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_t.imageset/key_t@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_u.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_u.imageset/Contents.json new file mode 100644 index 0000000..9aa5d19 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_u.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_u@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_u@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@2x.png b/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@2x.png new file mode 100644 index 0000000..b0b7b60 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@3x.png b/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@3x.png new file mode 100644 index 0000000..0d02abe Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_u.imageset/key_u@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_up.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_up.imageset/Contents.json new file mode 100644 index 0000000..ff21bb8 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_up.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_up@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_up@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@2x.png b/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@2x.png new file mode 100644 index 0000000..e657e87 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@3x.png b/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@3x.png new file mode 100644 index 0000000..4a81035 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_up.imageset/key_up@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_v.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_v.imageset/Contents.json new file mode 100644 index 0000000..53505e7 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_v.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_v@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_v@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@2x.png b/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@2x.png new file mode 100644 index 0000000..417aa9d Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@3x.png b/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@3x.png new file mode 100644 index 0000000..7cecfc1 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_v.imageset/key_v@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_w.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_w.imageset/Contents.json new file mode 100644 index 0000000..638d983 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_w.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_w@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_w@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@2x.png b/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@2x.png new file mode 100644 index 0000000..a1104d6 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@3x.png b/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@3x.png new file mode 100644 index 0000000..62d987d Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_w.imageset/key_w@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_x.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_x.imageset/Contents.json new file mode 100644 index 0000000..f6ab18b --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_x.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_x@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_x@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@2x.png b/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@2x.png new file mode 100644 index 0000000..cfbd41c Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@3x.png b/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@3x.png new file mode 100644 index 0000000..44f0871 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_x.imageset/key_x@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_y.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_y.imageset/Contents.json new file mode 100644 index 0000000..bc96386 --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_y.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_y@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_y@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@2x.png b/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@2x.png new file mode 100644 index 0000000..b1911e6 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@3x.png b/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@3x.png new file mode 100644 index 0000000..eb00f21 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_y.imageset/key_y@3x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_z.imageset/Contents.json b/keyBoard/Assets.xcassets/Test/key_z.imageset/Contents.json new file mode 100644 index 0000000..5ced3fe --- /dev/null +++ b/keyBoard/Assets.xcassets/Test/key_z.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "key_z@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "key_z@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@2x.png b/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@2x.png new file mode 100644 index 0000000..87df91f Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@2x.png differ diff --git a/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@3x.png b/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@3x.png new file mode 100644 index 0000000..3ce48d6 Binary files /dev/null and b/keyBoard/Assets.xcassets/Test/key_z.imageset/key_z@3x.png differ diff --git a/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m b/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m index 50155cb..cf626c0 100644 --- a/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m +++ b/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m @@ -7,6 +7,8 @@ #import "KBNetworkManager.h" #import "KBSkinManager.h" #import "KBHUD.h" +#import "KBConfig.h" +#import "KBSkinService.h" @interface KBSkinCell : UITableViewCell @property (nonatomic, strong) UIButton *applyBtn; @@ -34,7 +36,7 @@ @interface KBSkinCenterVC () @property (nonatomic, strong) UITableView *tableView; -@property (nonatomic, copy) NSArray *skins; // id, name, img(url relative to KB_BASE_URL) +@property (nonatomic, copy) NSArray *skins; // 每个元素即一套皮肤的 JSON(与后端约定格式一致) @end @implementation KBSkinCenterVC @@ -43,12 +45,103 @@ [super viewDidLoad]; // self.title = KBLocalized(@"皮肤中心"); self.view.backgroundColor = [UIColor whiteColor]; - // 绝对 URL 的测试皮肤图片(无需 KB_BASE_URL)。 - // 调整为较小尺寸,避免下载与存储过大(手机上预览已足够清晰)。 + + // 本地测试用的皮肤数据:结构与后端最终返回的 JSON 一致。 + // 说明: + // - background_image:背景大图 URL(此处仍使用 picsum 测试) + // - 颜色字段:background/key_bg/key_text/key_highlight/accent 与 KBSkinManager.applyThemeFromJSON 一致 + // - hidden_keys:需要隐藏文字的按键 identifier + // - key_icons: + // * 当 KB_SKIN_ICON_USE_REMOTE==0 时,value 写本地 Assets 名称(如 "kb_space_melon") + // * 当 KB_SKIN_ICON_USE_REMOTE==1 时,value 写远程图标 URL(如 "https://.../icons/space.png") self.skins = @[ - @{ @"id": @"aurora", @"name": KBLocalized(@"极光"), @"img": @"https://picsum.photos/id/1018/800/450.jpg" }, - @{ @"id": @"alps", @"name": KBLocalized(@"雪山"), @"img": @"https://picsum.photos/id/1016/800/450.jpg" }, - @{ @"id": @"lake", @"name": KBLocalized(@"湖面"), @"img": @"https://picsum.photos/id/1039/800/450.jpg" }, + @{ + @"id": @"melon", + @"name": KBLocalized(@"蜜瓜主题"), + @"background_image": @"https://picsum.photos/id/1018/800/450.jpg", + @"background": @"#F5FFE8", + @"key_bg": @"#FFFFFF", + @"key_text": @"#4A4A4A", + @"key_highlight": @"#D9F4C4", + @"accent": @"#A4D68A", + @"hidden_keys": @[ + @"letter_q",@"letter_w",@"letter_e",@"letter_r",@"letter_t", + @"letter_y",@"letter_u",@"letter_i",@"letter_o",@"letter_p", + @"letter_a",@"letter_s",@"letter_d",@"letter_f",@"letter_g", + @"letter_h",@"letter_j",@"letter_k",@"letter_l", + @"letter_z",@"letter_x",@"letter_c",@"letter_v", + @"letter_b",@"letter_n",@"letter_m", + @"space" + ], + // 默认假设本地测试:这里的值写 Assets 名称。 + // 如果开启远程图标模式,可改为实际的图标 URL。 + @"key_icons": @{ + // 字母键:大小写共用一套本地图(演示用) + // 若后续需要不同图,只需改为 *_lower / *_upper 对应不同资源名即可。 + @"letter_q_lower": @"key_q", + @"letter_q_upper": @"key_q", + @"letter_w_lower": @"key_w", + @"letter_w_upper": @"key_w", + @"letter_e_lower": @"key_e", + @"letter_e_upper": @"key_e", + @"letter_r_lower": @"key_r", + @"letter_r_upper": @"key_r", + @"letter_t_lower": @"key_t", + @"letter_t_upper": @"key_t", + @"letter_y_lower": @"key_y", + @"letter_y_upper": @"key_y", + @"letter_u_lower": @"key_u", + @"letter_u_upper": @"key_u", + @"letter_i_lower": @"key_i", + @"letter_i_upper": @"key_i", + @"letter_o_lower": @"key_o", + @"letter_o_upper": @"key_o", + @"letter_p_lower": @"key_p", + @"letter_p_upper": @"key_p", + + @"letter_a_lower": @"key_a", + @"letter_a_upper": @"key_a", + @"letter_s_lower": @"key_s", + @"letter_s_upper": @"key_s", + @"letter_d_lower": @"key_d", + @"letter_d_upper": @"key_d", + @"letter_f_lower": @"key_f", + @"letter_f_upper": @"key_f", + @"letter_g_lower": @"key_g", + @"letter_g_upper": @"key_g", + @"letter_h_lower": @"key_h", + @"letter_h_upper": @"key_h", + @"letter_j_lower": @"key_j", + @"letter_j_upper": @"key_j", + @"letter_k_lower": @"key_k", + @"letter_k_upper": @"key_k", + @"letter_l_lower": @"key_l", + @"letter_l_upper": @"key_l", + + @"letter_z_lower": @"key_z", + @"letter_z_upper": @"key_z", + @"letter_x_lower": @"key_x", + @"letter_x_upper": @"key_x", + @"letter_c_lower": @"key_c", + @"letter_c_upper": @"key_c", + @"letter_v_lower": @"key_v", + @"letter_v_upper": @"key_v", + @"letter_b_lower": @"key_b", + @"letter_b_upper": @"key_b", + @"letter_n_lower": @"key_n", + @"letter_n_upper": @"key_n", + @"letter_m_lower": @"key_m", + @"letter_m_upper": @"key_m", + + // 功能键(无大小写变体) + @"space": @"key_space", // 空格键 + @"backspace": @"key_del", // 删除键 + @"shift": @"key_up", // Shift(上箭头) + @"mode_123": @"key_123", // 字母面板左下角 "123" + @"ai": @"key_ai", // 自定义 AI 键 + @"return": @"key_send" // 发送/换行键 + } + } ]; self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, KB_NAV_TOTAL_HEIGHT, KB_SCREEN_WIDTH, KB_SCREEN_HEIGHT - KB_NAV_TOTAL_HEIGHT) style:UITableViewStyleInsetGrouped]; @@ -78,55 +171,8 @@ NSInteger idx = sender.tag; if (idx < 0 || idx >= self.skins.count) return; NSDictionary *skin = self.skins[idx]; - NSString *path = skin[@"img"] ?: @""; // 相对 KB_BASE_URL - - // 下载图片数据(非 JSON 将以 NSData 返回) - [[KBNetworkManager shared] GET:path parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { - NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil); - // 尝试压缩尺寸,避免 Keychain 过大:将宽度限制到 1500px - if (data && data.length > 0) { - UIImage *img = [UIImage imageWithData:data]; - if (img) { - CGFloat maxW = 1500.0; - if (img.size.width > maxW) { - CGFloat scale = maxW / img.size.width; - CGSize newSize = CGSizeMake(maxW, floor(img.size.height * scale)); - UIGraphicsBeginImageContextWithOptions(newSize, YES, 1.0); - [img drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; - UIImage *resized = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - img = resized ?: img; - } - data = UIImageJPEGRepresentation(img, 0.85) ?: data; // 压成 JPEG - } - } - dispatch_async(dispatch_get_main_queue(), ^{ - NSData *payload = data; - if (payload.length == 0) { - // 兜底:生成一张简单渐变图片 - CGSize size = CGSizeMake(1200, 600); - UIGraphicsBeginImageContextWithOptions(size, YES, 1.0); - CGContextRef ctx = UIGraphicsGetCurrentContext(); - UIColor *c1 = [UIColor colorWithRed:0.76 green:0.91 blue:0.86 alpha:1]; - UIColor *c2 = [UIColor colorWithRed:0.93 green:0.97 blue:0.91 alpha:1]; - if ([skin[@"id"] hasPrefix:@"dark"]) { - c1 = [UIColor colorWithRed:0.1 green:0.12 blue:0.16 alpha:1]; - c2 = [UIColor colorWithRed:0.22 green:0.24 blue:0.28 alpha:1]; - } - CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - NSArray *colors = @[(__bridge id)c1.CGColor, (__bridge id)c2.CGColor]; - CGFloat locs[] = {0,1}; - CGGradientRef grad = CGGradientCreateWithColors(space, (__bridge CFArrayRef)colors, locs); - CGContextDrawLinearGradient(ctx, grad, CGPointZero, CGPointMake(size.width, size.height), 0); - CGGradientRelease(grad); CGColorSpaceRelease(space); - UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - payload = UIImageJPEGRepresentation(img, 0.9); - } - BOOL ok = (payload.length > 0) ? [[KBSkinManager shared] applyImageSkinWithData:payload skinId:skin[@"id"] name:skin[@"name"]] : NO; - [KBHUD showInfo:(ok ? KBLocalized(@"已应用,切到键盘查看") : KBLocalized(@"应用失败"))]; - }); - }]; + if (!skin) return; + [[KBSkinService shared] applySkinWithJSON:skin fromViewController:self completion:nil]; } @end diff --git a/keyBoard/Class/Manager/KBSkinService.h b/keyBoard/Class/Manager/KBSkinService.h new file mode 100644 index 0000000..7cb5fc8 --- /dev/null +++ b/keyBoard/Class/Manager/KBSkinService.h @@ -0,0 +1,34 @@ +// +// KBSkinService.h +// keyBoard +// +// 统一的键盘皮肤下载/应用服务。 +// - App 端调用,一处封装本地图标导出 / 网络图标下载 / 背景图下载逻辑 +// - 内部会检查键盘是否开启完全访问,并在不足时给出提示或引导 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^KBSkinApplyCompletion)(BOOL success); + +/// 皮肤下载与应用服务(仅主 App 使用) +@interface KBSkinService : NSObject + ++ (instancetype)shared; + +/// 根据后端返回的皮肤 JSON 下载并应用皮肤。 +/// +/// @param skinJSON 与后端约定的皮肤结构(包含 id/name/background_image/hidden_keys/key_icons 等) +/// @param presenting 用于弹出“键盘权限引导页”的控制器,可为 nil +/// @param completion 应用完成回调(下载/写入全部结束后调用,success 表示是否成功) +- (void)applySkinWithJSON:(NSDictionary *)skinJSON + fromViewController:(nullable UIViewController *)presenting + completion:(nullable KBSkinApplyCompletion)completion; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Manager/KBSkinService.m b/keyBoard/Class/Manager/KBSkinService.m new file mode 100644 index 0000000..ae38128 --- /dev/null +++ b/keyBoard/Class/Manager/KBSkinService.m @@ -0,0 +1,237 @@ +// +// KBSkinService.m +// keyBoard +// + +#import "KBSkinService.h" + +#import "KBSkinManager.h" +#import "KBConfig.h" +#import "KBKeyboardPermissionManager.h" +#import "KBNetworkManager.h" +#import "KBHUD.h" + +@implementation KBSkinService + ++ (instancetype)shared { + static KBSkinService *s; static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ s = [KBSkinService new]; }); + return s; +} + +- (void)applySkinWithJSON:(NSDictionary *)skinJSON + fromViewController:(UIViewController *)presenting + completion:(KBSkinApplyCompletion)completion { + if (skinJSON.count == 0) { + if (completion) completion(NO); + return; + } + + // 1. 点击应用皮肤时,检查键盘启用 & 完全访问状态,并尽量给出友好提示。 + KBKeyboardPermissionManager *perm = [KBKeyboardPermissionManager shared]; + BOOL enabled = [perm isKeyboardEnabled]; + KBFARecord fa = [perm lastKnownFullAccess]; + BOOL hasFullAccess = (fa == KBFARecordGranted); + + if (!enabled || !hasFullAccess) { + // 引导页(内部有自己的展示策略,避免过度打扰) + [perm presentPermissionIfNeededFrom:presenting]; + + // 简单提示:皮肤可以应用,但未开启完全访问时扩展无法读取 App Group 中的图片。 + [KBHUD showInfo:KBLocalized(@"皮肤已应用,键盘需开启“允许完全访问”后才能显示图片")]; + } + +#if KB_SKIN_ICON_USE_REMOTE + [self kb_applySkinUsingRemoteIcons:skinJSON completion:completion]; +#else + [self kb_applySkinUsingLocalIcons:skinJSON completion:completion]; +#endif +} + +#pragma mark - Internal helpers + +/// 本地模式:key_icons 的 value 为 Assets 名称(主 App bundle 内),导出到 App Group 后再写入主题。 +- (void)kb_applySkinUsingLocalIcons:(NSDictionary *)skin completion:(KBSkinApplyCompletion)completion { + NSString *skinId = skin[@"id"] ?: @"local"; + NSString *name = skin[@"name"] ?: skinId; + + NSDictionary *iconNames = [skin[@"key_icons"] isKindOfClass:NSDictionary.class] ? skin[@"key_icons"] : @{}; + NSMutableDictionary *iconPathMap = [NSMutableDictionary dictionary]; + + NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup]; + if (containerURL && iconNames.count > 0) { + NSString *skinsRoot = [containerURL.path stringByAppendingPathComponent:@"Skins"]; + NSString *skinRoot = [skinsRoot stringByAppendingPathComponent:skinId]; + NSString *iconsDir = [skinRoot stringByAppendingPathComponent:@"icons"]; + [[NSFileManager defaultManager] createDirectoryAtPath:iconsDir + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + + [iconNames enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSString *imageName, BOOL *stop) { + if (![imageName isKindOfClass:NSString.class] || imageName.length == 0) return; + UIImage *img = [UIImage imageNamed:imageName]; + if (!img) return; + NSData *data = UIImagePNGRepresentation(img); + if (data.length == 0) return; + NSString *fileName = [NSString stringWithFormat:@"%@.png", identifier]; + NSString *fullPath = [iconsDir stringByAppendingPathComponent:fileName]; + if ([data writeToFile:fullPath atomically:YES]) { + NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@", skinId, fileName]; + iconPathMap[identifier] = relative; + } + }]; + } + + NSMutableDictionary *themeJSON = [skin mutableCopy]; + if (iconPathMap.count > 0) { + themeJSON[@"key_icons"] = iconPathMap.copy; // value 改为 App Group 相对路径 + } + + // 先应用颜色 / hidden_keys / key_icons(此时为相对路径,扩展从 App Group 读取) + BOOL themeOK = [[KBSkinManager shared] applyThemeFromJSON:themeJSON]; + + // 再处理背景图片(background_image),与原逻辑一致 + NSString *bgURL = skin[@"background_image"] ?: @""; + if (bgURL.length == 0) { + if (completion) completion(themeOK); + if (themeOK) { + [KBHUD showInfo:KBLocalized(@"已应用皮肤(无背景图)")]; + } else { + [KBHUD showInfo:KBLocalized(@"应用皮肤失败")]; + } + return; + } + + [[KBNetworkManager shared] GET:bgURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { + NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil); + // 压缩尺寸,避免 Keychain 过大 + if (data && data.length > 0) { + UIImage *img = [UIImage imageWithData:data]; + if (img) { + CGFloat maxW = 1500.0; + if (img.size.width > maxW) { + CGFloat scale = maxW / img.size.width; + CGSize newSize = CGSizeMake(maxW, floor(img.size.height * scale)); + UIGraphicsBeginImageContextWithOptions(newSize, YES, 1.0); + [img drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *resized = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + img = resized ?: img; + } + data = UIImageJPEGRepresentation(img, 0.85) ?: data; + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + NSData *payload = data; + if (payload.length == 0) { + // 兜底:生成一张简单渐变图片 + CGSize size = CGSizeMake(1200, 600); + UIGraphicsBeginImageContextWithOptions(size, YES, 1.0); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + UIColor *c1 = [UIColor colorWithRed:0.76 green:0.91 blue:0.86 alpha:1]; + UIColor *c2 = [UIColor colorWithRed:0.93 green:0.97 blue:0.91 alpha:1]; + if ([skin[@"id"] hasPrefix:@"dark"]) { + c1 = [UIColor colorWithRed:0.1 green:0.12 blue:0.16 alpha:1]; + c2 = [UIColor colorWithRed:0.22 green:0.24 blue:0.28 alpha:1]; + } + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + NSArray *colors = @[(__bridge id)c1.CGColor, (__bridge id)c2.CGColor]; + CGFloat locs[] = {0,1}; + CGGradientRef grad = CGGradientCreateWithColors(space, (__bridge CFArrayRef)colors, locs); + CGContextDrawLinearGradient(ctx, grad, CGPointZero, CGPointMake(size.width, size.height), 0); + CGGradientRelease(grad); CGColorSpaceRelease(space); + UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + payload = UIImageJPEGRepresentation(img, 0.9); + } + BOOL ok = (payload.length > 0) + ? [[KBSkinManager shared] applyImageSkinWithData:payload skinId:skinId name:name] + : themeOK; + if (completion) completion(ok); + [KBHUD showInfo:(ok ? KBLocalized(@"已应用,切到键盘查看") : KBLocalized(@"应用皮肤失败"))]; + }); + }]; +} + +/// 远程模式:key_icons 的 value 为 URL,需要下载到 App Group,写入主题后再应用。 +- (void)kb_applySkinUsingRemoteIcons:(NSDictionary *)skin completion:(KBSkinApplyCompletion)completion { + NSString *skinId = skin[@"id"] ?: @"remote"; + NSString *name = skin[@"name"] ?: skinId; + NSString *bgURL = skin[@"background_image"] ?: @""; + NSDictionary *iconURLs = [skin[@"key_icons"] isKindOfClass:NSDictionary.class] ? skin[@"key_icons"] : @{}; + + dispatch_group_t group = dispatch_group_create(); + __block NSData *bgData = nil; + __block NSMutableDictionary *iconPathMap = [NSMutableDictionary dictionary]; + + // 背景图 + if (bgURL.length > 0) { + dispatch_group_enter(group); + [[KBNetworkManager shared] GET:bgURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { + NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil); + if (data.length > 0) { + UIImage *img = [UIImage imageWithData:data]; + if (img) { + CGFloat maxW = 1500.0; + if (img.size.width > maxW) { + CGFloat scale = maxW / img.size.width; + CGSize newSize = CGSizeMake(maxW, floor(img.size.height * scale)); + UIGraphicsBeginImageContextWithOptions(newSize, YES, 1.0); + [img drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *resized = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + img = resized ?: img; + } + data = UIImageJPEGRepresentation(img, 0.85) ?: data; + } + } + bgData = data; + dispatch_group_leave(group); + }]; + } + + // 图标下载到 App Group:Skins//icons/.png + [iconURLs enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSString *urlString, BOOL *stop) { + if (![urlString isKindOfClass:NSString.class] || urlString.length == 0) return; + dispatch_group_enter(group); + [[KBNetworkManager shared] GET:urlString parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { + NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil); + if (data.length > 0) { + NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup]; + if (containerURL) { + NSString *skinsRoot = [containerURL.path stringByAppendingPathComponent:@"Skins"]; + NSString *skinRoot = [skinsRoot stringByAppendingPathComponent:skinId]; + NSString *iconsDir = [skinRoot stringByAppendingPathComponent:@"icons"]; + [[NSFileManager defaultManager] createDirectoryAtPath:iconsDir + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + NSString *fileName = [NSString stringWithFormat:@"%@.png", identifier]; + NSString *fullPath = [iconsDir stringByAppendingPathComponent:fileName]; + if ([data writeToFile:fullPath atomically:YES]) { + NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@", skinId, fileName]; + iconPathMap[identifier] = relative; + } + } + } + dispatch_group_leave(group); + }]; + }]; + + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + NSMutableDictionary *themeJSON = [skin mutableCopy]; + themeJSON[@"key_icons"] = iconPathMap.copy; // value 改为 App Group 相对路径 + + BOOL themeOK = [[KBSkinManager shared] applyThemeFromJSON:themeJSON]; + BOOL ok = themeOK; + if (bgData.length > 0) { + ok = [[KBSkinManager shared] applyImageSkinWithData:bgData skinId:skinId name:name]; + } + if (completion) completion(ok); + [KBHUD showInfo:(ok ? KBLocalized(@"已应用,切到键盘查看") : KBLocalized(@"应用皮肤失败"))]; + }); +} + +@end +