处理键盘不能拉起主app的问题
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>kbkeyboardAppExtension</string>
|
||||
</array>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
|
||||
@@ -52,8 +52,17 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20;
|
||||
|
||||
|
||||
- (void)setupUI {
|
||||
// 固定键盘整体高度
|
||||
[self.view.heightAnchor constraintEqualToConstant:KEYBOARDHEIGHT].active = YES;
|
||||
// 固定键盘整体高度;为减少与系统自调整高度产生的告警,将优先级设置为 High
|
||||
NSLayoutConstraint *h = [self.view.heightAnchor constraintEqualToConstant:KEYBOARDHEIGHT];
|
||||
h.priority = UILayoutPriorityDefaultHigh; // 750
|
||||
h.active = YES;
|
||||
// 关闭 UIInputView 自适应(某些系统版本会尝试放大为全屏高度导致冲突)
|
||||
if ([self.view isKindOfClass:[UIInputView class]]) {
|
||||
UIInputView *iv = (UIInputView *)self.view;
|
||||
if ([iv respondsToSelector:@selector(setAllowsSelfSizing:)]) {
|
||||
iv.allowsSelfSizing = NO;
|
||||
}
|
||||
}
|
||||
// 背景图铺底
|
||||
[self.view addSubview:self.bgImageView];
|
||||
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
@@ -222,7 +231,8 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20;
|
||||
}
|
||||
|
||||
- (void)kb_tryOpenContainerForLoginIfNeeded {
|
||||
NSURL *url = [NSURL URLWithString:@"kbkeyboard://login?src=keyboard"];
|
||||
// 使用与主 App 一致的自定义 Scheme
|
||||
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=keyboard", KB_APP_SCHEME]];
|
||||
if (!url) return;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.extensionContext openURL:url completionHandler:^(__unused BOOL success) {
|
||||
|
||||
@@ -23,9 +23,15 @@
|
||||
|
||||
// 通用链接(Universal Links)统一配置
|
||||
// 配置好 AASA 与 Associated Domains 后,只需修改这里即可切换域名/path。
|
||||
#define KB_UL_BASE @"https://your.domain/ul" // 替换为你的真实域名与前缀路径
|
||||
#define KB_UL_BASE @"https://app.tknb.net/ul" // 与 Associated Domains 一致
|
||||
#define KB_UL_LOGIN KB_UL_BASE @"/login"
|
||||
#define KB_UL_SETTINGS KB_UL_BASE @"/settings"
|
||||
|
||||
// 在扩展内,启用 URL Bridge(仅在显式的用户点击动作中使用)
|
||||
// 这样即便宿主 App(如备忘录)拒绝 extensionContext 的 openURL,仍可通过响应链兜底拉起容器 App。
|
||||
#ifndef KB_URL_BRIDGE_ENABLE
|
||||
#define KB_URL_BRIDGE_ENABLE 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* PrefixHeader_pch */
|
||||
|
||||
30
CustomKeyboard/Utils/KBURLOpenBridge.h
Normal file
30
CustomKeyboard/Utils/KBURLOpenBridge.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// KBURLOpenBridge.h
|
||||
// 非公开:通过响应链查找 `openURL:` 选择器,尝试在扩展环境中打开自定义 scheme。
|
||||
// 警告:存在审核风险。默认仅 Debug 启用(见 KB_URL_BRIDGE_ENABLE)。
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifndef KB_URL_BRIDGE_ENABLE
|
||||
#if DEBUG
|
||||
#define KB_URL_BRIDGE_ENABLE 1
|
||||
#else
|
||||
#define KB_URL_BRIDGE_ENABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@interface KBURLOpenBridge : NSObject
|
||||
|
||||
/// 尝试通过响应链调用 openURL:(仅在 KB_URL_BRIDGE_ENABLE 为 1 时执行)。
|
||||
/// @param url 自定义 scheme,如 kbkeyboard://settings
|
||||
/// @param start 起始 responder(传 self 或任意视图)
|
||||
/// @return 是否看起来已发起打开动作(不保证一定成功)
|
||||
+ (BOOL)openURLViaResponder:(NSURL *)url from:(UIResponder *)start;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
46
CustomKeyboard/Utils/KBURLOpenBridge.m
Normal file
46
CustomKeyboard/Utils/KBURLOpenBridge.m
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// KBURLOpenBridge.m
|
||||
//
|
||||
|
||||
#import "KBURLOpenBridge.h"
|
||||
#import <objc/message.h>
|
||||
|
||||
@implementation KBURLOpenBridge
|
||||
|
||||
+ (BOOL)openURLViaResponder:(NSURL *)url from:(UIResponder *)start {
|
||||
#if KB_URL_BRIDGE_ENABLE
|
||||
if (!url || !start) return NO;
|
||||
SEL sel = NSSelectorFromString(@"openURL:");
|
||||
UIResponder *responder = start;
|
||||
while (responder) {
|
||||
@try {
|
||||
if ([responder respondsToSelector:sel]) {
|
||||
// 尽量按签名调用;若失败则回退 performSelector
|
||||
BOOL handled = NO;
|
||||
// 尝试 (BOOL)openURL:(NSURL *)
|
||||
BOOL (*funcBool)(id, SEL, NSURL *) = (BOOL (*)(id, SEL, NSURL *))objc_msgSend;
|
||||
if (funcBool) {
|
||||
handled = funcBool(responder, sel, url);
|
||||
} else {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[responder performSelector:sel withObject:url];
|
||||
handled = YES;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
} @catch (__unused NSException *e) {
|
||||
// ignore and continue
|
||||
}
|
||||
responder = responder.nextResponder;
|
||||
}
|
||||
return NO;
|
||||
#else
|
||||
(void)url; (void)start;
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#import "KBFullAccessGuideView.h"
|
||||
#import "Masonry.h"
|
||||
#import "KBResponderUtils.h" // 统一查找 UIInputViewController 的工具
|
||||
#import "KBHUD.h"
|
||||
#import "KBURLOpenBridge.h"
|
||||
|
||||
@interface KBFullAccessGuideView ()
|
||||
@property (nonatomic, strong) UIControl *backdrop;
|
||||
@property (nonatomic, strong) UIView *card;
|
||||
// 预先保存当前键盘控制器,避免运行时通过响应链找不到
|
||||
@property (nonatomic, weak) UIInputViewController *ivc;
|
||||
@end
|
||||
|
||||
@implementation KBFullAccessGuideView
|
||||
@@ -110,7 +114,8 @@
|
||||
}
|
||||
|
||||
- (void)presentIn:(UIView *)parent {
|
||||
UIView *container = parent.window ?: parent;
|
||||
if (!parent) return;
|
||||
UIView *container = parent; // 关键:加到键盘视图树中,而不是 window
|
||||
self.frame = container.bounds;
|
||||
self.alpha = 0;
|
||||
[container addSubview:self];
|
||||
@@ -126,15 +131,19 @@
|
||||
|
||||
+ (void)showInView:(UIView *)parent {
|
||||
if (!parent) return;
|
||||
// 避免重复
|
||||
for (UIView *v in (parent.window ?: parent).subviews) {
|
||||
// 避免重复(仅在 parent 层级检查)
|
||||
for (UIView *v in parent.subviews) {
|
||||
if ([v isKindOfClass:[KBFullAccessGuideView class]]) return;
|
||||
}
|
||||
[[KBFullAccessGuideView build] presentIn:parent];
|
||||
KBFullAccessGuideView *view = [KBFullAccessGuideView build];
|
||||
// 预取 ivc
|
||||
view.ivc = KBFindInputViewController(parent);
|
||||
[view presentIn:parent];
|
||||
}
|
||||
|
||||
+ (void)dismissFromView:(UIView *)parent {
|
||||
UIView *container = parent.window ?: parent;
|
||||
UIView *container = parent;
|
||||
if (!container) return;
|
||||
for (UIView *v in container.subviews) {
|
||||
if ([v isKindOfClass:[KBFullAccessGuideView class]]) {
|
||||
[(KBFullAccessGuideView *)v dismiss];
|
||||
@@ -148,30 +157,73 @@
|
||||
#pragma mark - Actions
|
||||
|
||||
// 工具方法已提取到 KBResponderUtils.h
|
||||
|
||||
// 打开主 App,引导用户去系统设置开启完全访问:优先 Scheme,失败再试 UL;仍失败则提示手动路径。
|
||||
- (void)onTapGoEnable {
|
||||
// 在扩展中无法使用 UIApplication。改为委托宿主打开链接:
|
||||
// 方案:优先拉起主 App 并由主 App 打开设置页,避免宿主拦截。
|
||||
UIInputViewController *ivc = KBFindInputViewController(self);
|
||||
if (!ivc) { [self dismiss]; return; }
|
||||
|
||||
// 先尝试 Universal Link(如未配置可改为你的域名),失败再用自定义 scheme。
|
||||
|
||||
// 自定义 Scheme(App 里在 openURL 中转到设置页)
|
||||
// 统一使用主 App 的自定义 Scheme
|
||||
NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//settings?src=kb_extension", KB_APP_SCHEME]];
|
||||
// Universal Link(需 AASA/Associated Domains 配置且 KB_UL_BASE 与域名一致)
|
||||
NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=kb_extension", KB_UL_SETTINGS]];
|
||||
void (^fallback)(void) = ^{
|
||||
NSURL *scheme = [NSURL URLWithString:@"kbkeyboard://settings?src=kb_extension"]; // 主App在 openURL 中处理
|
||||
[ivc.extensionContext openURL:scheme completionHandler:^(__unused BOOL ok2) {
|
||||
// 无论成功与否,都收起当前提示层,避免遮挡
|
||||
[self dismiss];
|
||||
}];
|
||||
|
||||
void (^finish)(BOOL) = ^(BOOL ok){
|
||||
if (ok) { [self dismiss]; }
|
||||
else {
|
||||
[KBHUD showInfo:@"无法自动打开,请按路径:设置→通用→键盘→键盘→恋爱键盘→允许完全访问"]; // 失败兜底提示
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 先试 Scheme(更可能被宿主允许直接拉起 App)
|
||||
if (scheme) {
|
||||
[ivc.extensionContext openURL:scheme completionHandler:^(BOOL ok) {
|
||||
if (ok) { finish(YES); return; }
|
||||
if (ul) {
|
||||
[ivc.extensionContext openURL:ul completionHandler:^(BOOL ok2) {
|
||||
if (ok2) { finish(YES); return; }
|
||||
// 兜底:在用户点击触发的场景下,尝试通过响应链调用 openURL:
|
||||
BOOL bridged = NO;
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
bridged = [KBURLOpenBridge openURLViaResponder:scheme from:self];
|
||||
if (!bridged && ul) {
|
||||
bridged = [KBURLOpenBridge openURLViaResponder:ul from:self];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
} @catch (__unused NSException *e) { bridged = NO; }
|
||||
finish(bridged);
|
||||
}];
|
||||
} else {
|
||||
// 没有 UL,则直接尝试桥接 Scheme
|
||||
BOOL bridged = NO;
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
bridged = [KBURLOpenBridge openURLViaResponder:scheme from:self];
|
||||
#pragma clang diagnostic pop
|
||||
} @catch (__unused NSException *e) { bridged = NO; }
|
||||
finish(bridged);
|
||||
}
|
||||
}];
|
||||
return;
|
||||
}
|
||||
// 无 scheme 时,直接尝试 UL
|
||||
if (ul) {
|
||||
[ivc.extensionContext openURL:ul completionHandler:^(BOOL ok) {
|
||||
if (ok) { [self dismiss]; }
|
||||
else { fallback(); }
|
||||
if (ok) { finish(YES); return; }
|
||||
BOOL bridged = NO;
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
bridged = [KBURLOpenBridge openURLViaResponder:ul from:self];
|
||||
#pragma clang diagnostic pop
|
||||
} @catch (__unused NSException *e) { bridged = NO; }
|
||||
finish(bridged);
|
||||
}];
|
||||
} else {
|
||||
fallback();
|
||||
finish(NO);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "KBFullAccessGuideView.h"
|
||||
#import "KBFullAccessManager.h"
|
||||
#import "KBSkinManager.h"
|
||||
#import "KBURLOpenBridge.h" // 兜底从扩展侧直接尝试 openURL:
|
||||
|
||||
static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
|
||||
@@ -49,6 +50,9 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
|
||||
// 初始化剪贴板监控状态
|
||||
_lastHandledPBCount = [UIPasteboard generalPasteboard].changeCount;
|
||||
|
||||
// 监听“完全访问”状态变化,动态启停剪贴板监控,避免在未开完全访问时触发 TCC/XPC 错误日志
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_fullAccessChanged) name:KBFullAccessChangedNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -64,6 +68,7 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopPasteboardMonitor];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - UI
|
||||
@@ -92,10 +97,11 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
[self.rightButtonContainer addSubview:self.clearButtonInternal];
|
||||
[self.rightButtonContainer addSubview:self.sendButtonInternal];
|
||||
|
||||
// 竖向排布:粘贴、删除、清空为等高,发送略高
|
||||
// 竖向排布:粘贴、删除、清空为等高;发送优先更高,但允许在空间不足时压缩
|
||||
CGFloat smallH = 44;
|
||||
CGFloat bigH = 56;
|
||||
CGFloat vSpace = 10;
|
||||
// 原 10 在键盘总高度 276 下容易超出容器,改为 8 以避免 AutoLayout 冲突
|
||||
CGFloat vSpace = 8;
|
||||
[self.pasteButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.rightButtonContainer.mas_top);
|
||||
make.left.right.equalTo(self.rightButtonContainer);
|
||||
@@ -114,8 +120,10 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
[self.sendButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.clearButtonInternal.mas_bottom).offset(vSpace);
|
||||
make.left.right.equalTo(self.rightButtonContainer);
|
||||
make.height.mas_equalTo(bigH);
|
||||
make.bottom.lessThanOrEqualTo(self.rightButtonContainer.mas_bottom); // 底部可伸缩
|
||||
// 允许在空间不足时缩短到 smallH,避免产生约束冲突
|
||||
make.height.greaterThanOrEqualTo(@(smallH));
|
||||
make.height.lessThanOrEqualTo(@(bigH));
|
||||
make.bottom.lessThanOrEqualTo(self.rightButtonContainer.mas_bottom);
|
||||
}];
|
||||
|
||||
// 2. 粘贴区(位于右侧按钮左侧)
|
||||
@@ -193,9 +201,22 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
[ivc.extensionContext openURL:ul completionHandler:^(BOOL ok) {
|
||||
if (ok) return; // Universal Link 成功
|
||||
|
||||
NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"kbkeyboard://login?src=functionView&index=%ld&title=%@", (long)indexPath.item, encodedTitle]];
|
||||
// 统一使用主 App 注册的自定义 Scheme
|
||||
NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=functionView&index=%ld&title=%@", KB_APP_SCHEME, (long)indexPath.item, encodedTitle]];
|
||||
[ivc.extensionContext openURL:scheme completionHandler:^(BOOL ok2) {
|
||||
if (!ok2) {
|
||||
if (ok2) return;
|
||||
|
||||
// 兜底:在用户点击触发的场景下,尝试通过响应链调用 openURL:
|
||||
// 以提升在“备忘录”等宿主中的成功率。
|
||||
BOOL bridged = NO;
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
bridged = [KBURLOpenBridge openURLViaResponder:scheme from:self];
|
||||
#pragma clang diagnostic pop
|
||||
} @catch (__unused NSException *e) { bridged = NO; }
|
||||
|
||||
if (!bridged) {
|
||||
// 两条路都失败:大概率未开完全访问或宿主拦截。统一交由 Manager 引导。
|
||||
dispatch_async(dispatch_get_main_queue(), ^{ [[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self]; });
|
||||
}
|
||||
@@ -235,6 +256,8 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
// - 无论允许/拒绝,都把本次 changeCount 记为已处理,避免一直重复询问。
|
||||
|
||||
- (void)startPasteboardMonitor {
|
||||
// 未开启“完全访问”时不做自动读取,避免宿主/系统拒绝并刷错误日志
|
||||
if (![[KBFullAccessManager shared] hasFullAccess]) return;
|
||||
if (self.pasteboardTimer) return;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.pasteboardTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
|
||||
@@ -259,24 +282,30 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
|
||||
- (void)didMoveToWindow {
|
||||
[super didMoveToWindow];
|
||||
if (self.window && !self.isHidden) {
|
||||
[self startPasteboardMonitor];
|
||||
} else {
|
||||
[self stopPasteboardMonitor];
|
||||
}
|
||||
[self kb_refreshPasteboardMonitor];
|
||||
}
|
||||
|
||||
- (void)setHidden:(BOOL)hidden {
|
||||
BOOL wasHidden = self.isHidden;
|
||||
[super setHidden:hidden];
|
||||
if (wasHidden != hidden) {
|
||||
if (!hidden && self.window) {
|
||||
[self startPasteboardMonitor];
|
||||
} else {
|
||||
[self stopPasteboardMonitor];
|
||||
}
|
||||
[self kb_refreshPasteboardMonitor];
|
||||
}
|
||||
}
|
||||
|
||||
// 根据窗口可见性与完全访问状态,统一启停粘贴板监控
|
||||
- (void)kb_refreshPasteboardMonitor {
|
||||
BOOL visible = (self.window && !self.isHidden);
|
||||
if (visible && [[KBFullAccessManager shared] hasFullAccess]) {
|
||||
[self startPasteboardMonitor];
|
||||
} else {
|
||||
[self stopPasteboardMonitor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)kb_fullAccessChanged {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{ [self kb_refreshPasteboardMonitor]; });
|
||||
}
|
||||
- (void)onTapDelete {
|
||||
NSLog(@"点击:删除");
|
||||
UIInputViewController *ivc = KBFindInputViewController(self);
|
||||
|
||||
@@ -264,6 +264,9 @@
|
||||
if (firstChar) {
|
||||
for (KBKeyButton *b in row.subviews) {
|
||||
if (![b isKindOfClass:[KBKeyButton class]]) continue;
|
||||
// 当本行没有字符键时,firstChar 可能是一个“特殊键”,
|
||||
// 避免对基准按钮自身添加 self == self * k 的无效约束
|
||||
if (b == firstChar) continue;
|
||||
if (b.key.type == KBKeyTypeCharacter) continue;
|
||||
CGFloat multiplier = 1.5;
|
||||
if (b.key.type == KBKeyTypeSpace) multiplier = 4.0;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// Universal Links 通用链接
|
||||
#ifndef KB_UL_BASE
|
||||
#define KB_UL_BASE @"https://your.domain/ul"
|
||||
#define KB_UL_BASE @"https://app.tknb.net/ul"
|
||||
#endif
|
||||
|
||||
#define KB_UL_LOGIN KB_UL_BASE @"/login"
|
||||
@@ -37,6 +37,14 @@
|
||||
#define KB_KEYBOARD_EXTENSION_BUNDLE_ID @"com.loveKey.nyx.CustomKeyboard"
|
||||
#endif
|
||||
|
||||
// --- 应用自定义 Scheme ---
|
||||
// 主 App 在 Info.plist 中注册的 URL Scheme,用于从键盘扩展唤起容器 App。
|
||||
// 注意:AppDelegate 中对 scheme 做了小写化比较(kbkeyboardappextension),iOS 对大小写不敏感;
|
||||
// 这里统一通过宏引用,避免出现与 App 端不一致的字符串。
|
||||
#ifndef KB_APP_SCHEME
|
||||
#define KB_APP_SCHEME @"kbkeyboardAppExtension"
|
||||
#endif
|
||||
|
||||
// --- 常用宏 ---
|
||||
// 弱引用 self(在 block 中避免循环引用):使用处直接写 KBWeakSelf;
|
||||
#ifndef KBWeakSelf
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */; };
|
||||
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
||||
0459D1B82EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
||||
0477BD952EBAFF4E0055D639 /* KBURLOpenBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BD932EBAFF4E0055D639 /* KBURLOpenBridge.m */; };
|
||||
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97082EB31B14007BD342 /* KBHUD.m */; };
|
||||
04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */; };
|
||||
04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */; };
|
||||
@@ -94,6 +95,8 @@
|
||||
0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinCenterVC.m; sourceTree = "<group>"; };
|
||||
0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = "<group>"; };
|
||||
0459D1B62EBA287900F2D189 /* KBSkinManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinManager.m; sourceTree = "<group>"; };
|
||||
0477BD922EBAFF4E0055D639 /* KBURLOpenBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBURLOpenBridge.h; sourceTree = "<group>"; };
|
||||
0477BD932EBAFF4E0055D639 /* KBURLOpenBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBURLOpenBridge.m; 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>"; };
|
||||
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = "<group>"; };
|
||||
@@ -217,6 +220,15 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0477BD942EBAFF4E0055D639 /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0477BD922EBAFF4E0055D639 /* KBURLOpenBridge.h */,
|
||||
0477BD932EBAFF4E0055D639 /* KBURLOpenBridge.m */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04A9FE122EB4D0D20020DB6D /* Manager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -257,6 +269,7 @@
|
||||
04C6EAD72EAF870B0089C901 /* CustomKeyboard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0477BD942EBAFF4E0055D639 /* Utils */,
|
||||
04A9FE122EB4D0D20020DB6D /* Manager */,
|
||||
04FC95662EB0546C007BD342 /* Model */,
|
||||
04C6EADA2EAF8C7B0089C901 /* View */,
|
||||
@@ -842,6 +855,7 @@
|
||||
A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */,
|
||||
04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */,
|
||||
A1B2C4202EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */,
|
||||
0477BD952EBAFF4E0055D639 /* KBURLOpenBridge.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1005,7 +1019,7 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = keyBoard/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法";
|
||||
INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.loveKey.nyx.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboard\n );\n}";
|
||||
INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.loveKey.nyx.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboardAppExtension\n );\n}";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
@@ -1044,7 +1058,7 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = keyBoard/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "YOLO输入法";
|
||||
INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.loveKey.nyx.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboard\n );\n}";
|
||||
INFOPLIST_KEY_CFBundleURLTypes = "{\n CFBundleURLName = \"com.loveKey.nyx.keyboard\";\n CFBundleURLSchemes = (\n kbkeyboardAppExtension\n );\n}";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.7">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -27,8 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@@ -38,18 +36,20 @@
|
||||
ReferencedContainer = "container:keyBoard.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableGPUFrameCaptureMode = "3"
|
||||
enableGPUValidationMode = "1"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUFrameCaptureMode = "3"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
@@ -87,4 +87,3 @@
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
||||
|
||||
@@ -61,10 +61,25 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomK
|
||||
|
||||
#pragma mark - Deep Link
|
||||
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
NSURL *url = userActivity.webpageURL;
|
||||
if (!url) return NO;
|
||||
NSString *host = url.host.lowercaseString ?: @"";
|
||||
if ([host hasSuffix:@"app.tknb.net"]) {
|
||||
NSString *path = url.path.lowercaseString ?: @"";
|
||||
if ([path hasPrefix:@"/ul/settings"]) { [self kb_openAppSettings]; return YES; }
|
||||
if ([path hasPrefix:@"/ul/login"]) { [self kb_presentLoginSheetIfNeeded]; return YES; }
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// iOS 9+
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
if (!url) return NO;
|
||||
if ([[url.scheme lowercaseString] isEqualToString:@"kbkeyboard"]) {
|
||||
// 注意:已对 scheme 做了小写化,比较值也应为小写
|
||||
if ([[url.scheme lowercaseString] isEqualToString:@"kbkeyboardappextension"]) {
|
||||
NSString *urlHost = url.host ?: @"";
|
||||
NSString *host = [urlHost lowercaseString];
|
||||
if ([host isEqualToString:@"login"]) { // kbkeyboard://login
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.loveKey.nyx.keyboard</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>kbkeyboard</string>
|
||||
<string>kbkeyboardAppExtension</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
//-----------------------------------------------宏定义全局----------------------------------------------------------/
|
||||
// 通用链接(Universal Links)统一配置
|
||||
// 仅需修改这里的域名/前缀,工程内所有使用 UL 的地方都会同步。
|
||||
#define KB_UL_BASE @"https://your.domain/ul" // 替换为你的真实域名与前缀路径
|
||||
#define KB_UL_BASE @"https://app.tknb.net/ul" // 与 Associated Domains 一致
|
||||
#define KB_UL_LOGIN KB_UL_BASE @"/login"
|
||||
#define KB_UL_SETTINGS KB_UL_BASE @"/settings"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user