diff --git a/CustomKeyboard/Info.plist b/CustomKeyboard/Info.plist index 1686c1d..c25c1ec 100644 --- a/CustomKeyboard/Info.plist +++ b/CustomKeyboard/Info.plist @@ -13,7 +13,7 @@ PrimaryLanguage en-US RequestsOpenAccess - + NSExtensionPointIdentifier com.apple.keyboard-service diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 10643b4..6e6eccc 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; }; 04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAAE2EAF86530089C901 /* Assets.xcassets */; }; 04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB12EAF86530089C901 /* LaunchScreen.storyboard */; }; 04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB42EAF86530089C901 /* Main.storyboard */; }; @@ -59,6 +60,8 @@ 04C6EADB2EAF8CEB0089C901 /* KBToolBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBToolBar.h; sourceTree = ""; }; 04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBToolBar.m; sourceTree = ""; }; 04C6EADE2EAF8D680089C901 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; + 04C6EAE02EAF940F0089C901 /* KBPermissionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPermissionViewController.h; sourceTree = ""; }; + 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPermissionViewController.m; sourceTree = ""; }; 727EC7532EAF848B00B36487 /* keyBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = keyBoard.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -83,6 +86,7 @@ 04C6EAB92EAF86530089C901 /* keyBoard */ = { isa = PBXGroup; children = ( + 04C6EAE32EAF942E0089C901 /* VC */, 04C6EAAC2EAF86530089C901 /* AppDelegate.h */, 04C6EAAD2EAF86530089C901 /* AppDelegate.m */, 04C6EAAE2EAF86530089C901 /* Assets.xcassets */, @@ -117,6 +121,15 @@ path = View; sourceTree = ""; }; + 04C6EAE32EAF942E0089C901 /* VC */ = { + isa = PBXGroup; + children = ( + 04C6EAE02EAF940F0089C901 /* KBPermissionViewController.h */, + 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */, + ); + path = VC; + sourceTree = ""; + }; 727EC74A2EAF848B00B36487 = { isa = PBXGroup; children = ( @@ -250,6 +263,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */, 04C6EABE2EAF86530089C901 /* AppDelegate.m in Sources */, 04C6EABF2EAF86530089C901 /* main.m in Sources */, 04C6EAC12EAF86530089C901 /* ViewController.m in Sources */, @@ -295,7 +309,7 @@ GCC_PREFIX_HEADER = CustomKeyboard/PrefixHeader.pch; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CustomKeyboard/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "卡拉斯输入法"; + INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 15; LD_RUNPATH_SEARCH_PATHS = ( @@ -321,7 +335,7 @@ GCC_PREFIX_HEADER = CustomKeyboard/PrefixHeader.pch; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CustomKeyboard/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "卡拉斯输入法"; + INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 15; LD_RUNPATH_SEARCH_PATHS = ( @@ -348,7 +362,7 @@ DEVELOPMENT_TEAM = TN6HHV45BB; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "卡拉斯输入法"; + INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -378,7 +392,7 @@ DEVELOPMENT_TEAM = TN6HHV45BB; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = keyBoard/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "卡拉斯输入法"; + INFOPLIST_KEY_CFBundleDisplayName = "我的输入法"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index 9e49607..72c1d94 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -7,10 +7,25 @@ #import "AppDelegate.h" #import "ViewController.h" +#import "KBPermissionViewController.h" -@interface AppDelegate () +static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoard.CustomKeyboard"; +static BOOL KBIsKeyboardEnabled(void) { + for (UITextInputMode *mode in [UITextInputMode activeInputModes]) { + NSString *identifier = nil; + @try { + identifier = [mode valueForKey:@"identifier"]; // not a public API + } @catch (__unused NSException *e) { + identifier = nil; + } + if ([identifier isKindOfClass:[NSString class]] && + [identifier rangeOfString:kKBKeyboardExtensionBundleId].location != NSNotFound) { + return YES; + } + } + return NO; +} -@end @implementation AppDelegate @@ -22,6 +37,15 @@ [self.window makeKeyAndVisible]; ViewController *vc = [[ViewController alloc] init]; self.window.rootViewController = vc; + + // Present guide if the custom keyboard is not enabled yet + dispatch_async(dispatch_get_main_queue(), ^{ + if (!KBIsKeyboardEnabled()) { + KBPermissionViewController *guide = [KBPermissionViewController new]; + guide.modalPresentationStyle = UIModalPresentationFullScreen; + [self.window.rootViewController presentViewController:guide animated:YES completion:nil]; + } + }); return YES; } diff --git a/keyBoard/VC/KBPermissionViewController.h b/keyBoard/VC/KBPermissionViewController.h new file mode 100644 index 0000000..e2a77a1 --- /dev/null +++ b/keyBoard/VC/KBPermissionViewController.h @@ -0,0 +1,16 @@ +// +// KBPermissionViewController.h +// CustomKeyboard +// +// Created by Mac on 2025/10/27. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBPermissionViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/VC/KBPermissionViewController.m b/keyBoard/VC/KBPermissionViewController.m new file mode 100644 index 0000000..275bd19 --- /dev/null +++ b/keyBoard/VC/KBPermissionViewController.m @@ -0,0 +1,115 @@ +// +// KBPermissionViewController.m +// CustomKeyboard +// +// Created by Mac on 2025/10/27. +// + +#import "KBPermissionViewController.h" +#import + +@interface KBPermissionViewController () + +@end + +@implementation KBPermissionViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0]; + + // Close button (top-right) + UIButton *close = [UIButton buttonWithType:UIButtonTypeSystem]; + [close setTitle:@"X" forState:UIControlStateNormal]; + close.titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold]; + close.tintColor = [UIColor darkTextColor]; + close.frame = CGRectMake(self.view.bounds.size.width - 44, 44, 28, 28); + close.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; + [close addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:close]; + + // Title + UILabel *title = [[UILabel alloc] init]; + title.text = @"启用输入法"; + title.font = [UIFont systemFontOfSize:22 weight:UIFontWeightSemibold]; + title.textColor = [UIColor blackColor]; + title.textAlignment = NSTextAlignmentCenter; + title.frame = CGRectMake(24, 100, self.view.bounds.size.width - 48, 28); + title.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [self.view addSubview:title]; + + // Step tips + UILabel *tips = [[UILabel alloc] init]; + tips.text = @"1 开启键盘 > 2 允许完全访问"; + tips.font = [UIFont systemFontOfSize:14]; + tips.textColor = [UIColor darkGrayColor]; + tips.textAlignment = NSTextAlignmentCenter; + tips.frame = CGRectMake(24, CGRectGetMaxY(title.frame) + 8, self.view.bounds.size.width - 48, 20); + tips.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [self.view addSubview:tips]; + + // Illustration placeholder (simple rounded rect) + UIView *card = [[UIView alloc] initWithFrame:CGRectMake(32, CGRectGetMaxY(tips.frame) + 28, self.view.bounds.size.width - 64, 260)]; + card.backgroundColor = [UIColor whiteColor]; + card.layer.cornerRadius = 16; + card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor; + card.layer.shadowOpacity = 1; + card.layer.shadowRadius = 12; + [self.view addSubview:card]; + + // Button to open Settings + UIButton *open = [UIButton buttonWithType:UIButtonTypeSystem]; + [open setTitle:@"去设置中开启" forState:UIControlStateNormal]; + open.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold]; + [open setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + open.backgroundColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0]; + open.layer.cornerRadius = 8; + CGFloat btnW = self.view.bounds.size.width - 64; + open.frame = CGRectMake(32, CGRectGetMaxY(card.frame) + 32, btnW, 48); + open.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + [open addTarget:self action:@selector(openSettings) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:open]; + + // Bottom helper text + UILabel *help = [[UILabel alloc] init]; + help.text = @"没有找到键盘? 请前往 设置 > 通用 > 键盘 > 键盘 > 添加新键盘"; + help.font = [UIFont systemFontOfSize:12]; + help.textColor = [UIColor grayColor]; + help.textAlignment = NSTextAlignmentCenter; + help.numberOfLines = 2; + help.frame = CGRectMake(24, CGRectGetMaxY(open.frame) + 12, self.view.bounds.size.width - 48, 36); + help.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + [self.view addSubview:help]; +} + +- (void)dismissSelf { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)openSettings { + // In app extensions you cannot call UIApplication. Use extension-safe code. + NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + +//#if TARGET_OS_APP_EXTENSION +// // Try to open Settings via the extension context (extension-safe). This may +// // fail depending on the extension type and system policy. +// if (self.extensionContext) { +// if (@available(iOS 10.0, *)) { +// [self.extensionContext openURL:url completionHandler:nil]; +// } else { +// [self.extensionContext openURL:url completionHandler:nil]; +// } +// } +//#else + UIApplication *app = [UIApplication sharedApplication]; + if ([app canOpenURL:url]) { + if (@available(iOS 10.0, *)) { + [app openURL:url options:@{} completionHandler:nil]; + } else { + [app openURL:url]; + } + } +//#endif +} + +@end