This commit is contained in:
2025-11-14 16:34:01 +08:00
parent eacac8425c
commit 66a1ddef66
16 changed files with 262 additions and 55 deletions

View File

@@ -19,7 +19,8 @@
#import "KBULBridge.h" // Darwin UL
#import "LSTPopView.h"
#import "KBLoginPopView.h"
#import "IAPVerifyTransactionObj.h"
#import "FGIAPManager.h"
// bundle id target
// PRODUCT_BUNDLE_IDENTIFIER
// CustomKeyboard target com.loveKey.nyx.CustomKeyboard
@@ -29,7 +30,8 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomK
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.
[[FGIAPManager shared] setConfigureWith:[IAPVerifyTransactionObj new]];
[self setupRootVC];
// 访
[KBNetworkManager shared].enabled = YES;

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "close_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "close_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "qx_ax_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "qx_ax_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "qx_bg_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "qx_bg_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@@ -9,7 +9,7 @@
NS_ASSUME_NONNULL_BEGIN
@interface KBGuideVC : UIViewController
@interface KBGuideVC : BaseViewController
@end

View File

@@ -29,6 +29,8 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *items; // [{type, text}]
///
@property (nonatomic, strong, nullable) KBPermissionViewController *permVC;
///
@property (nonatomic, copy, nullable) NSString *kb_lastInputModeIdentifier;
@end
@@ -45,8 +47,9 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
[self.inputBar addSubview:self.textField];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.equalTo(self.view);
make.left.right.equalTo(self.view);
make.bottom.equalTo(self.view);
make.top.mas_equalTo(KB_NAV_TOTAL_HEIGHT);
}];
[self.inputBar mas_makeConstraints:^(MASConstraintMaker *make) {
@@ -83,8 +86,17 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
// /
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_checkKeyboardPermission) name:UIApplicationDidBecomeActiveNotification object:nil];
// 使
NSArray<NSString *> *modeNotiNames = @[ @"UITextInputCurrentInputModeDidChangeNotification",
@"UITextInputCurrentInputModeDidChange" ];
for (NSString *n in modeNotiNames) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_inputModeDidChange:) name:n object:nil];
}
//
[self kb_preparePermissionOverlayIfNeeded];
}
- (void)dealloc {
@@ -95,6 +107,19 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
[super viewWillAppear:animated];
//
[self kb_checkKeyboardPermission];
// /
BOOL permissionReady = (self.permVC && self.permVC.view.hidden == YES);
if (permissionReady) {
// textInputMode
if (![self.textField isFirstResponder]) {
[self.textField becomeFirstResponder];
}
// 线
// UIKeyboardWillChangeFrame
dispatch_async(dispatch_get_main_queue(), ^{
[self kb_evaluateCurrentInputModeAndNotifyIfNeeded];
});
}
}
///
@@ -179,6 +204,8 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
// self.tableView.scrollIndicatorInsets = inset;
} completion:^(BOOL finished) {
[self scrollToBottomAnimated:YES];
//
[self kb_evaluateCurrentInputModeAndNotifyIfNeeded];
}];
}
@@ -191,6 +218,58 @@ typedef NS_ENUM(NSInteger, KBGuideItemType) {
}
}
#pragma mark - Keyboard Detection
///
///
/// - `UITextField.textInputMode`
/// - KVC `identifier` bundle id
/// - `textField`
- (BOOL)kb_isMyExtensionKeyboardSelected {
UITextInputMode *mode = self.textField.textInputMode;
if (!mode) { return NO; }
NSString *identifier = nil;
@try {
// App
identifier = [mode valueForKey:@"identifier"];
} @catch (__unused NSException *e) {
identifier = nil;
}
if (![identifier isKindOfClass:[NSString class]]) { return NO; }
return [identifier rangeOfString:KB_KEYBOARD_EXTENSION_BUNDLE_ID].location != NSNotFound;
}
- (void)kb_inputModeDidChange:(NSNotification *)note {
//
[self kb_evaluateCurrentInputModeAndNotifyIfNeeded];
}
/// identifier nil
- (NSString *)kb_currentInputModeIdentifier {
UITextInputMode *mode = self.textField.textInputMode;
if (!mode) return nil;
NSString *identifier = nil;
@try { identifier = [mode valueForKey:@"identifier"]; } @catch (__unused NSException *e) { identifier = nil; }
return [identifier isKindOfClass:NSString.class] ? identifier : nil;
}
///
- (void)kb_evaluateCurrentInputModeAndNotifyIfNeeded {
//
if (![self.textField isFirstResponder]) return;
//
if (self.permVC && self.permVC.view.hidden == NO) return;
NSString *currId = [self kb_currentInputModeIdentifier];
if (currId.length == 0) return;
if ([self.kb_lastInputModeIdentifier isEqualToString:currId]) return; //
self.kb_lastInputModeIdentifier = currId;
BOOL isMine = [currId rangeOfString:KB_KEYBOARD_EXTENSION_BUNDLE_ID].location != NSNotFound;
[KBHUD showInfo:(isMine ? @"是自己的键盘" : @"❎不是自己的键盘")];
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

View File

@@ -163,8 +163,8 @@
#pragma mark - Actions
- (void)onTapBuyAction {
// if (self.onTapBuy) { self.onTapBuy(); }
// KBJfPay *vc = [[KBJfPay alloc] init];
// [KB_CURRENT_NAV pushViewController:vc animated:true];
KBJfPay *vc = [[KBJfPay alloc] init];
[KB_CURRENT_NAV pushViewController:vc animated:true];
}
#pragma mark - Lazy

View File

@@ -4,7 +4,7 @@
#import "KBJfPay.h"
#import "KBJfPayCell.h"
#import "FGIAPProductsFilter.h"
static NSString * const kKBJfPayCellId = @"kKBJfPayCellId";
@interface KBJfPay () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@@ -34,12 +34,15 @@ static NSString * const kKBJfPayCellId = @"kKBJfPayCellId";
@property (nonatomic, strong) NSArray<NSDictionary *> *data; // @{coins, price}
@property (nonatomic, assign) NSInteger selectedIndex; //
@property (nonatomic, strong) FGIAPProductsFilter *filter;
@end
@implementation KBJfPay
- (void)viewDidLoad {
[super viewDidLoad];
self.filter = [[FGIAPProductsFilter alloc] init];
self.bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_bg_icon"]];
self.bgImageView.contentMode = UIViewContentModeScaleAspectFill;
self.kb_navView.backgroundColor = [UIColor clearColor];
@@ -51,12 +54,12 @@ static NSString * const kKBJfPayCellId = @"kKBJfPayCellId";
//
self.data = @[
@{ @"coins": @690, @"price": @"$6.90" },
@{ @"coins": @1280, @"price": @"$12.90" },
@{ @"coins": @3290, @"price": @"$32.90" },
@{ @"coins": @4990, @"price": @"$49.90" },
@{ @"coins": @9990, @"price": @"$99.90" },
@{ @"coins": @19990,@"price": @"$199.90" },
@{ @"coins": @690, @"price": @"$6.90",@"product_id" : @"100_coin" },
@{ @"coins": @1280, @"price": @"$12.90" ,@"product_id" : @"vip_a_week" },
@{ @"coins": @3290, @"price": @"$32.90" ,@"product_id" : @"100_coin" },
@{ @"coins": @4990, @"price": @"$49.90" ,@"product_id" : @"100_coin" },
@{ @"coins": @9990, @"price": @"$99.90" ,@"product_id" : @"100_coin" },
@{ @"coins": @19990,@"price": @"$199.90" ,@"product_id" : @"100_coin" },
];
self.selectedIndex = 1; //
@@ -234,11 +237,20 @@ static NSString * const kKBJfPayCellId = @"kKBJfPayCellId";
#pragma mark - Actions
- (void)onTapPayButton {
// UI
if (self.selectedIndex >= 0 && self.selectedIndex < self.data.count) {
NSDictionary *item = self.data[self.selectedIndex];
NSString *msg = [NSString stringWithFormat:@"购买:%@ Coins %@", item[@"coins"], item[@"price"]];
[KBHUD showInfo:msg];
}
// if (self.selectedIndex >= 0 && self.selectedIndex < self.data.count) {
// NSDictionary *item = self.data[self.selectedIndex];
// NSString *msg = [NSString stringWithFormat:@"购买:%@ Coins %@", item[@"coins"], item[@"price"]];
// [KBHUD showInfo:msg];
// }
NSString *productId = @"com.yolo.vip.1month";
/// 2.
[self.filter requestProductsWith:[NSSet setWithObject:productId] completion:^(NSArray<SKProduct *> * _Nonnull products) {
NSLog(@"=====");
/// 3.
// [[FGIAPManager shared].iap buyProduct:products.firstObject onCompletion:^(NSString * _Nonnull message, FGIAPManagerPurchaseRusult result) {
// [self.view makeToast:message];
// }];
}];
}
- (void)agreementButtonAction{

View File

@@ -12,65 +12,78 @@
@property (nonatomic, strong) UILabel *tipsLabel; //
@property (nonatomic, strong) UIView *cardView; //
@property (nonatomic, strong) UIButton *openButton; //
@property (nonatomic, strong) UIButton *closeButton; //
@property (nonatomic, strong) UILabel *helpLabel; //
@property (nonatomic, strong) UIImageView *redImageView;
@property (nonatomic, strong) UIImageView *bgImageView;
@end
@implementation KBPermissionViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
[self.view addSubview:self.bgImageView];
[self.view addSubview:self.redImageView];
// +
[self.view addSubview:self.backButton];
[self.view addSubview:self.closeButton];
[self.view addSubview:self.titleLabel];
[self.view addSubview:self.tipsLabel];
[self.view addSubview:self.cardView];
[self.view addSubview:self.openButton];
[self.view addSubview:self.helpLabel];
// Masonry
[self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(8);
make.width.mas_equalTo(60);
make.height.mas_equalTo(32);
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
// Masonry
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(20);
make.width.mas_equalTo(26);
make.height.mas_equalTo(26);
}];
[self.redImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.closeButton.mas_centerY);
make.right.equalTo(self.view).offset(20);
make.width.mas_equalTo(143);
make.height.mas_equalTo(132);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(48);
make.left.equalTo(self.view).offset(24);
make.right.equalTo(self.view).offset(-24);
make.height.mas_equalTo(28);
make.top.equalTo(self.closeButton.mas_bottom).offset(13);
make.left.equalTo(self.view).offset(22);
make.height.mas_equalTo(24);
}];
[self.tipsLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).offset(8);
make.left.equalTo(self.view).offset(24);
make.right.equalTo(self.view).offset(-24);
make.left.equalTo(self.titleLabel);
}];
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.tipsLabel.mas_bottom).offset(28);
make.left.equalTo(self.view).offset(32);
make.right.equalTo(self.view).offset(-32);
make.height.mas_equalTo(260);
make.bottom.equalTo(self.openButton.mas_top).offset(-36);
make.left.right.equalTo(self.view).inset(56);
make.top.equalTo(self.tipsLabel.mas_bottom).offset(36);
}];
[self.openButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.cardView.mas_bottom).offset(32);
make.left.equalTo(self.view).offset(32);
make.right.equalTo(self.view).offset(-32);
make.height.mas_equalTo(48);
make.bottom.equalTo(self.view).offset(-KB_SAFE_BOTTOM-20);
make.left.equalTo(self.view).offset(47);
make.right.equalTo(self.view).offset(-47);
make.height.mas_equalTo(60);
}];
[self.helpLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.openButton.mas_bottom).offset(12);
make.left.equalTo(self.view).offset(24);
make.right.equalTo(self.view).offset(-24);
}];
// [self.helpLabel mas_makeConstraints:^(MASConstraintMaker *make) {
// make.top.equalTo(self.openButton.mas_bottom).offset(12);
// make.left.equalTo(self.view).offset(24);
// make.right.equalTo(self.view).offset(-24);
// }];
}
#pragma mark - Actions
@@ -112,6 +125,10 @@
}
}
- (void)closeButtonAction{
[self.navigationController popViewControllerAnimated:true];
}
#pragma mark - Lazy Subviews
- (UIButton *)backButton {
@@ -128,8 +145,8 @@
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.text = KBLocalized(@"perm_title_enable");
_titleLabel.font = [UIFont systemFontOfSize:22 weight:UIFontWeightSemibold];
_titleLabel.text = (@"key of love keyboard");
_titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold];
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
@@ -139,7 +156,7 @@
- (UILabel *)tipsLabel {
if (!_tipsLabel) {
_tipsLabel = [UILabel new];
_tipsLabel.text = KBLocalized(@"perm_steps");
_tipsLabel.text = (@"One-click to find a partner");
_tipsLabel.font = [UIFont systemFontOfSize:14];
_tipsLabel.textColor = [UIColor darkGrayColor];
_tipsLabel.textAlignment = NSTextAlignmentCenter;
@@ -161,17 +178,26 @@
- (UIButton *)openButton {
if (!_openButton) {
_openButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_openButton setTitle:KBLocalized(@"perm_open_settings") forState:UIControlStateNormal];
_openButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold];
_openButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_openButton setTitle:@"Turn on the keyboard" forState:UIControlStateNormal];
_openButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
[_openButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_openButton.backgroundColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0];
_openButton.layer.cornerRadius = 8;
_openButton.backgroundColor = [UIColor colorWithHex:KBBlackValue];
_openButton.layer.cornerRadius = 30;
[_openButton addTarget:self action:@selector(openSettings) forControlEvents:UIControlEventTouchUpInside];
}
return _openButton;
}
- (UIButton *)closeButton {
if (!_closeButton) {
_closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeButton setImage:[UIImage imageNamed:@"close_icon"] forState:UIControlStateNormal];
[_closeButton addTarget:self action:@selector(closeButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _closeButton;
}
- (UILabel *)helpLabel {
if (!_helpLabel) {
_helpLabel = [UILabel new];
@@ -184,4 +210,20 @@
return _helpLabel;
}
- (UIImageView *)bgImageView{
if (!_bgImageView) {
_bgImageView = [[UIImageView alloc] init];
_bgImageView.image = [UIImage imageNamed:@"qx_bg_icon"];
}
return _bgImageView;
}
- (UIImageView *)redImageView{
if (!_redImageView) {
_redImageView = [[UIImageView alloc] init];
_redImageView.image = [UIImage imageNamed:@"qx_ax_icon"];
}
return _redImageView;
}
@end