From 50163d02a7a646f2068e78e3c3adab626b8d22af Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Thu, 13 Nov 2025 19:07:59 +0800 Subject: [PATCH] 3 --- Shared/KBAPI.h | 6 +- keyBoard.xcodeproj/project.pbxproj | 22 ++++- keyBoard/AppDelegate.m | 54 ++--------- keyBoard/Class/Login/M/KBUser.h | 34 +++++++ keyBoard/Class/Login/M/KBUser.m | 97 ++++++++++++++++++++ keyBoard/Class/Login/VM/KBLoginVM.h | 32 +++++++ keyBoard/Class/Login/VM/KBLoginVM.m | 99 ++++++++++++++++++++ keyBoard/Class/Shop/V/KBShopHeadView.m | 121 +++++++++++++++++-------- keyBoard/KeyBoardPrefixHeader.pch | 1 + 9 files changed, 378 insertions(+), 88 deletions(-) create mode 100644 keyBoard/Class/Login/M/KBUser.h create mode 100644 keyBoard/Class/Login/M/KBUser.m create mode 100644 keyBoard/Class/Login/VM/KBLoginVM.h create mode 100644 keyBoard/Class/Login/VM/KBLoginVM.m diff --git a/Shared/KBAPI.h b/Shared/KBAPI.h index cb2f5cd..65a3463 100644 --- a/Shared/KBAPI.h +++ b/Shared/KBAPI.h @@ -7,10 +7,9 @@ #ifndef KBAPI_h #define KBAPI_h -// 用户与认证 -#ifndef KB_API_USER_APPLE_LOGIN + +// 兼容旧命名(如有使用 API_APPLE_LOGIN 的位置,映射到统一命名) #define API_APPLE_LOGIN @"/user/appleLogin" // Apple 登录 -#endif // 应用配置 #ifndef KB_API_APP_CONFIG @@ -18,4 +17,3 @@ #endif #endif /* KBAPI_h */ - diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 28c8983..21058f7 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 04122F5D2EC5E5A900EF7AB3 /* KBLoginVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122F5B2EC5E5A900EF7AB3 /* KBLoginVM.m */; }; + 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122F612EC5F41D00EF7AB3 /* KBUser.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 */; }; @@ -177,6 +179,10 @@ /* Begin PBXFileReference section */ 04122F592EC5D40000EF7AB3 /* KBAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAPI.h; sourceTree = ""; }; + 04122F5A2EC5E5A900EF7AB3 /* KBLoginVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBLoginVM.h; sourceTree = ""; }; + 04122F5B2EC5E5A900EF7AB3 /* KBLoginVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBLoginVM.m; sourceTree = ""; }; + 04122F602EC5F41D00EF7AB3 /* KBUser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBUser.h; sourceTree = ""; }; + 04122F612EC5F41D00EF7AB3 /* KBUser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBUser.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 = ""; }; @@ -453,6 +459,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 04122F5C2EC5E5A900EF7AB3 /* VM */ = { + isa = PBXGroup; + children = ( + 04122F5A2EC5E5A900EF7AB3 /* KBLoginVM.h */, + 04122F5B2EC5E5A900EF7AB3 /* KBLoginVM.m */, + ); + path = VM; + sourceTree = ""; + }; 0477BD942EBAFF4E0055D639 /* Utils */ = { isa = PBXGroup; children = ( @@ -1099,7 +1114,9 @@ 04FC95EA2EB33611007BD342 /* M */ = { isa = PBXGroup; children = ( - ); + 04122F602EC5F41D00EF7AB3 /* KBUser.h */, + 04122F612EC5F41D00EF7AB3 /* KBUser.m */, + ); path = M; sourceTree = ""; }; @@ -1126,6 +1143,7 @@ 04FC95ED2EB33611007BD342 /* Login */ = { isa = PBXGroup; children = ( + 04122F5C2EC5E5A900EF7AB3 /* VM */, 04FC95EA2EB33611007BD342 /* M */, 04FC95EB2EB33611007BD342 /* V */, 04FC95EC2EB33611007BD342 /* VC */, @@ -1459,6 +1477,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */, 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */, 048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */, 048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */, @@ -1539,6 +1558,7 @@ A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */, 047C65532EBCBAC60035E841 /* KBCommunityVC.m in Sources */, A1B2C4212EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */, + 04122F5D2EC5E5A900EF7AB3 /* KBLoginVM.m in Sources */, 0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */, 048908E32EBF760000FABA60 /* MySkinCell.m in Sources */, 04890B122EC2F00000FABA60 /* KBMyHeaderView.m in Sources */, diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index ec70203..0449ddd 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -135,52 +135,14 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomK __weak typeof(pop) weakPop = pop; view.appleLoginHandler = ^{ [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:API_APPLE_LOGIN jsonBody:params headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { - NSLog(@"====="); - }]; - - NSLog(@"===="); + // 交给 VM 统一处理 Apple 登录 + 服务端登录 + [[KBLoginVM shared] signInWithAppleFromViewController:KB_CURRENT_NAV completion:^(BOOL success, NSError * _Nullable error) { + if (success) { + [KBHUD showInfo:@"登录成功"]; + } else { + NSString *msg = error.localizedDescription ?: @"登录失败"; + [KBHUD showInfo:msg]; + } }]; }; view.closeHandler = ^{ [weakPop dismiss]; }; diff --git a/keyBoard/Class/Login/M/KBUser.h b/keyBoard/Class/Login/M/KBUser.h new file mode 100644 index 0000000..d78d3c9 --- /dev/null +++ b/keyBoard/Class/Login/M/KBUser.h @@ -0,0 +1,34 @@ +// +// KBUser.h +// 登录模块-用户模型(MJExtension 解析) +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBUser : NSObject + +// 标识 +@property (nonatomic, copy, nullable) NSString *userId; // id/user_id/uid +@property (nonatomic, copy, nullable) NSString *appleUserId; // 用 Apple 登录返回的 userID(可选) + +// 基本信息 +@property (nonatomic, copy, nullable) NSString *nickname; +@property (nonatomic, copy, nullable) NSString *avatar; // 头像 URL +@property (nonatomic, copy, nullable) NSString *gender; // 性别(后端可能返回 string/int,统一转字符串存) +@property (nonatomic, copy, nullable) NSString *mobile; +@property (nonatomic, copy, nullable) NSString *email; + +// 会话信息 +@property (nonatomic, copy, nullable) NSString *token; // token/access_token/accessToken +@property (nonatomic, copy, nullable) NSString *refreshToken; // refresh_token/refreshToken +@property (nonatomic, strong, nullable) NSDate *expiryDate; // 若后端返回过期时间,转为日期 + +/// 从后端返回(可能顶层或 data/user 嵌套)中解析用户模型。内部使用 MJExtension。 ++ (instancetype)userFromResponseObject:(id)jsonObject; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Login/M/KBUser.m b/keyBoard/Class/Login/M/KBUser.m new file mode 100644 index 0000000..5556882 --- /dev/null +++ b/keyBoard/Class/Login/M/KBUser.m @@ -0,0 +1,97 @@ +// +// KBUser.m +// + +#import "KBUser.h" +#import + +@implementation KBUser + ++ (NSDictionary *)mj_replacedKeyFromPropertyName { + return @{ + @"userId": @[@"id", @"user_id", @"uid"], + @"appleUserId": @[@"appleUserId", @"apple_user_id", @"apple_userid", @"appleUserID"], + @"nickname": @[@"nickname", @"nick", @"name"], + @"avatar": @[@"avatar", @"avatar_url", @"head", @"headimg"], + @"gender": @[@"gender", @"sex"], + @"mobile": @[@"mobile", @"phone"], + @"email": @[@"email"], + @"token": @[@"token", @"access_token", @"accessToken"], + @"refreshToken": @[@"refresh_token", @"refreshToken"], + @"expiryDate": @[@"expire_at", @"expireAt", @"expires_at", @"expiresAt", @"expired_at"], + }; +} + +// 将可能是字符串/时间戳(秒/毫秒)的过期时间,转为 NSDate +- (void)setExpiryDate:(NSDate *)expiryDate { _expiryDate = expiryDate; } + ++ (instancetype)userFromResponseObject:(id)jsonObject { + if (!jsonObject) return nil; + NSDictionary *dict = nil; + if ([jsonObject isKindOfClass:NSDictionary.class]) { + dict = (NSDictionary *)jsonObject; + } else if ([jsonObject isKindOfClass:NSData.class]) { + // JSON data -> dict + id obj = [NSJSONSerialization JSONObjectWithData:(NSData *)jsonObject options:0 error:NULL]; + if ([obj isKindOfClass:NSDictionary.class]) dict = obj; + } + if (!dict) return nil; + + // 兼容多种后端包装:优先 data.user,其次 data,本身就是用户 + NSDictionary *candidate = nil; + id data = dict[@"data"]; if ([data isKindOfClass:NSDictionary.class]) { candidate = data; } + id user = [candidate objectForKey:@"user"]; if (![user isKindOfClass:NSDictionary.class]) { user = dict[@"user"]; } + NSDictionary *userDict = ([user isKindOfClass:NSDictionary.class]) ? (NSDictionary *)user : (candidate ?: dict); + + KBUser *u = [KBUser mj_objectWithKeyValues:userDict]; + + // 额外兼容:若 token 不在用户对象里,尝试从上层提取到模型上 + if (u.token.length == 0) { + NSString *t = [self pickTokenFromDictionary:dict]; + if (t.length) u.token = t; + } + + // 过期时间字段可能是字符串或时间戳,做宽松转换 + id exp = userDict[@"expire_at"] ?: userDict[@"expireAt"] ?: userDict[@"expires_at"] ?: userDict[@"expiresAt"] ?: userDict[@"expired_at"]; + if ([exp isKindOfClass:NSNumber.class]) { + // 兼容秒/毫秒(> 10^11 视为毫秒) + NSTimeInterval ts = [(NSNumber *)exp doubleValue]; + if (ts > 1e11) ts = ts / 1000.0; + u.expiryDate = [NSDate dateWithTimeIntervalSince1970:ts]; + } else if ([exp isKindOfClass:NSString.class]) { + // 尝试 ISO8601 + NSDateFormatter *fmt = [NSDateFormatter new]; + fmt.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + fmt.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; + NSDate *d = [fmt dateFromString:(NSString *)exp]; + if (!d) { + // 尝试纯秒 + NSTimeInterval ts = [(NSString *)exp doubleValue]; + if (ts > 0) d = [NSDate dateWithTimeIntervalSince1970:ts]; + } + if (d) u.expiryDate = d; + } + + return u; +} + ++ (NSString *)pickTokenFromDictionary:(NSDictionary *)dict { + if (![dict isKindOfClass:NSDictionary.class]) return nil; + NSString *(^pick)(NSDictionary *) = ^NSString *(NSDictionary *d) { + NSArray *keys = @[ @"token", @"access_token", @"accessToken" ]; + for (NSString *k in keys) { + id v = d[k]; if ([v isKindOfClass:NSString.class] && ((NSString *)v).length > 0) return v; + } + return nil; + }; + NSString *t = pick(dict); if (t.length) return t; + id data = dict[@"data"]; if ([data isKindOfClass:NSDictionary.class]) { t = pick(data); if (t.length) return t; } + id user = dict[@"user"]; if ([user isKindOfClass:NSDictionary.class]) { t = pick(user); if (t.length) return t; } + NSDictionary *d2 = dict[@"data"]; if ([d2 isKindOfClass:NSDictionary.class]) { + NSDictionary *session = d2[@"session"]; if ([session isKindOfClass:NSDictionary.class]) { t = pick(session); if (t.length) return t; } + } + return nil; +} + +@end + diff --git a/keyBoard/Class/Login/VM/KBLoginVM.h b/keyBoard/Class/Login/VM/KBLoginVM.h new file mode 100644 index 0000000..345234e --- /dev/null +++ b/keyBoard/Class/Login/VM/KBLoginVM.h @@ -0,0 +1,32 @@ +// +// KBLoginVM.h +// 登录相关的 ViewModel:封装 Apple 登录与服务端校验、登录态落盘。 +// + +#import +#import + +@class KBUser; + +NS_ASSUME_NONNULL_BEGIN + +/// 登录完成回调 +typedef void(^KBLoginCompletion)(BOOL success, NSError * _Nullable error); + +@interface KBLoginVM : NSObject + ++ (instancetype)shared; + +/// 最近一次登录/拉取到的用户信息(仅内存缓存),可选 +@property (atomic, strong, readonly, nullable) KBUser *currentUser; // 最近一次解析得到的用户 + +/// 调起 Apple 登录并在服务端完成校验;成功后会将 token 写入共享钥匙串(KBAuthManager),作为“已登录”的判断依据。 +- (void)signInWithAppleFromViewController:(UIViewController *)presenter + completion:(KBLoginCompletion)completion; + +/// 是否已登录:由 KBAuthManager 判断(是否存在有效 token) +- (BOOL)isLoggedIn; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Login/VM/KBLoginVM.m b/keyBoard/Class/Login/VM/KBLoginVM.m new file mode 100644 index 0000000..44ca4fe --- /dev/null +++ b/keyBoard/Class/Login/VM/KBLoginVM.m @@ -0,0 +1,99 @@ +// +// KBLoginVM.m +// + +#import "KBLoginVM.h" +#import "AppleSignInManager.h" +#import "KBNetworkManager.h" +#import "KBAuthManager.h" +#import "KBAPI.h" +#import "KBUser.h" + +@interface KBLoginVM () +@property (atomic, strong, readwrite, nullable) KBUser *currentUser; +@end + +@implementation KBLoginVM + ++ (instancetype)shared { + static KBLoginVM *vm; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ vm = [KBLoginVM new]; }); + return vm; +} + +- (BOOL)isLoggedIn { + return [[KBAuthManager shared] isLoggedIn]; +} + +- (void)signInWithAppleFromViewController:(UIViewController *)presenter + completion:(KBLoginCompletion)completion { + // 调起 Apple 登录 + [[AppleSignInManager shared] signInFromViewController:presenter completion:^(ASAuthorizationAppleIDCredential * _Nullable credential, NSError * _Nullable error) { + if (error) { if (completion) completion(NO, error); return; } + if (![credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { + if (completion) completion(NO, [NSError errorWithDomain:@"KBLogin" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"无效的登录凭证"}]); + return; + } + + ASAuthorizationAppleIDCredential *cred = (ASAuthorizationAppleIDCredential *)credential; + // identityToken/authorizationCode 均可按后端要求传递;本项目后端使用 identityToken 映射为 code + NSString *identityToken = cred.identityToken ? [[NSString alloc] initWithData:cred.identityToken encoding:NSUTF8StringEncoding] : nil; + NSString *authorizationCode = cred.authorizationCode ? [[NSString alloc] initWithData:cred.authorizationCode encoding:NSUTF8StringEncoding] : nil; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (identityToken.length) params[@"code"] = identityToken; + if (authorizationCode.length) params[@"accessCode"] = authorizationCode; // 仅供后端需要时使用 + if (cred.user.length) params[@"userID"] = cred.user; // 可选 + + // 向服务端发起校验 + [[KBNetworkManager shared] POST:API_APPLE_LOGIN jsonBody:params headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + if (error) { if (completion) completion(NO, error); return; } + + // 从返回 JSON 中提取 token(支持常见命名与层级 data/user) + // 先解析用户模型(使用 MJExtension) + KBUser *user = [KBUser userFromResponseObject:jsonOrData]; + self.currentUser = user; + NSString *token = user.token ?: [self.class tokenFromResponseObject:jsonOrData]; + if (token.length == 0) { + if (completion) completion(NO, [NSError errorWithDomain:@"KBLogin" code:-2 userInfo:@{NSLocalizedDescriptionKey: @"未返回 token"}]); + return; + } + + // 保存登录态到共享钥匙串;供 App 与扩展共享 + BOOL ok = [[KBAuthManager shared] saveAccessToken:token + refreshToken:nil + expiryDate:nil + userIdentifier:cred.user]; + if (completion) completion(ok, ok ? nil : [NSError errorWithDomain:@"KBLogin" code:-3 userInfo:@{NSLocalizedDescriptionKey: @"保存登录态失败"}]); + }]; + }]; +} + +#pragma mark - Helpers + +// 宽松解析:token / access_token / accessToken,支持顶层或 data/user 层 ++ (NSString *)tokenFromResponseObject:(id)obj { + if (![obj isKindOfClass:[NSDictionary class]]) return nil; + NSDictionary *dict = (NSDictionary *)obj; + + NSString *(^pick)(NSDictionary *) = ^NSString *(NSDictionary *d) { + NSArray *keys = @[ @"token", @"access_token", @"accessToken" ]; + for (NSString *k in keys) { + id v = d[k]; if ([v isKindOfClass:NSString.class] && ((NSString *)v).length > 0) return v; + } + return nil; + }; + + NSString *t = pick(dict); + if (t.length) return t; + + id data = dict[@"data"]; if ([data isKindOfClass:NSDictionary.class]) { t = pick(data); if (t.length) return t; } + id user = dict[@"user"]; if ([user isKindOfClass:NSDictionary.class]) { t = pick(user); if (t.length) return t; } + + // 扩展:允许后端将 token 放在 data.session.token + NSDictionary *d2 = dict[@"data"]; if ([d2 isKindOfClass:NSDictionary.class]) { + NSDictionary *session = d2[@"session"]; if ([session isKindOfClass:NSDictionary.class]) { t = pick(session); if (t.length) return t; } + } + return nil; +} + +@end diff --git a/keyBoard/Class/Shop/V/KBShopHeadView.m b/keyBoard/Class/Shop/V/KBShopHeadView.m index 85ef0ff..9951067 100644 --- a/keyBoard/Class/Shop/V/KBShopHeadView.m +++ b/keyBoard/Class/Shop/V/KBShopHeadView.m @@ -15,7 +15,12 @@ // 顶部左侧标题与点缀 @property (nonatomic, strong) UILabel *titleLabel; // "Points\nMall" @property (nonatomic, strong) UIImageView *yellowStarView; // 左侧黄色装饰星:shop_yellowxx_icon +@property (nonatomic, strong) UIImageView *yellowStarView2; // 左侧黄色装饰星:shop_yellowxx_icon + @property (nonatomic, strong) UIImageView *greenStarView; // 右上角绿色装饰星:shop_greenxx_icon +@property (nonatomic, strong) UIImageView *greenStarView2; // 右上角绿色装饰星:shop_greenxx_icon +@property (nonatomic, strong) UIImageView *greenStarView3; // 右上角绿色装饰星:shop_greenxx_icon + // 右侧大金币 @property (nonatomic, strong) UIImageView *bigCoinView; // shop_headbigBg_icon @@ -38,9 +43,12 @@ // 组装视图层级 [self addSubview:self.containerView]; [self.containerView addSubview:self.titleLabel]; - [self.containerView addSubview:self.yellowStarView]; - [self.containerView addSubview:self.greenStarView]; [self.containerView addSubview:self.bigCoinView]; + [self.containerView addSubview:self.yellowStarView]; + [self.containerView addSubview:self.yellowStarView2]; + [self.containerView addSubview:self.greenStarView]; + [self.containerView addSubview:self.greenStarView2]; + [self.containerView addSubview:self.greenStarView3]; [self.containerView addSubview:self.infoCard]; [self.infoCard addSubview:self.myPointsLabel]; [self.infoCard addSubview:self.smallCoinView]; @@ -57,21 +65,36 @@ // 左侧标题 [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.right.left.equalTo(self.containerView).offset(0); - make.bottom.equalTo(self.containerView).offset(0); + make.left.equalTo(self.containerView).offset(32); + make.top.equalTo(self.containerView).offset(KB_NAV_TOTAL_HEIGHT + 20); }]; // 左侧黄色装饰星 [self.yellowStarView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(self.titleLabel.mas_centerY).offset(6); make.left.equalTo(self.containerView).offset(6); + make.width.height.mas_equalTo(18); + }]; + [self.yellowStarView2 mas_makeConstraints:^(MASConstraintMaker *make) { + make.bottom.equalTo(self.titleLabel.mas_top).offset(6); + make.left.equalTo(self.titleLabel.mas_right).offset(6); make.width.height.mas_equalTo(16); }]; // 右上角绿色装饰星 [self.greenStarView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.containerView).offset(10); - make.right.equalTo(self.containerView).offset(-6); + make.top.equalTo(self.yellowStarView2).offset(0); + make.left.equalTo(self.yellowStarView2.mas_right).offset(62); + make.width.height.mas_equalTo(20); + }]; + [self.greenStarView2 mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.greenStarView.mas_bottom).offset(10); + make.right.equalTo(self.containerView).offset(-18); + make.width.height.mas_equalTo(14); + }]; + [self.greenStarView3 mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.greenStarView2.mas_bottom).offset(20); + make.right.equalTo(self.containerView).offset(0); make.width.height.mas_equalTo(14); }]; @@ -91,25 +114,25 @@ [self.myPointsLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.infoCard).offset(16); - make.top.equalTo(self.infoCard).offset(16); + make.bottom.equalTo(self.smallCoinView.mas_top).offset(-16); }]; [self.smallCoinView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.infoCard).offset(16); - make.top.equalTo(self.myPointsLabel.mas_bottom).offset(12); - make.width.height.mas_equalTo(36); + make.centerY.equalTo(self.amountLabel); + make.width.height.mas_equalTo(38); }]; [self.amountLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.smallCoinView.mas_right).offset(10); - make.centerY.equalTo(self.smallCoinView); + make.centerY.equalTo(self.rechargeButton); }]; [self.rechargeButton mas_makeConstraints:^(MASConstraintMaker *make) { - make.centerY.equalTo(self.amountLabel); - make.right.equalTo(self.infoCard).offset(-16); - make.width.mas_equalTo(120); - make.height.mas_equalTo(40); + make.bottom.equalTo(self.infoCard).offset(-32); + make.right.equalTo(self.infoCard).offset(-15); + make.width.mas_equalTo(114); + make.height.mas_equalTo(42); }]; } return self; @@ -180,6 +203,13 @@ } return _yellowStarView; } +- (UIImageView *)yellowStarView2 { + if (!_yellowStarView2) { + _yellowStarView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_yellowxx_icon"]]; + _yellowStarView2.contentMode = UIViewContentModeScaleAspectFit; + } + return _yellowStarView2; +} - (UIImageView *)greenStarView { if (!_greenStarView) { @@ -188,6 +218,20 @@ } return _greenStarView; } +- (UIImageView *)greenStarView2 { + if (!_greenStarView2) { + _greenStarView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_greenxx_icon"]]; + _greenStarView2.contentMode = UIViewContentModeScaleAspectFit; + } + return _greenStarView2; +} +- (UIImageView *)greenStarView3 { + if (!_greenStarView3) { + _greenStarView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_greenxx_icon"]]; + _greenStarView3.contentMode = UIViewContentModeScaleAspectFit; + } + return _greenStarView3; +} - (UIImageView *)bigCoinView { if (!_bigCoinView) { @@ -208,8 +252,8 @@ if (!_myPointsLabel) { _myPointsLabel = [UILabel new]; _myPointsLabel.text = @"My Points"; - _myPointsLabel.textColor = [UIColor colorWithHex:0x1B1F1A]; - _myPointsLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold]; + _myPointsLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _myPointsLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; } return _myPointsLabel; } @@ -225,11 +269,11 @@ - (UILabel *)amountLabel { if (!_amountLabel) { _amountLabel = [UILabel new]; - _amountLabel.text = @"88.00"; // 默认文案 + _amountLabel.text = @"88.00"; _amountLabel.textColor = [UIColor colorWithHex:KBColorValue]; - _amountLabel.font = [UIFont systemFontOfSize:38 weight:UIFontWeightBold]; + _amountLabel.font = [UIFont systemFontOfSize:40 weight:UIFontWeightBold]; _amountLabel.adjustsFontSizeToFitWidth = YES; - _amountLabel.minimumScaleFactor = 0.7; +// _amountLabel.minimumScaleFactor = 0.7; } return _amountLabel; } @@ -238,25 +282,28 @@ if (!_rechargeButton) { _rechargeButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_rechargeButton setTitle:@"Recharge" forState:UIControlStateNormal]; - [_rechargeButton setTitleColor:[UIColor colorWithHex:0x1B1F1A] forState:UIControlStateNormal]; - _rechargeButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; - - // 切图背景图(可拉伸) + [_rechargeButton setTitleColor:[UIColor colorWithHex:KBBlackValue] forState:UIControlStateNormal]; + _rechargeButton.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; UIImage *bg = [UIImage imageNamed:@"recharge_btn_bg"]; - if (bg) { - CGFloat cap = 20; // 圆角拉伸区域 - bg = [bg resizableImageWithCapInsets:UIEdgeInsetsMake(cap, cap, cap, cap) resizingMode:UIImageResizingModeStretch]; - [_rechargeButton setBackgroundImage:bg forState:UIControlStateNormal]; - } else { - // 兜底:用绿色渐变生成 - UIImage *fallback = [UIImage kb_gradientImageWithColors:@[[UIColor colorWithHex:0xA6F6E9], [UIColor colorWithHex:0xD6FFF4]] - size:CGSizeMake(120, 40) - direction:KBGradientDirectionLeftToRight]; - [_rechargeButton setBackgroundImage:fallback forState:UIControlStateNormal]; - } - - _rechargeButton.layer.cornerRadius = 20; - _rechargeButton.layer.masksToBounds = YES; + [_rechargeButton setBackgroundImage:bg forState:UIControlStateNormal]; + +// +// // 切图背景图(可拉伸) +// UIImage *bg = [UIImage imageNamed:@"recharge_btn_bg"]; +// if (bg) { +// CGFloat cap = 20; // 圆角拉伸区域 +// bg = [bg resizableImageWithCapInsets:UIEdgeInsetsMake(cap, cap, cap, cap) resizingMode:UIImageResizingModeStretch]; +// [_rechargeButton setBackgroundImage:bg forState:UIControlStateNormal]; +// } else { +// // 兜底:用绿色渐变生成 +// UIImage *fallback = [UIImage kb_gradientImageWithColors:@[[UIColor colorWithHex:0xA6F6E9], [UIColor colorWithHex:0xD6FFF4]] +// size:CGSizeMake(120, 40) +// direction:KBGradientDirectionLeftToRight]; +// [_rechargeButton setBackgroundImage:fallback forState:UIControlStateNormal]; +// } +// +// _rechargeButton.layer.cornerRadius = 20; +// _rechargeButton.layer.masksToBounds = YES; [_rechargeButton addTarget:self action:@selector(onRechargeTappedAction) forControlEvents:UIControlEventTouchUpInside]; } return _rechargeButton; diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index ff659bc..32cd06e 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -18,6 +18,7 @@ // 公共配置 #import "KBConfig.h" #import "KBAPI.h" // 接口路径宏(统一管理) +#import "KBLoginVM.h" // 登录 VM(Apple 登录复用) /// 系统 #import