1
@@ -221,25 +221,25 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20;
|
||||
// 当键盘第一次显示时,尝试唤起主 App 以提示登录(由主 App 决定是否真的弹登录)。
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
if (!_kb_didTriggerLoginDeepLinkOnce) {
|
||||
_kb_didTriggerLoginDeepLinkOnce = YES;
|
||||
// 仅在未登录时尝试拉起主App登录
|
||||
if (!KBAuthManager.shared.isLoggedIn) {
|
||||
[self kb_tryOpenContainerForLoginIfNeeded];
|
||||
}
|
||||
}
|
||||
// if (!_kb_didTriggerLoginDeepLinkOnce) {
|
||||
// _kb_didTriggerLoginDeepLinkOnce = YES;
|
||||
// // 仅在未登录时尝试拉起主App登录
|
||||
// if (!KBAuthManager.shared.isLoggedIn) {
|
||||
// [self kb_tryOpenContainerForLoginIfNeeded];
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
- (void)kb_tryOpenContainerForLoginIfNeeded {
|
||||
// 使用与主 App 一致的自定义 Scheme
|
||||
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=keyboard", KB_APP_SCHEME]];
|
||||
if (!url) return;
|
||||
KBWeakSelf
|
||||
[self.extensionContext openURL:url completionHandler:^(__unused BOOL success) {
|
||||
// 即使失败也不重复尝试;避免打扰。
|
||||
__unused typeof(weakSelf) selfStrong = weakSelf;
|
||||
}];
|
||||
}
|
||||
//- (void)kb_tryOpenContainerForLoginIfNeeded {
|
||||
// // 使用与主 App 一致的自定义 Scheme
|
||||
// NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=keyboard", KB_APP_SCHEME]];
|
||||
// if (!url) return;
|
||||
// KBWeakSelf
|
||||
// [self.extensionContext openURL:url completionHandler:^(__unused BOOL success) {
|
||||
// // 即使失败也不重复尝试;避免打扰。
|
||||
// __unused typeof(weakSelf) selfStrong = weakSelf;
|
||||
// }];
|
||||
//}
|
||||
|
||||
#pragma mark - Theme
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
// 基础baseUrl
|
||||
#ifndef KB_BASE_URL
|
||||
#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/"
|
||||
//#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/"
|
||||
#define KB_BASE_URL @"http://192.168.1.144:7529/api"
|
||||
#endif
|
||||
|
||||
// Universal Links 通用链接
|
||||
|
||||
@@ -110,10 +110,10 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomK
|
||||
|
||||
- (void)kb_presentLoginSheetIfNeeded {
|
||||
// 已登录则不提示
|
||||
BOOL loggedIn = ([AppleSignInManager shared].storedUserIdentifier.length > 0);
|
||||
if (loggedIn) return;
|
||||
UIViewController *top = [UIViewController kb_topMostViewController];
|
||||
if (!top) return;
|
||||
// BOOL loggedIn = ([AppleSignInManager shared].storedUserIdentifier.length > 0);
|
||||
// if (loggedIn) return;
|
||||
// UIViewController *top = [UIViewController kb_topMostViewController];
|
||||
// if (!top) return;
|
||||
// [KBLoginSheetViewController presentIfNeededFrom:top];
|
||||
[self goLogin];
|
||||
|
||||
@@ -137,6 +137,57 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomK
|
||||
[weakPop dismiss];
|
||||
[[AppleSignInManager shared] signInFromViewController:KB_CURRENT_NAV completion:^(ASAuthorizationAppleIDCredential * _Nullable credential, NSError * _Nullable error) {
|
||||
NSLog(@"=====");
|
||||
// 成功回调:仅处理 AppleID 凭证
|
||||
if (![credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
|
||||
return;
|
||||
}
|
||||
ASAuthorizationAppleIDCredential *cred = (ASAuthorizationAppleIDCredential *)credential;
|
||||
|
||||
// identityToken 与 authorizationCode 需要发给后端校验
|
||||
NSString *tokenString = nil;
|
||||
if (cred.identityToken) {
|
||||
tokenString = [[NSString alloc] initWithData:cred.identityToken encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
NSString *authorizationCodeString = nil;
|
||||
if (cred.authorizationCode) {
|
||||
authorizationCodeString = [[NSString alloc] initWithData:cred.authorizationCode encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
// fullName 仅在首次授权时可能返回
|
||||
NSString *fullName = nil;
|
||||
if (cred.fullName.givenName.length || cred.fullName.familyName.length) {
|
||||
NSString *given = cred.fullName.givenName ?: @"";
|
||||
NSString *family = cred.fullName.familyName ?: @"";
|
||||
fullName = [[NSString stringWithFormat:@"%@ %@", given, family] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
}
|
||||
|
||||
// 保存 user 标识,后续可以查询撤销状态或和账号体系绑定(生产建议存钥匙串)
|
||||
NSString *userID = cred.user;
|
||||
// [self _persistAppleUserIdentifier:userID];
|
||||
|
||||
// NSDictionary *params = @{
|
||||
// @"userID": userID ?: @"",
|
||||
// @"accessCode": authorizationCodeString ?: @"",
|
||||
// // 可选字段:若后端不接受,请删除下列键
|
||||
// @"identityToken": tokenString ?: @"",
|
||||
// @"fullName": fullName ?: @"",
|
||||
// @"state": cred.state ?: @""
|
||||
// };
|
||||
NSDictionary *params = @{
|
||||
@"code": tokenString ?: @"",
|
||||
};
|
||||
// [[KBNetworkManager shared] POST:@"/user/appleLogin" parameters:params headers:nil progress:^(NSProgress * _Nonnull uploadProgress) {
|
||||
//
|
||||
// } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
||||
//
|
||||
// } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
//
|
||||
// }];
|
||||
[[KBNetworkManager shared] POST:@"/user/appleLogin" jsonBody:params headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
NSLog(@"=====");
|
||||
}];
|
||||
|
||||
NSLog(@"====");
|
||||
}];
|
||||
};
|
||||
view.closeHandler = ^{ [weakPop dismiss]; };
|
||||
|
||||
6
keyBoard/Assets.xcassets/Shop/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
22
keyBoard/Assets.xcassets/Shop/shop_greenxx_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_greenxx_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_greenxx_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_greenxx_icon.imageset/shop_greenxx_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
keyBoard/Assets.xcassets/Shop/shop_greenxx_icon.imageset/shop_greenxx_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
22
keyBoard/Assets.xcassets/Shop/shop_headbigBg_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_headbigBg_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_headbigBg_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_headbigBg_icon.imageset/shop_headbigBg_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
keyBoard/Assets.xcassets/Shop/shop_headbigBg_icon.imageset/shop_headbigBg_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 272 KiB |
22
keyBoard/Assets.xcassets/Shop/shop_jb_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_jb_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_jb_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_jb_icon.imageset/shop_jb_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
keyBoard/Assets.xcassets/Shop/shop_jb_icon.imageset/shop_jb_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
22
keyBoard/Assets.xcassets/Shop/shop_search_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_search_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_search_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_search_icon.imageset/shop_search_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
keyBoard/Assets.xcassets/Shop/shop_search_icon.imageset/shop_search_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
22
keyBoard/Assets.xcassets/Shop/shop_skin_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_skin_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_skin_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_skin_icon.imageset/shop_skin_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
keyBoard/Assets.xcassets/Shop/shop_skin_icon.imageset/shop_skin_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
22
keyBoard/Assets.xcassets/Shop/shop_yellowxx_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_yellowxx_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "shop_yellowxx_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/shop_yellowxx_icon.imageset/shop_yellowxx_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 818 B |
BIN
keyBoard/Assets.xcassets/Shop/shop_yellowxx_icon.imageset/shop_yellowxx_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
@@ -37,7 +37,6 @@
|
||||
|
||||
// 商城
|
||||
KBShopVC *shop = [[KBShopVC alloc] init];
|
||||
shop.title = @"商城";
|
||||
BaseNavigationController *navShop = [[BaseNavigationController alloc] initWithRootViewController:shop];
|
||||
navShop.tabBarItem = [self tabItemWithTitle:@"商城"
|
||||
image:@"tab_shop"
|
||||
|
||||
@@ -26,6 +26,11 @@ static NSString * const kKBAppleUserIdentifierKey = @"com.company.keyboard.apple
|
||||
}
|
||||
|
||||
- (void)signInFromViewController:(UIViewController *)presenting completion:(KBAppleSignInCompletion)completion {
|
||||
if (!NSThread.isMainThread) {
|
||||
// 确保在主线程发起,否则可能得到 Unknown(1000)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{ [self signInFromViewController:presenting completion:completion]; });
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 13.0, *)) {
|
||||
self.presentingVC = presenting;
|
||||
self.completion = completion;
|
||||
@@ -110,7 +115,21 @@ static NSString * const kKBAppleUserIdentifierKey = @"com.company.keyboard.apple
|
||||
#pragma mark - 授权界面展示锚点 (ASAuthorizationControllerPresentationContextProviding)
|
||||
|
||||
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)) {
|
||||
return self.presentingVC.view.window ?: UIApplication.sharedApplication.keyWindow;
|
||||
// 优先用传入 VC 的 window
|
||||
UIWindow *win = self.presentingVC.view.window;
|
||||
if (win) return win;
|
||||
// iOS13+ 从前台激活的 scene 中取 keyWindow
|
||||
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
|
||||
UIWindowScene *ws = (UIWindowScene *)scene;
|
||||
for (UIWindow *w in ws.windows) { if (w.isKeyWindow) return w; }
|
||||
if (ws.windows.firstObject) return ws.windows.firstObject;
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return UIApplication.sharedApplication.keyWindow ?: UIApplication.sharedApplication.windows.firstObject;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
#pragma mark - Keychain 工具
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
|
||||
@implementation KBShopHeadView
|
||||
|
||||
/*
|
||||
// Only override drawRect: if you perform custom drawing.
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
// Drawing code
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBShopVC : UIViewController
|
||||
@interface KBShopVC : BaseViewController
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -30,13 +30,21 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
@property (nonatomic, strong) JXCategoryTitleView *categoryView;
|
||||
@property (nonatomic, strong) NSArray <NSString *> *titles;
|
||||
@property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图
|
||||
|
||||
@end
|
||||
|
||||
@implementation KBShopVC
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// [self setupUI];
|
||||
self.bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_bg_icon"]];
|
||||
self.bgImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[self.view addSubview:self.bgImageView];
|
||||
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
[self setupUI];
|
||||
|
||||
}
|
||||
|
||||
@@ -90,6 +98,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
_pagerView = [self preferredPagingView];
|
||||
self.pagerView.mainTableView.gestureDelegate = self;
|
||||
self.pagerView.mainTableView.backgroundColor = [UIColor clearColor];
|
||||
[self.view addSubview:self.pagerView];
|
||||
// self.pagerView.listContainerView.scrollView.scrollEnabled = false;
|
||||
|
||||
@@ -119,7 +128,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
self.naviBGView.frame = CGRectMake(0, 0, KB_SCREEN_WIDTH, KB_NAV_TOTAL_HEIGHT);
|
||||
[self.view addSubview:self.naviBGView];
|
||||
UILabel *naviTitleLabel = [[UILabel alloc] init];
|
||||
naviTitleLabel.text = @"导航栏隐藏";
|
||||
naviTitleLabel.text = @"商城";
|
||||
naviTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
naviTitleLabel.frame = CGRectMake(0, KB_STATUSBAR_HEIGHT, self.view.bounds.size.width, 44);
|
||||
[self.naviBGView addSubview:naviTitleLabel];
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
|
||||
/// 项目
|
||||
#import "KBNetworkManager.h"
|
||||
#import "UIView+KBShadow.h"
|
||||
#import "UIImage+KBColor.h"
|
||||
#import "UIColor+Extension.h"
|
||||
|
||||