diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 3cefb31..db58e60 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -58,6 +58,15 @@ 047920072ED86ABC004E8522 /* kb_guide_keyboard.gif in Resources */ = {isa = PBXBuildFile; fileRef = 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */; }; 0479200B2ED87CEE004E8522 /* permiss_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */; }; 047920112ED98E7D004E8522 /* permiss_video_2.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 047920102ED98E7D004E8522 /* permiss_video_2.mp4 */; }; + 0479204A2EDDCE25004E8522 /* KBUserSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920492EDDCE25004E8522 /* KBUserSessionManager.m */; }; + 0479205A2EDEE1FC004E8522 /* CRBoxTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920542EDEE1FC004E8522 /* CRBoxTextView.m */; }; + 0479205B2EDEE1FC004E8522 /* CRSecrectImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920582EDEE1FC004E8522 /* CRSecrectImageView.m */; }; + 0479205C2EDEE1FC004E8522 /* CRBoxFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 0479204C2EDEE1FC004E8522 /* CRBoxFlowLayout.m */; }; + 0479205D2EDEE1FC004E8522 /* CRBoxInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920522EDEE1FC004E8522 /* CRBoxInputView.m */; }; + 0479205E2EDEE1FC004E8522 /* CRBoxInputCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0479204E2EDEE1FC004E8522 /* CRBoxInputCell.m */; }; + 0479205F2EDEE1FC004E8522 /* CRBoxInputCellProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920502EDEE1FC004E8522 /* CRBoxInputCellProperty.m */; }; + 047920602EDEE1FC004E8522 /* CRLineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920562EDEE1FC004E8522 /* CRLineView.m */; }; + 047920632EDEEFC7004E8522 /* KBLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047920622EDEEFC7004E8522 /* KBLoginVC.m */; }; 047C650D2EBC8A840035E841 /* KBPanModalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650C2EBC8A840035E841 /* KBPanModalView.m */; }; 047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650F2EBCA8DD0035E841 /* HomeRankContentVC.m */; }; 047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C654F2EBCBA9E0035E841 /* KBShopVC.m */; }; @@ -274,6 +283,24 @@ 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = kb_guide_keyboard.gif; sourceTree = ""; }; 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = permiss_video.mp4; sourceTree = ""; }; 047920102ED98E7D004E8522 /* permiss_video_2.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = permiss_video_2.mp4; sourceTree = ""; }; + 047920482EDDCE25004E8522 /* KBUserSessionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBUserSessionManager.h; sourceTree = ""; }; + 047920492EDDCE25004E8522 /* KBUserSessionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBUserSessionManager.m; sourceTree = ""; }; + 0479204B2EDEE1FC004E8522 /* CRBoxFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRBoxFlowLayout.h; sourceTree = ""; }; + 0479204C2EDEE1FC004E8522 /* CRBoxFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRBoxFlowLayout.m; sourceTree = ""; }; + 0479204D2EDEE1FC004E8522 /* CRBoxInputCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRBoxInputCell.h; sourceTree = ""; }; + 0479204E2EDEE1FC004E8522 /* CRBoxInputCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRBoxInputCell.m; sourceTree = ""; }; + 0479204F2EDEE1FC004E8522 /* CRBoxInputCellProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRBoxInputCellProperty.h; sourceTree = ""; }; + 047920502EDEE1FC004E8522 /* CRBoxInputCellProperty.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRBoxInputCellProperty.m; sourceTree = ""; }; + 047920512EDEE1FC004E8522 /* CRBoxInputView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRBoxInputView.h; sourceTree = ""; }; + 047920522EDEE1FC004E8522 /* CRBoxInputView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRBoxInputView.m; sourceTree = ""; }; + 047920532EDEE1FC004E8522 /* CRBoxTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRBoxTextView.h; sourceTree = ""; }; + 047920542EDEE1FC004E8522 /* CRBoxTextView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRBoxTextView.m; sourceTree = ""; }; + 047920552EDEE1FC004E8522 /* CRLineView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRLineView.h; sourceTree = ""; }; + 047920562EDEE1FC004E8522 /* CRLineView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRLineView.m; sourceTree = ""; }; + 047920572EDEE1FC004E8522 /* CRSecrectImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRSecrectImageView.h; sourceTree = ""; }; + 047920582EDEE1FC004E8522 /* CRSecrectImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRSecrectImageView.m; sourceTree = ""; }; + 047920612EDEEFC7004E8522 /* KBLoginVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBLoginVC.h; sourceTree = ""; }; + 047920622EDEEFC7004E8522 /* KBLoginVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBLoginVC.m; sourceTree = ""; }; 047C650B2EBC8A840035E841 /* KBPanModalView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPanModalView.h; sourceTree = ""; }; 047C650C2EBC8A840035E841 /* KBPanModalView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPanModalView.m; sourceTree = ""; }; 047C650E2EBCA8DD0035E841 /* HomeRankContentVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeRankContentVC.h; sourceTree = ""; }; @@ -628,6 +655,27 @@ path = FunctionTest; sourceTree = ""; }; + 047920592EDEE1FC004E8522 /* CRBoxInputView */ = { + isa = PBXGroup; + children = ( + 0479204B2EDEE1FC004E8522 /* CRBoxFlowLayout.h */, + 0479204C2EDEE1FC004E8522 /* CRBoxFlowLayout.m */, + 0479204D2EDEE1FC004E8522 /* CRBoxInputCell.h */, + 0479204E2EDEE1FC004E8522 /* CRBoxInputCell.m */, + 0479204F2EDEE1FC004E8522 /* CRBoxInputCellProperty.h */, + 047920502EDEE1FC004E8522 /* CRBoxInputCellProperty.m */, + 047920512EDEE1FC004E8522 /* CRBoxInputView.h */, + 047920522EDEE1FC004E8522 /* CRBoxInputView.m */, + 047920532EDEE1FC004E8522 /* CRBoxTextView.h */, + 047920542EDEE1FC004E8522 /* CRBoxTextView.m */, + 047920552EDEE1FC004E8522 /* CRLineView.h */, + 047920562EDEE1FC004E8522 /* CRLineView.m */, + 047920572EDEE1FC004E8522 /* CRSecrectImageView.h */, + 047920582EDEE1FC004E8522 /* CRSecrectImageView.m */, + ); + path = CRBoxInputView; + sourceTree = ""; + }; 047C652C2EBCAAAC0035E841 /* Resource */ = { isa = PBXGroup; children = ( @@ -791,6 +839,7 @@ 04122F6C2EC5F40800EF7AB3 /* FGIAPService */, 049FB2162EC20A6600FAB05D /* BMLongPressDragCellCollectionView */, 048908D92EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout */, + 047920592EDEE1FC004E8522 /* CRBoxInputView */, ); path = Vender; sourceTree = ""; @@ -1255,6 +1304,8 @@ 04FC95F02EB339A7007BD342 /* LoginViewController.m */, 04FC96112EB34E00007BD342 /* KBLoginSheetViewController.h */, 04FC96122EB34E00007BD342 /* KBLoginSheetViewController.m */, + 047920612EDEEFC7004E8522 /* KBLoginVC.h */, + 047920622EDEEFC7004E8522 /* KBLoginVC.m */, ); path = VC; sourceTree = ""; @@ -1277,6 +1328,8 @@ 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */, 04286A042ECC81B200CE730C /* KBSkinService.h */, 04286A052ECC81B200CE730C /* KBSkinService.m */, + 047920482EDDCE25004E8522 /* KBUserSessionManager.h */, + 047920492EDDCE25004E8522 /* KBUserSessionManager.m */, ); path = Manager; sourceTree = ""; @@ -1597,6 +1650,7 @@ 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */, 04122F8B2EC6F7C800EF7AB3 /* IAPVerifyTransactionObj.m in Sources */, 04286A062ECC81B200CE730C /* KBSkinService.m in Sources */, + 0479204A2EDDCE25004E8522 /* KBUserSessionManager.m in Sources */, 04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */, 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */, 048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */, @@ -1657,6 +1711,7 @@ 0477BDFA2EBC66340055D639 /* HomeHeadView.m in Sources */, 04FC97032EB30A00007BD342 /* KBGuideKFCell.m in Sources */, 04FC97062EB30A00007BD342 /* KBGuideUserCell.m in Sources */, + 047920632EDEEFC7004E8522 /* KBLoginVC.m in Sources */, 04FC97092EB31B14007BD342 /* KBHUD.m in Sources */, 04FC970E2EB334F8007BD342 /* UIImageView+KBWebImage.m in Sources */, 049FB2232EC311F900FAB05D /* KBPersonInfoVC.m in Sources */, @@ -1674,6 +1729,13 @@ 048908DA2EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m in Sources */, 04C6EABF2EAF86530089C901 /* main.m in Sources */, 04FC95CC2EB1E780007BD342 /* BaseTabBarController.m in Sources */, + 0479205A2EDEE1FC004E8522 /* CRBoxTextView.m in Sources */, + 0479205B2EDEE1FC004E8522 /* CRSecrectImageView.m in Sources */, + 0479205C2EDEE1FC004E8522 /* CRBoxFlowLayout.m in Sources */, + 0479205D2EDEE1FC004E8522 /* CRBoxInputView.m in Sources */, + 0479205E2EDEE1FC004E8522 /* CRBoxInputCell.m in Sources */, + 0479205F2EDEE1FC004E8522 /* CRBoxInputCellProperty.m in Sources */, + 047920602EDEE1FC004E8522 /* CRLineView.m in Sources */, 047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */, 0477BE042EBC83130055D639 /* HomeMainVC.m in Sources */, 0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */, diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index 565e703..a2e49b7 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -24,6 +24,8 @@ #import "KBSexSelVC.h" #import "KBKeyboardPermissionManager.h" #import "KBVipPay.h" +#import "KBUserSessionManager.h" +#import "KBLoginVC.h" // 注意:用于判断系统已启用本输入法扩展的 bundle id 需与扩展 target 的 // PRODUCT_BUNDLE_IDENTIFIER 完全一致。 @@ -38,6 +40,9 @@ [[FGIAPManager shared] setConfigureWith:[IAPVerifyTransactionObj new]]; /// 2:配置国际化 [KBLocalizationManager shared].supportedLanguageCodes = @[ @"en", @"zh-Hans"]; + /// 3 : 处理token问题 +// [[KBUserSessionManager shared] bootstrapIfNeeded]; + // 首次安装/升级:重置“完全访问”记录,避免继承旧安装遗留在 Keychain 中的状态 static NSString *const kKBFullAccessRecordInitializedKey = @"KBFullAccessRecordInitialized"; @@ -49,12 +54,12 @@ } // 首次安装先进入性别选择页;点击 Skip 或确认后再进入主 TabBar - BOOL hasShownSexVC = [[NSUserDefaults standardUserDefaults] boolForKey:KBSexSelectShownKey]; - if (hasShownSexVC) { - [self setupRootVC]; - } else { +// BOOL hasShownSexVC = [[NSUserDefaults standardUserDefaults] boolForKey:KBSexSelectShownKey]; +// if (hasShownSexVC) { +// [self setupRootVC]; +// } else { [self setupSexSelectRootVC]; - } +// } // 主工程默认开启网络总开关(键盘扩展仍需用户允许完全访问后再行开启) [KBNetworkManager shared].enabled = YES; @@ -93,9 +98,13 @@ /// 首次安装时,展示性别选择页作为根控制器 - (void)setupSexSelectRootVC { + KBLoginVC *vc = [[KBLoginVC alloc] init]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; + self.window.rootViewController = vc; + + return; KBSexSelVC *sexVC = [KBSexSelVC new]; __weak typeof(self) weakSelf = self; diff --git a/keyBoard/Assets.xcassets/Login/Contents.json b/keyBoard/Assets.xcassets/Login/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/keyBoard/Assets.xcassets/Login/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/Contents.json b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/Contents.json new file mode 100644 index 0000000..f2137bb --- /dev/null +++ b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "login_bg_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "login_bg_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@2x.png b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@2x.png new file mode 100644 index 0000000..f982d86 Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@2x.png differ diff --git a/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@3x.png b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@3x.png new file mode 100644 index 0000000..9d4d212 Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_bg_icon.imageset/login_bg_icon@3x.png differ diff --git a/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/Contents.json b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/Contents.json new file mode 100644 index 0000000..e11b010 --- /dev/null +++ b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "login_email_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "login_email_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@2x.png b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@2x.png new file mode 100644 index 0000000..508cdd1 Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@2x.png differ diff --git a/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@3x.png b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@3x.png new file mode 100644 index 0000000..4bb611a Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_email_icon.imageset/login_email_icon@3x.png differ diff --git a/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/Contents.json b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/Contents.json new file mode 100644 index 0000000..a6dafab --- /dev/null +++ b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "login_jianp_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "login_jianp_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@2x.png b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@2x.png new file mode 100644 index 0000000..b60c190 Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@2x.png differ diff --git a/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@3x.png b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@3x.png new file mode 100644 index 0000000..dd5d3d2 Binary files /dev/null and b/keyBoard/Assets.xcassets/Login/login_jianp_icon.imageset/login_jianp_icon@3x.png differ diff --git a/keyBoard/Class/Login/VC/KBLoginVC.h b/keyBoard/Class/Login/VC/KBLoginVC.h new file mode 100644 index 0000000..0e935dc --- /dev/null +++ b/keyBoard/Class/Login/VC/KBLoginVC.h @@ -0,0 +1,16 @@ +// +// KBLoginVC.h +// keyBoard +// +// Created by Mac on 2025/12/2. +// + +#import "BaseViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface KBLoginVC : BaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Login/VC/KBLoginVC.m b/keyBoard/Class/Login/VC/KBLoginVC.m new file mode 100644 index 0000000..4aaf537 --- /dev/null +++ b/keyBoard/Class/Login/VC/KBLoginVC.m @@ -0,0 +1,308 @@ +// +// KBLoginVC.m +// keyBoard +// +// Created by Mac on 2025/12/2. +// + +#import "KBLoginVC.h" +#import + +@interface KBLoginVC () + +// 背景 +@property (nonatomic, strong) UIImageView *bgImageView; // 整体背景图:login_bg_icon +@property (nonatomic, strong) UIImageView *topRightImageView; // 顶部右侧装饰图:login_jianp_icon + +// 底部白色容器(仅上圆角 26) +@property (nonatomic, strong) UIView *contentContainerView; + +// 标题 +@property (nonatomic, strong) UILabel *titleLabel; + +// 按钮 +@property (nonatomic, strong) UIControl *appleLoginButton; // Apple 原生登录按钮(iOS13 以下降级为普通按钮) +@property (nonatomic, strong) UIButton *emailLoginButton; // 邮箱登录按钮 + +// 协议 & 底部文案 +@property (nonatomic, strong) UILabel *agreementLabel; // “By Continuing, You Agree To Our” +@property (nonatomic, strong) UIButton *policyButton; // “Terms Of Service & Privacy Policy” + +@property (nonatomic, strong) UILabel *noAccountLabel; // “Don't Have An Account?” +@property (nonatomic, strong) UIButton *signUpButton; // “Sign Up” +@property (nonatomic, strong) UIButton *forgotPasswordButton; // “Forgot Password?” + +@end + +@implementation KBLoginVC + +- (void)viewDidLoad { + [super viewDidLoad]; + // 使用全屏内容,隐藏自定义导航栏 + self.kb_enableCustomNavBar = NO; + self.view.backgroundColor = [UIColor whiteColor]; + + [self setupUI]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + // 仅白色容器的左上、右上圆角为 26 + if (!CGRectIsEmpty(self.contentContainerView.bounds)) { + UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight; + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.contentContainerView.bounds + byRoundingCorners:corners + cornerRadii:CGSizeMake(26, 26)]; + CAShapeLayer *mask = [CAShapeLayer layer]; + mask.frame = self.contentContainerView.bounds; + mask.path = path.CGPath; + self.contentContainerView.layer.mask = mask; + } +} + +#pragma mark - UI + +- (void)setupUI { + // 背景图 + [self.view addSubview:self.bgImageView]; + [self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.view); + }]; + + // 顶部右侧装饰图 + [self.view addSubview:self.topRightImageView]; + [self.topRightImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.view).offset(KB_StatusBarHeight() + KBFit(24)); + make.right.equalTo(self.view).offset(-KBFit(20)); + make.width.mas_equalTo(KBFit(244)); + make.height.mas_equalTo(KBFit(224)); + }]; + + // 底部白色容器 + [self.view addSubview:self.contentContainerView]; + [self.contentContainerView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.bottom.equalTo(self.view); + // 顶部位置大致贴合设计稿,可根据实际效果微调 + make.top.equalTo(self.view).offset(272); + }]; + + // 标题 + [self.contentContainerView addSubview:self.titleLabel]; + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.contentContainerView).offset(32); + make.centerX.equalTo(self.contentContainerView); + }]; + + // Apple 登录按钮 + [self.contentContainerView addSubview:self.appleLoginButton]; + [self.appleLoginButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.titleLabel.mas_bottom).offset(24); + make.left.equalTo(self.contentContainerView).offset(30); + make.right.equalTo(self.contentContainerView).offset(-30); + make.height.mas_equalTo(52); + }]; + + // 邮箱登录按钮 + [self.contentContainerView addSubview:self.emailLoginButton]; + [self.emailLoginButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.appleLoginButton.mas_bottom).offset(16); + make.left.right.height.equalTo(self.appleLoginButton); + }]; + + // 底部协议文案 + [self.contentContainerView addSubview:self.policyButton]; + [self.contentContainerView addSubview:self.agreementLabel]; + + [self.policyButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.contentContainerView); + make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 84); + }]; + + [self.agreementLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.contentContainerView); + make.bottom.equalTo(self.policyButton.mas_top).offset(-4); + }]; + + // 底部账号相关文案 + UIButton *forgot = self.forgotPasswordButton; + [self.contentContainerView addSubview:forgot]; + [forgot mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.contentContainerView); + make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 24); + }]; + + UIView *accountLine = [UIView new]; + [self.contentContainerView addSubview:accountLine]; + [accountLine mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.contentContainerView); + make.bottom.equalTo(forgot.mas_top).offset(-8); + }]; + + [accountLine addSubview:self.noAccountLabel]; + [accountLine addSubview:self.signUpButton]; + + [self.noAccountLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.left.bottom.equalTo(accountLine); + }]; + + [self.signUpButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.noAccountLabel.mas_right).offset(4); + make.right.equalTo(accountLine); + make.centerY.equalTo(self.noAccountLabel); + }]; +} + +#pragma mark - Actions + +- (void)onTapAppleLogin { + // 后续可接入 Apple Sign-In 逻辑,这里先占位 +} + +- (void)onTapEmailLogin { + // 后续接入邮箱登录逻辑 +} + +- (void)onTapPolicy { + // 打开服务条款/隐私政策 +} + +- (void)onTapSignUp { + // 打开注册页 +} + +- (void)onTapForgotPassword { + // 打开忘记密码页 +} + +#pragma mark - Lazy UI + +- (UIImageView *)bgImageView { + if (!_bgImageView) { + _bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"login_bg_icon"]]; + _bgImageView.contentMode = UIViewContentModeScaleAspectFill; + _bgImageView.clipsToBounds = YES; + } + return _bgImageView; +} + +- (UIImageView *)topRightImageView { + if (!_topRightImageView) { + _topRightImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"login_jianp_icon"]]; + _topRightImageView.contentMode = UIViewContentModeScaleAspectFit; + _topRightImageView.clipsToBounds = YES; + } + return _topRightImageView; +} + +- (UIView *)contentContainerView { + if (!_contentContainerView) { + _contentContainerView = [UIView new]; + _contentContainerView.backgroundColor = [UIColor whiteColor]; + } + return _contentContainerView; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.text = KBLocalized(@"Log In To Key Of Love"); + _titleLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _titleLabel.font = [KBFont bold:18]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +- (UIControl *)appleLoginButton { + if (!_appleLoginButton) { + ASAuthorizationAppleIDButton *btn = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeSignIn + style:ASAuthorizationAppleIDButtonStyleWhite]; + [btn addTarget:self action:@selector(onTapAppleLogin) forControlEvents:UIControlEventTouchUpInside]; + btn.cornerRadius = 10.0; + btn.layer.borderColor = [UIColor colorWithHex:0xE5E5E5].CGColor; + btn.layer.borderWidth = 1; + _appleLoginButton = btn; + } + return _appleLoginButton; +} + +- (UIButton *)emailLoginButton { + if (!_emailLoginButton) { + _emailLoginButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_emailLoginButton setTitle:KBLocalized(@"Continue Via Email") forState:UIControlStateNormal]; + [_emailLoginButton setTitleColor:[UIColor colorWithHex:KBBlackValue] forState:UIControlStateNormal]; + _emailLoginButton.titleLabel.font = [KBFont medium:16]; + _emailLoginButton.backgroundColor = [UIColor colorWithHex:0xF7F7F7]; + _emailLoginButton.layer.cornerRadius = 10.0; + _emailLoginButton.layer.masksToBounds = YES; + + UIImage *icon = [UIImage imageNamed:@"login_email_icon"]; + if (icon) { + [_emailLoginButton setImage:icon forState:UIControlStateNormal]; + // 图片在左,文字在右,保持与截图类似 + _emailLoginButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; + _emailLoginButton.imageEdgeInsets = UIEdgeInsetsMake(0, 16, 0, 0); + _emailLoginButton.titleEdgeInsets = UIEdgeInsetsMake(0, 32, 0, 0); + } + + [_emailLoginButton addTarget:self action:@selector(onTapEmailLogin) forControlEvents:UIControlEventTouchUpInside]; + } + return _emailLoginButton; +} + +- (UILabel *)agreementLabel { + if (!_agreementLabel) { + _agreementLabel = [UILabel new]; + _agreementLabel.text = KBLocalized(@"By Continuing, You Agree To Our"); + _agreementLabel.font = [KBFont regular:12]; + _agreementLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0]; + _agreementLabel.textAlignment = NSTextAlignmentCenter; + _agreementLabel.numberOfLines = 1; + } + return _agreementLabel; +} + +- (UIButton *)policyButton { + if (!_policyButton) { + _policyButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_policyButton setTitle:KBLocalized(@"Terms Of Service & Privacy Policy") forState:UIControlStateNormal]; + [_policyButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal]; + _policyButton.titleLabel.font = [KBFont regular:12]; + [_policyButton addTarget:self action:@selector(onTapPolicy) forControlEvents:UIControlEventTouchUpInside]; + } + return _policyButton; +} + +- (UILabel *)noAccountLabel { + if (!_noAccountLabel) { + _noAccountLabel = [UILabel new]; + _noAccountLabel.text = KBLocalized(@"Don't Have An Account?"); + _noAccountLabel.font = [KBFont regular:13]; + _noAccountLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0]; + } + return _noAccountLabel; +} + +- (UIButton *)signUpButton { + if (!_signUpButton) { + _signUpButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_signUpButton setTitle:KBLocalized(@"Sign Up") forState:UIControlStateNormal]; + [_signUpButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal]; + _signUpButton.titleLabel.font = [KBFont medium:13]; + [_signUpButton addTarget:self action:@selector(onTapSignUp) forControlEvents:UIControlEventTouchUpInside]; + } + return _signUpButton; +} + +- (UIButton *)forgotPasswordButton { + if (!_forgotPasswordButton) { + _forgotPasswordButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_forgotPasswordButton setTitle:KBLocalized(@"Forgot Password?") forState:UIControlStateNormal]; + [_forgotPasswordButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal]; + _forgotPasswordButton.titleLabel.font = [KBFont regular:13]; + [_forgotPasswordButton addTarget:self action:@selector(onTapForgotPassword) forControlEvents:UIControlEventTouchUpInside]; + } + return _forgotPasswordButton; +} + +@end diff --git a/keyBoard/Class/Manager/KBUserSessionManager.h b/keyBoard/Class/Manager/KBUserSessionManager.h new file mode 100644 index 0000000..b1c5e5e --- /dev/null +++ b/keyBoard/Class/Manager/KBUserSessionManager.h @@ -0,0 +1,42 @@ +// +// KBUserSessionManager.h +// keyBoard +// +// Created by Mac on 2025/12/1. +// + +// KBUserSessionManager.h +// 主 App + 键盘扩展 共用的登录会话管理 + +#import + +@class KBUser; + +NS_ASSUME_NONNULL_BEGIN + +@interface KBUserSessionManager : NSObject + ++ (instancetype)shared; + +/// 当前登录用户(可能为 nil) +@property (atomic, strong, readonly, nullable) KBUser *currentUser; + +/// 是否已登录:由 KBAuthManager 基于 accessToken 判断 +@property (nonatomic, assign, readonly) BOOL isLoggedIn; + +/// 启动时调用:做“卸载重装”检测 & 恢复本地缓存 +- (void)bootstrapIfNeeded; + +/// 当前 accessToken,可能为 nil/空字符串 +- (nullable NSString *)accessToken; + +/// 登录成功统一入口:写 Keychain + 缓存用户信息 +- (void)handleLoginSuccessWithUser:(KBUser *)user; + +/// 退出登录:清 Keychain + 本地缓存 +- (void)logout; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Manager/KBUserSessionManager.m b/keyBoard/Class/Manager/KBUserSessionManager.m new file mode 100644 index 0000000..4bea61e --- /dev/null +++ b/keyBoard/Class/Manager/KBUserSessionManager.m @@ -0,0 +1,155 @@ +// +// KBUserSessionManager.m +// keyBoard +// +// Created by Mac on 2025/12/1. +// + +#import "KBUserSessionManager.h" +#import "KBAuthManager.h" +#import "KBUser.h" +#import "KBConfig.h" +#import + +/// App Group 里的用户缓存 key +static NSString * const kKBSessionUserStoreKey = @"KBSession.currentUser"; +/// “本次安装是否已初始化”标记 key(用来识别卸载重装) +static NSString * const kKBSessionInstallFlagKey = @"KBSession.installInitialized"; + +@interface KBUserSessionManager () +@property (nonatomic, strong) NSUserDefaults *defaults; +@property (atomic, strong, readwrite, nullable) KBUser *currentUser; +@property (nonatomic, assign) BOOL didBootstrap; +@end + +@implementation KBUserSessionManager + ++ (instancetype)shared { + static KBUserSessionManager *m; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + m = [KBUserSessionManager new]; + }); + return m; +} + +- (instancetype)init { + if (self = [super init]) { + // 优先用 App Group 的 UserDefaults,App 和扩展共用一份 + NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:AppGroup]; + _defaults = ud ?: [NSUserDefaults standardUserDefaults]; + + // 监听 token 变化(例如别处调用了 signOut) + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_onAuthChanged:) + name:KBAuthChangedNotification + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Public + +- (void)bootstrapIfNeeded { + if (self.didBootstrap) return; + self.didBootstrap = YES; + + BOOL hasInitializedThisInstall = [self.defaults boolForKey:kKBSessionInstallFlagKey]; + + KBAuthManager *auth = [KBAuthManager shared]; // 内部会 reloadFromKeychain + + if (!hasInitializedThisInstall) { + // 说明是“卸载重装后的第一次运行”(或者真正第一次安装) + [self.defaults setBool:YES forKey:kKBSessionInstallFlagKey]; + [self.defaults synchronize]; + + // 若此时 Keychain 里已有 token,多半是上一次安装遗留的;按你的需求清掉 + if (auth.current.accessToken.length > 0) { + [auth signOut]; + } + + // 清除旧的用户缓存 + [self.defaults removeObjectForKey:kKBSessionUserStoreKey]; + [self.defaults synchronize]; + self.currentUser = nil; + } else { + // 正常启动:从本地恢复用户信息 + [self p_loadUserFromStore]; + } +} + +- (BOOL)isLoggedIn { + return [[KBAuthManager shared] isLoggedIn]; +} + +- (NSString *)accessToken { + return [KBAuthManager shared].current.accessToken; +} + +- (void)handleLoginSuccessWithUser:(KBUser *)user { + if (!user) return; + + // 先写 Keychain(统一走 KBAuthManager) + NSString *token = user.token; + if (token.length > 0) { + [[KBAuthManager shared] saveAccessToken:token + refreshToken:user.refreshToken + expiryDate:user.expiryDate + userIdentifier:user.appleUserId ?: user.userId]; + } + + // 再缓存用户信息,供 App/键盘使用 + self.currentUser = user; + [self p_saveUserToStore:user]; +} + +- (void)logout { + // 清 Keychain + [[KBAuthManager shared] signOut]; + + // 清本地用户缓存 + self.currentUser = nil; + [self.defaults removeObjectForKey:kKBSessionUserStoreKey]; + [self.defaults synchronize]; +} + +#pragma mark - Private + +- (void)p_loadUserFromStore { + id obj = [self.defaults objectForKey:kKBSessionUserStoreKey]; + if (![obj isKindOfClass:[NSDictionary class]]) { + self.currentUser = nil; + return; + } + KBUser *user = [KBUser mj_objectWithKeyValues:(NSDictionary *)obj]; + self.currentUser = user; +} + +- (void)p_saveUserToStore:(KBUser *)user { + if (!user) { + [self.defaults removeObjectForKey:kKBSessionUserStoreKey]; + [self.defaults synchronize]; + return; + } + NSDictionary *dict = [user mj_keyValues]; + if (dict) { + [self.defaults setObject:dict forKey:kKBSessionUserStoreKey]; + [self.defaults synchronize]; + } +} + +- (void)_onAuthChanged:(NSNotification *)note { + // 如果 token 被别处清掉了,这里同步清一下 currentUser + if (![[KBAuthManager shared] isLoggedIn]) { + self.currentUser = nil; + [self.defaults removeObjectForKey:kKBSessionUserStoreKey]; + [self.defaults synchronize]; + } +} + +@end + diff --git a/keyBoard/Class/Me/VC/KBPersonInfoVC.m b/keyBoard/Class/Me/VC/KBPersonInfoVC.m index 60e9cdb..b913c70 100644 --- a/keyBoard/Class/Me/VC/KBPersonInfoVC.m +++ b/keyBoard/Class/Me/VC/KBPersonInfoVC.m @@ -185,7 +185,7 @@ - (void)onTapAvatarEdit { [self presentImagePicker]; } - (void)onTapLogout { - // 示例:退出登录 + [[KBUserSessionManager shared] logout]; } #pragma mark - Lazy UI(懒加载) diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.h b/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.h new file mode 100644 index 0000000..4eda0a2 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.h @@ -0,0 +1,31 @@ +// +// CRBoxFlowLayout.h +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CRBoxFlowLayout : UICollectionViewFlowLayout + +/** ifNeedEqualGap + * default: YES + */ +@property (assign, nonatomic) BOOL ifNeedEqualGap; + +@property (assign, nonatomic) NSInteger itemNum; + +/** minLineSpacing +* default: 10 +*/ +@property (assign, nonatomic) NSInteger minLineSpacing; + +- (void)autoCalucateLineSpacing; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.m b/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.m new file mode 100644 index 0000000..84ce57b --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxFlowLayout.m @@ -0,0 +1,58 @@ +// +// CRBoxFlowLayout.m +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import "CRBoxFlowLayout.h" + +@implementation CRBoxFlowLayout + +- (instancetype)init +{ + self = [super init]; + + if (self) { + [self initPara]; + } + + return self; +} + +- (void)initPara +{ + self.ifNeedEqualGap = YES; + self.scrollDirection = UICollectionViewScrollDirectionHorizontal; + self.minLineSpacing = 10; + self.minimumLineSpacing = 0; + self.minimumInteritemSpacing = 0; + self.sectionInset = UIEdgeInsetsZero; + self.itemNum = 1; +} + +- (void)prepareLayout +{ + if (_ifNeedEqualGap) { + [self autoCalucateLineSpacing]; + } + + [super prepareLayout]; +} + +- (void)autoCalucateLineSpacing +{ + if (self.itemNum > 1) { + CGFloat width = CGRectGetWidth(self.collectionView.frame); + self.minimumLineSpacing = floor(1.0 * (width - self.itemNum * self.itemSize.width - self.collectionView.contentInset.left - self.collectionView.contentInset.right) / (self.itemNum - 1)); + + if (self.minimumLineSpacing < self.minLineSpacing) { + self.minimumLineSpacing = self.minLineSpacing; + } + }else{ + self.minimumLineSpacing = 0; + } +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.h b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.h new file mode 100644 index 0000000..02997ce --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.h @@ -0,0 +1,38 @@ +// +// CRBoxInputCell.h +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import +#import "CRBoxInputCellProperty.h" + +NS_ASSUME_NONNULL_BEGIN + +#define CRBoxCursoryAnimationKey @"CRBoxCursoryAnimationKey" +#define CRBoxInputCellID @"CRBoxInputCellID" + +@interface CRBoxInputCell : UICollectionViewCell + +/** + cursor + You should not use these properties, unless you know what you are doing. + */ +@property (strong, nonatomic) UIView *cursorView; +@property (assign, nonatomic) BOOL ifNeedCursor; + +/** + boxInputCellProperty + You should not use these properties, unless you know what you are doing. + */ +@property (strong, nonatomic) CRBoxInputCellProperty *boxInputCellProperty; + +// 你可以在继承的子类中重写父类方法 +// You can inherit and rewrite +- (UIView *)createCustomSecurityView __deprecated_msg("Please use `customSecurityViewBlock` in CRBoxInputCellProperty."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.m b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.m new file mode 100644 index 0000000..dabd120 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCell.m @@ -0,0 +1,281 @@ +// +// CRBoxInputCell.m +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import "CRBoxInputCell.h" +#import +#import "CRLineView.h" + +@interface CRBoxInputCell () +{ + +} + +@property (strong, nonatomic) UILabel *valueLabel; +@property (strong, nonatomic) CABasicAnimation *opacityAnimation; +@property (strong, nonatomic) UIView *customSecurityView; + +@property (strong, nonatomic) CRLineView *lineView; + +@end + +@implementation CRBoxInputCell + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + [self createUIBase]; + } + + return self; +} + +- (void)initPara +{ + self.ifNeedCursor = YES; + self.userInteractionEnabled = NO; +} + +- (void)createUIBase +{ + [self initPara]; + + _valueLabel = [UILabel new]; + _valueLabel.font = [UIFont systemFontOfSize:38]; + [self.contentView addSubview:_valueLabel]; + [_valueLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.offset(0); + make.centerY.offset(0); + }]; + + _cursorView = [UIView new]; + [self.contentView addSubview:_cursorView]; + [_cursorView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.offset(0); + make.centerY.offset(0); + }]; + + [self initCellProperty]; +} + +- (void)initCellProperty +{ + CRBoxInputCellProperty *cellProperty = [CRBoxInputCellProperty new]; + self.boxInputCellProperty = cellProperty; +} + +- (void)valueLabelLoadData +{ + _valueLabel.hidden = NO; + [self hideCustomSecurityView]; + + // 默认字体配置 + __weak typeof(self) weakSelf = self; + void (^defaultTextConfig)(void) = ^{ + if (weakSelf.boxInputCellProperty.cellFont) { + weakSelf.valueLabel.font = weakSelf.boxInputCellProperty.cellFont; + } + + if (weakSelf.boxInputCellProperty.cellTextColor) { + weakSelf.valueLabel.textColor = weakSelf.boxInputCellProperty.cellTextColor; + } + }; + + // 占位字符字体配置 + void (^placeholderTextConfig)(void) = ^{ + if (weakSelf.boxInputCellProperty.cellFont) { + weakSelf.valueLabel.font = weakSelf.boxInputCellProperty.cellPlaceholderFont; + } + + if (weakSelf.boxInputCellProperty.cellTextColor) { + weakSelf.valueLabel.textColor = weakSelf.boxInputCellProperty.cellPlaceholderTextColor; + } + }; + + BOOL hasOriginValue = self.boxInputCellProperty.originValue && self.boxInputCellProperty.originValue.length > 0; + if (hasOriginValue) { + if (self.boxInputCellProperty.ifShowSecurity) { + if (self.boxInputCellProperty.securityType == CRBoxSecuritySymbolType) { + _valueLabel.text = self.boxInputCellProperty.securitySymbol; + }else if (self.boxInputCellProperty.securityType == CRBoxSecurityCustomViewType) { + _valueLabel.hidden = YES; + [self showCustomSecurityView]; + } + + }else{ + _valueLabel.text = self.boxInputCellProperty.originValue; + } + defaultTextConfig(); + }else{ + BOOL hasPlaceholderText = self.boxInputCellProperty.cellPlaceholderText && self.boxInputCellProperty.cellPlaceholderText.length > 0; + // 有占位字符 + if (hasPlaceholderText) { + _valueLabel.text = self.boxInputCellProperty.cellPlaceholderText; + placeholderTextConfig(); + } + // 空 + else{ + _valueLabel.text = @""; + defaultTextConfig(); + } + } + + +} + +#pragma mark - Custom security view +- (void)showCustomSecurityView +{ + if (!self.customSecurityView.superview) { + [self.contentView addSubview:self.customSecurityView]; + [self.customSecurityView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(UIEdgeInsetsZero); + }]; + } + + self.customSecurityView.alpha = 1; +} + +- (void)hideCustomSecurityView +{ + // Must add this judge. Otherwise _customSecurityView maybe null, and cause error. + if (_customSecurityView) { + self.customSecurityView.alpha = 0; + } +} + +#pragma mark - Setter & Getter +- (CABasicAnimation *)opacityAnimation +{ + if (!_opacityAnimation) { + _opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + _opacityAnimation.fromValue = @(1.0); + _opacityAnimation.toValue = @(0.0); + _opacityAnimation.duration = 0.9; + _opacityAnimation.repeatCount = HUGE_VALF; + _opacityAnimation.removedOnCompletion = YES; + _opacityAnimation.fillMode = kCAFillModeForwards; + _opacityAnimation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; + } + + return _opacityAnimation; +} + +- (void)setSelected:(BOOL)selected +{ + if (selected) { + self.layer.borderColor = self.boxInputCellProperty.cellBorderColorSelected.CGColor; + self.backgroundColor = self.boxInputCellProperty.cellBgColorSelected; + }else{ + BOOL hasFill = _valueLabel.text.length > 0 ? YES : NO; + UIColor *cellBorderColor = self.boxInputCellProperty.cellBorderColorNormal; + UIColor *cellBackgroundColor = self.boxInputCellProperty.cellBgColorNormal; + if (hasFill) { + if (self.boxInputCellProperty.cellBorderColorFilled) { + cellBorderColor = self.boxInputCellProperty.cellBorderColorFilled; + } + if (self.boxInputCellProperty.cellBgColorFilled) { + cellBackgroundColor = self.boxInputCellProperty.cellBgColorFilled; + } + } + self.layer.borderColor = cellBorderColor.CGColor; + self.backgroundColor = cellBackgroundColor; + } + + if (_lineView) { + // 未选中 + if (!selected) { + if (self.boxInputCellProperty.originValue.length > 0 && _lineView.underlineColorFilled) { + // 有内容 + _lineView.lineView.backgroundColor = _lineView.underlineColorFilled; + }else if (_lineView.underlineColorNormal) { + // 无内容 + _lineView.lineView.backgroundColor = _lineView.underlineColorNormal; + }else{ + // 默认 + _lineView.lineView.backgroundColor = CRColorMaster; + } + } + // 已选中 + else if (selected && _lineView.underlineColorSelected){ + _lineView.lineView.backgroundColor = _lineView.underlineColorSelected; + } + // 默认 + else{ + _lineView.lineView.backgroundColor = CRColorMaster; + } + + _lineView.selected = selected; + } + + if (_ifNeedCursor) { + if (selected) { + _cursorView.hidden= NO; + [_cursorView.layer addAnimation:self.opacityAnimation forKey:CRBoxCursoryAnimationKey]; + }else{ + _cursorView.hidden= YES; + [_cursorView.layer removeAnimationForKey:CRBoxCursoryAnimationKey]; + } + }else{ + _cursorView.hidden= YES; + } +} + +- (void)setBoxInputCellProperty:(CRBoxInputCellProperty *)boxInputCellProperty +{ + _boxInputCellProperty = boxInputCellProperty; + + _cursorView.backgroundColor = boxInputCellProperty.cellCursorColor; + [_cursorView mas_updateConstraints:^(MASConstraintMaker *make) { + make.width.mas_equalTo(boxInputCellProperty.cellCursorWidth); + make.height.mas_equalTo(boxInputCellProperty.cellCursorHeight); + }]; + self.layer.cornerRadius = boxInputCellProperty.cornerRadius; + self.layer.borderWidth = boxInputCellProperty.borderWidth; + + [self valueLabelLoadData]; +} + +- (UIView *)customSecurityView +{ + if (!_customSecurityView) { + // Compatiable for 0.19 verion and earlier. + if ([self respondsToSelector:@selector(createCustomSecurityView)]) { + _customSecurityView = [self createCustomSecurityView]; + } + else if(_boxInputCellProperty.customSecurityViewBlock){ + NSAssert(_boxInputCellProperty.customSecurityViewBlock, @"customSecurityViewBlock can not be null!"); + _customSecurityView = _boxInputCellProperty.customSecurityViewBlock(); + } + } + + return _customSecurityView; +} + +- (void)layoutSubviews +{ + __weak typeof(self) weakSelf = self; + + if (_boxInputCellProperty.showLine && !_lineView) { + NSAssert(_boxInputCellProperty.customLineViewBlock, @"customLineViewBlock can not be null!"); + _lineView = _boxInputCellProperty.customLineViewBlock(); + [self.contentView addSubview:_lineView]; + [_lineView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.bottom.top.offset(0); + }]; + } + + if (_boxInputCellProperty.configCellShadowBlock) { + _boxInputCellProperty.configCellShadowBlock(weakSelf.layer); + } + + [super layoutSubviews]; +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.h b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.h new file mode 100644 index 0000000..fda3b04 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.h @@ -0,0 +1,200 @@ +// +// CRBoxInputCellProperty.h +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import +#import "CRLineView.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, CRBoxSecurityType) { + CRBoxSecuritySymbolType, + CRBoxSecurityCustomViewType, +}; + +typedef UIView *_Nonnull(^CustomSecurityViewBlock)(void); +typedef CRLineView *_Nonnull(^CustomLineViewBlock)(void); +typedef void(^ConfigCellShadowBlock)(CALayer *layer); + +@interface CRBoxInputCellProperty : NSObject + +#pragma mark - UI +/** + cell边框宽度 + 默认:0.5 + */ +@property (assign, nonatomic) CGFloat borderWidth; + +/** + cell边框颜色 + 状态:未选中状态时 + 默认:[UIColor colorWithRed:228/255.0 green:228/255.0 blue:228/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *cellBorderColorNormal; + +/** + cell边框颜色 + 状态:选中状态时 + 默认:[UIColor colorWithRed:255/255.0 green:70/255.0 blue:62/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *cellBorderColorSelected; + +/** + cell边框颜色 + 状态:填充文字后,未选中状态时 + 默认:nil + */ +@property (copy, nonatomic) UIColor *__nullable cellBorderColorFilled; + +/** + cell背景颜色 + 状态:无填充文字,未选中状态时 + 默认:[UIColor whiteColor] + */ +@property (copy, nonatomic) UIColor *cellBgColorNormal; + +/** + cell背景颜色 + 状态:选中状态时 + 默认:[UIColor whiteColor] + */ +@property (copy, nonatomic) UIColor *cellBgColorSelected; + +/** + cell背景颜色 + 状态:填充文字后,未选中状态时 + 默认:与cellBgColorFilled相同 + */ +@property (copy, nonatomic) UIColor *__nullable cellBgColorFilled; + + +/** + 光标颜色 + 默认: [UIColor colorWithRed:255/255.0 green:70/255.0 blue:62/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *cellCursorColor; + +/** + 光标宽度 + 默认: 2 + */ +@property (assign, nonatomic) CGFloat cellCursorWidth; + +/** + 光标高度 + 默认: 32 + */ +@property (assign, nonatomic) CGFloat cellCursorHeight; + +/** + 圆角 + 默认: 4 + */ +@property (assign, nonatomic) CGFloat cornerRadius; + + + +#pragma mark - line +/** + 显示下划线 + 默认: NO + */ +@property (assign, nonatomic) BOOL showLine; + + + +#pragma mark - label +/** + 字体/字号 + 默认:[UIFont systemFontOfSize:20]; + */ +@property (copy, nonatomic) UIFont *cellFont; + +/** + 字体颜色 + 默认:[UIColor blackColor]; + */ +@property (copy, nonatomic) UIColor *cellTextColor; + + + +#pragma mark - Security +/** + 是否密文显示 + 默认:NO + */ +@property (assign, nonatomic) BOOL ifShowSecurity; + +/** + 密文符号 + 默认:✱ + 说明:只有ifShowSecurity=YES时,有效 + */ +@property (copy, nonatomic) NSString *securitySymbol; + +/** + 保存当前显示的字符 + 若想一次性修改所有输入值,请使用 CRBoxInputView中的'reloadInputString'方法 + 禁止修改该值!!!(除非你知道该怎么使用它。) + */ +@property (copy, nonatomic, readonly) NSString *originValue; +- (void)setMyOriginValue:(NSString *)originValue; + +/** + 密文类型 + 默认:CRBoxSecuritySymbolType + 类型说明: + CRBoxSecuritySymbolType 符号类型,根据securitySymbol,originValue的内容来显示 + CRBoxSecurityCustomViewType 自定义View类型,可以自定义密文状态下的图片,View + */ +@property (assign, nonatomic) CRBoxSecurityType securityType; + + + +#pragma mark - Placeholder +/** + 占位符默认填充值 + 禁止修改该值!!!(除非你知道该怎么使用它。) + */ +@property (strong, nonatomic) NSString *__nullable cellPlaceholderText; + +/** + 占位符字体颜色 + 默认:[UIColor colorWithRed:114/255.0 green:126/255.0 blue:124/255.0 alpha:0.3]; + */ +@property (copy, nonatomic) UIColor *cellPlaceholderTextColor; + +/** + 占位符字体/字号 + 默认:[UIFont systemFontOfSize:20]; + */ +@property (copy, nonatomic) UIFont *cellPlaceholderFont; + + + +#pragma mark - Block +/** + 自定义密文View回调 + */ +@property (copy, nonatomic) CustomSecurityViewBlock customSecurityViewBlock; +/** + 自定义下划线回调 + */ +@property (copy, nonatomic) CustomLineViewBlock customLineViewBlock; +/** + 自定义阴影回调 + */ +@property (copy, nonatomic) ConfigCellShadowBlock __nullable configCellShadowBlock; + + + +#pragma mark - Test +@property (assign, nonatomic) NSInteger index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.m b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.m new file mode 100644 index 0000000..31a6d72 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputCellProperty.m @@ -0,0 +1,157 @@ +// +// CRBoxInputself.m +// CaiShenYe +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import "CRBoxInputCellProperty.h" +#import + +@interface CRBoxInputCellProperty () + +@property (copy, nonatomic, readwrite) NSString *originValue; + +@end + + +@implementation CRBoxInputCellProperty + +- (instancetype)init +{ + self = [super init]; + + if (self) { + + __weak typeof(self) weakSelf = self; + + // UI + self.borderWidth = (0.5); + self.cellBorderColorNormal = [UIColor colorWithRed:228/255.0 green:228/255.0 blue:228/255.0 alpha:1]; + self.cellBorderColorSelected = [UIColor colorWithRed:255/255.0 green:70/255.0 blue:62/255.0 alpha:1]; + self.cellBorderColorFilled = nil; + self.cellBgColorNormal = [UIColor whiteColor]; + self.cellBgColorSelected = [UIColor whiteColor]; + self.cellBgColorFilled = nil; + self.cellCursorColor = [UIColor colorWithRed:255/255.0 green:70/255.0 blue:62/255.0 alpha:1]; + self.cellCursorWidth = 2; + self.cellCursorHeight = 32; + self.cornerRadius = 4; + + // line + self.showLine = NO; + + // label + self.cellFont = [UIFont systemFontOfSize:20]; + self.cellTextColor = [UIColor blackColor]; + + // Security + self.ifShowSecurity = NO; + self.securitySymbol = @"✱"; + self.originValue = @""; + self.securityType = CRBoxSecuritySymbolType; + + // Placeholder + self.cellPlaceholderText = nil; + self.cellPlaceholderTextColor = [UIColor colorWithRed:114/255.0 green:116/255.0 blue:124/255.0 alpha:0.3]; + self.cellPlaceholderFont = [UIFont systemFontOfSize:20]; + + // Block + self.customSecurityViewBlock = ^UIView * _Nonnull{ + return [weakSelf defaultCustomSecurityView]; + }; + self.customLineViewBlock = ^CRLineView * _Nonnull{ + return [CRLineView new]; + }; + self.configCellShadowBlock = nil; + + // Test + self.index = 0; + } + + return self; +} + +#pragma mark - Copy +- (id)copyWithZone:(NSZone *)zone +{ + CRBoxInputCellProperty *copy = [[self class] allocWithZone:zone]; + + // UI + copy.borderWidth = _borderWidth; + copy.cellBorderColorNormal = [_cellBorderColorNormal copy]; + copy.cellBorderColorSelected = [_cellBorderColorSelected copy]; + if (_cellBorderColorFilled) { + copy.cellBorderColorFilled = [_cellBorderColorFilled copy]; + } + copy.cellBgColorNormal = [_cellBgColorNormal copy]; + copy.cellBgColorSelected = [_cellBgColorSelected copy]; + if (_cellBgColorFilled) { + copy.cellBgColorFilled = [_cellBgColorFilled copy]; + } + copy.cellCursorColor = [_cellCursorColor copy]; + copy.cellCursorWidth = _cellCursorWidth; + copy.cellCursorHeight = _cellCursorHeight; + copy.cornerRadius = _cornerRadius; + + // line + copy.showLine = _showLine; + + // label + copy.cellFont = [_cellFont copy]; + copy.cellTextColor = [_cellTextColor copy]; + + // Security + copy.ifShowSecurity = _ifShowSecurity; + copy.securitySymbol = [_securitySymbol copy]; + copy.originValue = [_originValue copy]; + copy.securityType = _securityType; + + // Placeholder + if (_cellPlaceholderText) { + copy.cellPlaceholderText = [_cellPlaceholderText copy]; + } + copy.cellPlaceholderTextColor = [_cellPlaceholderTextColor copy]; + copy.cellPlaceholderFont = [_cellPlaceholderFont copy]; + + // Block + copy.customSecurityViewBlock = [_customSecurityViewBlock copy]; + copy.customLineViewBlock = [_customLineViewBlock copy]; + if (_configCellShadowBlock) { + copy.configCellShadowBlock = [_configCellShadowBlock copy]; + } + + // Test + copy.index = _index; + + return copy; +} + +#pragma mark - Getter +- (UIView *)defaultCustomSecurityView +{ + UIView *customSecurityView = [UIView new]; + customSecurityView.backgroundColor = [UIColor clearColor]; + + // circleView + static CGFloat circleViewWidth = 20; + UIView *circleView = [UIView new]; + circleView.backgroundColor = [UIColor blackColor]; + circleView.layer.cornerRadius = 4; + [customSecurityView addSubview:circleView]; + [circleView mas_makeConstraints:^(MASConstraintMaker *make) { + make.width.height.mas_equalTo(circleViewWidth); + make.centerX.offset(0); + make.centerY.offset(0); + }]; + + return customSecurityView; +} + +#pragma mark - Setter +- (void)setMyOriginValue:(NSString *)originValue { + _originValue = originValue; +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.h b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.h new file mode 100755 index 0000000..9e0a2c7 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.h @@ -0,0 +1,165 @@ +// +// CRBoxInputView.h +// CRBoxInputView +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import +#import "CRBoxFlowLayout.h" +#import "CRBoxInputCellProperty.h" +#import "CRBoxInputCell.h" +@class CRBoxInputView; + +typedef NS_ENUM(NSInteger, CRTextEditStatus) { + CRTextEditStatus_Idle, + CRTextEditStatus_BeginEdit, + CRTextEditStatus_EndEdit, +}; + +typedef NS_ENUM(NSInteger, CRInputType) { + /// 数字 + CRInputType_Number, + /// 普通(不作任何处理) + CRInputType_Normal, + /// 自定义正则(此时需要设置customInputRegex) + CRInputType_Regex, +}; + +typedef void(^TextDidChangeblock)(NSString * _Nullable text, BOOL isFinished); +typedef void(^TextEditStatusChangeblock)(CRTextEditStatus editStatus); +typedef NSString *(^TextCustomProcessblock)(NSString * _Nullable text); + +@interface CRBoxInputView : UIView + +/** + 是否需要光标 + ifNeedCursor + default: YES + */ +@property (assign, nonatomic) BOOL ifNeedCursor; + +/** + 验证码长度 + codeLength + default: 4 + */ +@property (nonatomic, assign, readonly) NSInteger codeLength; //If you want to set codeLength, please use `- (instancetype)initWithCodeLength:(NSInteger)codeLength, or - (void)resetCodeLength:(NSInteger)codeLength beginEdit:(BOOL)beginEdit` in CRBoxInputView. + +/** + 是否开启密文模式 + 描述:你可以在任何时候修改该属性,并且已经存在的文字会自动刷新。 + ifNeedSecurity + desc: You can change this property anytime. And the existing texts can be refreshed automatically. + default: NO + */ +@property (assign, nonatomic) BOOL ifNeedSecurity; + +/** + 显示密文的延时时间 + securityDelay + desc: show security delay time + default: 0.3 + */ +@property (assign, nonatomic) CGFloat securityDelay; + +/** + 键盘类型 + keyBoardType + default: UIKeyboardTypeNumberPad + */ +@property (assign, nonatomic) UIKeyboardType keyBoardType; + +/** + 输入样式 + inputType + default: CRInputType_Number + */ +@property (assign, nonatomic) CRInputType inputType; + +/** +自定义正则匹配输入内容 +customInputRegex +default: @"" +当inputType == CRInputType_Regex时才会生效 +*/ +@property (copy, nonatomic) NSString * _Nullable customInputRegex; + +/** + textContentType + 描述: 你可以设置为 'nil' 或者 'UITextContentTypeOneTimeCode' 来自动获取短信验证码 + desc: You set this 'nil' or 'UITextContentTypeOneTimeCode' to auto fill verify code. + default: nil + */ +@property (null_unspecified,nonatomic,copy) UITextContentType textContentType NS_AVAILABLE_IOS(10_0); + +/** + 占位字符填充值 + 说明:在对应的输入框没有内容时,会显示该值。 + 默认:nil + */ +@property (strong, nonatomic) NSString * _Nullable placeholderText; + +/** + 弹出键盘时,是否清空所有输入 + 只有在输入的字数等于codeLength时,生效 + default: NO + */ +@property (assign, nonatomic) BOOL ifClearAllInBeginEditing; + + +@property (copy, nonatomic) TextDidChangeblock _Nullable textDidChangeblock; +@property (copy, nonatomic) TextEditStatusChangeblock _Nullable textEditStatusChangeblock; +/// 文本自定义处理 +@property (copy, nonatomic) TextCustomProcessblock _Nullable textCustomProcessblock; +@property (strong, nonatomic) CRBoxFlowLayout * _Nullable boxFlowLayout; +@property (strong, nonatomic) CRBoxInputCellProperty * _Nullable customCellProperty; +@property (strong, nonatomic, readonly) NSString * _Nullable textValue; +@property (strong, nonatomic) UIView * _Nullable inputAccessoryView; + +/** + 装载数据和准备界面 + desc: Load and prepareView + beginEdit: 自动开启编辑模式 + default: YES + */ +- (void)loadAndPrepareView; +- (void)loadAndPrepareViewWithBeginEdit:(BOOL)beginEdit; + +/** + 重载输入的数据(用来设置预设数据) + desc:Reload string. (You can use this function to set deault value) + */ +- (void)reloadInputString:(NSString *_Nullable)value; + +/** + 清空输入 + desc: Clear all + beginEdit: 自动开启编辑模式 + default: YES + */ +- (void)clearAll; +- (void)clearAllWithBeginEdit:(BOOL)beginEdit; + +- (UICollectionView *_Nullable)mainCollectionView; + +// 快速设置 +// Qiuck set +- (void)quickSetSecuritySymbol:(NSString *_Nullable)securitySymbol; + +// 你可以在继承的子类中调用父类方法 +// You can inherit and call super +- (void)initDefaultValue; + +// 你可以在继承的子类中重写父类方法 +// You can inherit and rewrite +- (UICollectionViewCell *_Nullable)customCollectionView:(UICollectionView *_Nullable)collectionView cellForItemAtIndexPath:(NSIndexPath *_Nullable)indexPath; + +// code Length 调整 +- (void)resetCodeLength:(NSInteger)codeLength beginEdit:(BOOL)beginEdit; + +// Init +- (instancetype _Nullable )initWithCodeLength:(NSInteger)codeLength; + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.m b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.m new file mode 100755 index 0000000..3e83eba --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxInputView.m @@ -0,0 +1,649 @@ +// +// CRBoxInputView.m +// CRBoxInputView +// +// Created by Chobits on 2019/1/3. +// Copyright © 2019 Chobits. All rights reserved. +// + +#import "CRBoxInputView.h" +#import +#import "CRBoxTextView.h" + +typedef NS_ENUM(NSInteger, CRBoxTextChangeType) { + CRBoxTextChangeType_NoChange, + CRBoxTextChangeType_Insert, + CRBoxTextChangeType_Delete, +}; + +@interface CRBoxInputView () +{ + NSInteger _oldLength; + BOOL _ifNeedBeginEdit; +} + +@property (nonatomic, assign) NSInteger codeLength; +@property (nonatomic, strong) UITapGestureRecognizer *tapGR; +@property (nonatomic, strong) CRBoxTextView *textView; +@property (nonatomic, strong) UICollectionView *mainCollectionView; +@property (nonatomic, strong) NSMutableArray *valueArr; +@property (nonatomic, strong) NSMutableArray *cellPropertyArr; + +@end + +@implementation CRBoxInputView + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self initDefaultValue]; + [self addNotificationObserver]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) { + [self initDefaultValue]; + [self addNotificationObserver]; + } + + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self initDefaultValue]; + [self addNotificationObserver]; + } + + return self; +} + +- (instancetype _Nullable )initWithCodeLength:(NSInteger)codeLength +{ + self = [super init]; + if (self) { + [self initDefaultValue]; + [self addNotificationObserver]; + self.codeLength = codeLength; + } + + return self; +} + +- (void)dealloc +{ + [self removeNotificationObserver]; +} + +#pragma mark - Notification Observer +- (void)addNotificationObserver +{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; +} + +- (void)removeNotificationObserver { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)applicationWillResignActive:(NSNotification *)notification +{ + // 触发home按下,光标动画移除 +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + // 重新进来后响应,光标动画重新开始 + [self reloadAllCell]; +} + +#pragma mark - You can inherit +- (void)initDefaultValue +{ + _oldLength = 0; + self.ifNeedSecurity = NO; + self.securityDelay = 0.3; + self.codeLength = 4; + self.ifNeedCursor = YES; + self.keyBoardType = UIKeyboardTypeNumberPad; + self.inputType = CRInputType_Number; + self.customInputRegex = @""; + self.backgroundColor = [UIColor clearColor]; + _valueArr = [NSMutableArray new]; + _ifNeedBeginEdit = NO; +} + +#pragma mark - LoadAndPrepareView +- (void)loadAndPrepareView +{ + [self loadAndPrepareViewWithBeginEdit:YES]; +} + +- (void)loadAndPrepareViewWithBeginEdit:(BOOL)beginEdit +{ + if (_codeLength<=0) { + NSAssert(NO, @"请输入大于0的验证码位数"); + return; + } + + [self generateCellPropertyArr]; + + // mainCollectionView + if (!self.mainCollectionView || ![self.subviews containsObject:self.mainCollectionView]) { + [self addSubview:self.mainCollectionView]; + [self.mainCollectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(UIEdgeInsetsZero); + }]; + } + + // textView + if (!self.textView || ![self.subviews containsObject:self.textView]) { + [self addSubview:self.textView]; + [self.textView mas_makeConstraints:^(MASConstraintMaker *make) { + make.width.height.mas_equalTo(0); + make.left.top.mas_equalTo(0); + }]; + } + + // tap + if (self.tapGR.view != self) { + [self addGestureRecognizer:self.tapGR]; + } + + if (![self.textView.text isEqualToString:self.customCellProperty.originValue]) { + self.textView.text = self.customCellProperty.originValue; + [self textDidChange:self.textView]; + } + + if (beginEdit) { + [self beginEdit]; + } +} + +- (void)generateCellPropertyArr +{ + [self.cellPropertyArr removeAllObjects]; + for (int i = 0; i < self.codeLength; i++) { + [self.cellPropertyArr addObject:[self.customCellProperty copy]]; + } +} + +#pragma mark - code Length 调整 +- (void)resetCodeLength:(NSInteger)codeLength beginEdit:(BOOL)beginEdit +{ + if (codeLength<=0) { + NSAssert(NO, @"请输入大于0的验证码位数"); + return; + } + + self.codeLength = codeLength; + [self generateCellPropertyArr]; + [self clearAllWithBeginEdit:beginEdit]; +} + +#pragma mark - Reload Input View +- (void)reloadInputString:(NSString *_Nullable)value +{ + if (![self.textView.text isEqualToString:value]) { + self.textView.text = value; + [self baseTextDidChange:self.textView manualInvoke:YES]; + } +} + +#pragma mark - UITextFieldDelegate +- (void)textFieldDidBeginEditing:(UITextField *)textField +{ + _ifNeedBeginEdit = YES; + + if (self.ifClearAllInBeginEditing && self.textValue.length == self.codeLength) { + [self clearAll]; + } + + if (self.textEditStatusChangeblock) { + self.textEditStatusChangeblock(CRTextEditStatus_BeginEdit); + } + + [self reloadAllCell]; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField +{ + _ifNeedBeginEdit = NO; + + if (self.textEditStatusChangeblock) { + self.textEditStatusChangeblock(CRTextEditStatus_EndEdit); + } + + [self reloadAllCell]; +} + +#pragma mark - TextViewEdit +- (void)beginEdit{ + if (![self.textView isFirstResponder]) { + [self.textView becomeFirstResponder]; + } +} + +- (void)endEdit{ + if ([self.textView isFirstResponder]) { + [self.textView resignFirstResponder]; + } +} + +- (void)clearAll +{ + [self clearAllWithBeginEdit:YES]; +} + +- (void)clearAllWithBeginEdit:(BOOL)beginEdit +{ + _oldLength = 0; + [_valueArr removeAllObjects]; + self.textView.text = @""; + [self allSecurityClose]; + [self reloadAllCell]; + [self triggerBlock]; + + if (beginEdit) { + [self beginEdit]; + } +} + +#pragma mark - UITextFieldDidChange +- (void)textDidChange:(UITextField *)textField { + [self baseTextDidChange:textField manualInvoke:NO]; +} + +/** + * 过滤输入内容 +*/ +- (NSString *)filterInputContent:(NSString *)inputStr { + + NSMutableString *mutableStr = [[NSMutableString alloc] initWithString:inputStr]; + if (self.inputType == CRInputType_Number) { + + /// 纯数字 + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^0-9]" options:0 error:nil]; + [regex replaceMatchesInString:mutableStr options:0 range:NSMakeRange(0, [mutableStr length]) withTemplate:@""]; + } else if (self.inputType == CRInputType_Normal) { + + /// 不处理 + nil; + } else if (self.inputType == CRInputType_Regex) { + + /// 自定义正则 + if (self.customInputRegex.length > 0) { + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:self.customInputRegex options:0 error:nil]; + [regex replaceMatchesInString:mutableStr options:0 range:NSMakeRange(0, [mutableStr length]) withTemplate:@""]; + } + } + + return [mutableStr copy]; +} + +/** + * textDidChange基操作 + * manualInvoke:是否为手动调用 + */ +- (void)baseTextDidChange:(UITextField *)textField manualInvoke:(BOOL)manualInvoke { + + __weak typeof(self) weakSelf = self; + NSString *verStr = textField.text; + + //有空格去掉空格 + verStr = [verStr stringByReplacingOccurrencesOfString:@" " withString:@""]; + verStr = [self filterInputContent:verStr]; + + //自定义处理 + if (self.textCustomProcessblock) { + verStr = self.textCustomProcessblock(verStr); + } + + if (verStr.length >= _codeLength) { + verStr = [verStr substringToIndex:_codeLength]; + [self endEdit]; + } + textField.text = verStr; + + // 判断删除/增加 + CRBoxTextChangeType boxTextChangeType = CRBoxTextChangeType_NoChange; + if (verStr.length > _oldLength) { + boxTextChangeType = CRBoxTextChangeType_Insert; + }else if (verStr.length < _oldLength){ + boxTextChangeType = CRBoxTextChangeType_Delete; + } + + // _valueArr + if (boxTextChangeType == CRBoxTextChangeType_Delete) { + [self setSecurityShow:NO index:_valueArr.count-1]; + [_valueArr removeLastObject]; + + }else if (boxTextChangeType == CRBoxTextChangeType_Insert){ + if (verStr.length > 0) { + if (_valueArr.count > 0) { + [self replaceValueArrToAsteriskWithIndex:_valueArr.count - 1 needEqualToCount:NO]; + } +// NSString *subStr = [verStr substringWithRange:NSMakeRange(verStr.length - 1, 1)]; +// [strongSelf.valueArr addObject:subStr]; + [_valueArr removeAllObjects]; + + [verStr enumerateSubstringsInRange:NSMakeRange(0, verStr.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf.valueArr addObject:substring]; + }]; + + if (self.ifNeedSecurity) { + if (manualInvoke) { + // 处理所有秘文 + [self delaySecurityProcessAll]; + }else { + // 只处理最后一个秘文 + [self delaySecurityProcessLastOne]; + } + } + } + } + [self reloadAllCell]; + + _oldLength = verStr.length; + + if (boxTextChangeType != CRBoxTextChangeType_NoChange) { + [self triggerBlock]; + } +} + +#pragma mark - Control security show +- (void)setSecurityShow:(BOOL)isShow index:(NSInteger)index +{ + if (index < 0) { + NSAssert(NO, @"index必须大于等于0"); + return; + } + + CRBoxInputCellProperty *cellProperty = self.cellPropertyArr[index]; + cellProperty.ifShowSecurity = isShow; +} + +- (void)allSecurityClose +{ + [self.cellPropertyArr enumerateObjectsUsingBlock:^(CRBoxInputCellProperty * _Nonnull cellProperty, NSUInteger idx, BOOL * _Nonnull stop) { + if (cellProperty.ifShowSecurity == YES) { + cellProperty.ifShowSecurity = NO; + } + }]; +} + +- (void)allSecurityOpen +{ + [self.cellPropertyArr enumerateObjectsUsingBlock:^(CRBoxInputCellProperty * _Nonnull cellProperty, NSUInteger idx, BOOL * _Nonnull stop) { + if (cellProperty.ifShowSecurity == NO) { + cellProperty.ifShowSecurity = YES; + } + }]; +} + + +#pragma mark - Trigger block +- (void)triggerBlock +{ + if (self.textDidChangeblock) { + BOOL isFinished = _valueArr.count == _codeLength ? YES : NO; + self.textDidChangeblock(_textView.text, isFinished); + } +} + +#pragma mark - Asterisk 替换密文 +/** + * 替换密文 + * needEqualToCount:是否只替换最后一个 + */ +- (void)replaceValueArrToAsteriskWithIndex:(NSInteger)index needEqualToCount:(BOOL)needEqualToCount +{ + if (!self.ifNeedSecurity) { + return; + } + + if (needEqualToCount && index != _valueArr.count - 1) { + return; + } + + [self setSecurityShow:YES index:index]; +} + +#pragma mark 延时替换最后一个密文 +- (void)delaySecurityProcessLastOne +{ + __weak __typeof(self)weakSelf = self; + [self delayAfter:self.securityDelay dealBlock:^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (strongSelf.valueArr.count > 0) { + [strongSelf replaceValueArrToAsteriskWithIndex:strongSelf.valueArr.count-1 needEqualToCount:YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + [strongSelf reloadAllCell]; + }); + } + }]; +} + +#pragma mark 延时替换所有一个密文 +- (void)delaySecurityProcessAll +{ + __weak __typeof(self)weakSelf = self; + [self.valueArr enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf replaceValueArrToAsteriskWithIndex:idx needEqualToCount:NO]; + }]; + + [self reloadAllCell]; +} + +#pragma mark - DelayBlock +- (void)delayAfter:(CGFloat)delayTime dealBlock:(void (^)(void))dealBlock +{ + dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime *NSEC_PER_SEC)); + dispatch_after(timer, dispatch_get_main_queue(), ^{ + if (dealBlock) { + dealBlock(); + } + }); +} + +#pragma mark - UICollectionViewDataSource +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return _codeLength; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + id tempCell = [self customCollectionView:collectionView cellForItemAtIndexPath:indexPath]; + + if ([tempCell isKindOfClass:[CRBoxInputCell class]]) { + + CRBoxInputCell *cell = (CRBoxInputCell *)tempCell; + cell.ifNeedCursor = self.ifNeedCursor; + + // CellProperty + CRBoxInputCellProperty *cellProperty = self.cellPropertyArr[indexPath.row]; + cellProperty.index = indexPath.row; + + NSString *currentPlaceholderStr = nil; + if (_placeholderText.length > indexPath.row) { + currentPlaceholderStr = [_placeholderText substringWithRange:NSMakeRange(indexPath.row, 1)]; + cellProperty.cellPlaceholderText = currentPlaceholderStr; + } + + // setOriginValue + NSUInteger focusIndex = _valueArr.count; + if (_valueArr.count > 0 && indexPath.row <= focusIndex - 1) { + [cellProperty setMyOriginValue:_valueArr[indexPath.row]]; + }else{ + [cellProperty setMyOriginValue:@""]; + } + + cell.boxInputCellProperty = cellProperty; + + if (_ifNeedBeginEdit) { + cell.selected = indexPath.row == focusIndex ? YES : NO; + }else{ + cell.selected = NO; + } + } + + return tempCell; +} + +- (void)reloadAllCell +{ + [self.mainCollectionView reloadData]; + + NSUInteger focusIndex = _valueArr.count; + /// 最后一个 + if (focusIndex == self.codeLength) { + [self.mainCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:focusIndex - 1 inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:YES]; + } else { + [self.mainCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:focusIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; + } +} + +#pragma mark - Qiuck set +- (void)quickSetSecuritySymbol:(NSString *)securitySymbol +{ + if (securitySymbol.length != 1) { + securitySymbol = @"✱"; + } + + self.customCellProperty.securitySymbol = securitySymbol; +} + +#pragma mark - You can rewrite +- (UICollectionViewCell *)customCollectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + CRBoxInputCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CRBoxInputCellID forIndexPath:indexPath]; + return cell; +} + +#pragma mark - Setter & Getter +- (UITapGestureRecognizer *)tapGR +{ + if (!_tapGR) { + _tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(beginEdit)]; + } + + return _tapGR; +} + +- (UICollectionView *)mainCollectionView +{ + if (!_mainCollectionView) { + _mainCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.boxFlowLayout]; + _mainCollectionView.showsHorizontalScrollIndicator = NO; + _mainCollectionView.backgroundColor = [UIColor clearColor]; + _mainCollectionView.delegate = self; + _mainCollectionView.dataSource = self; + _mainCollectionView.layer.masksToBounds = YES; + _mainCollectionView.clipsToBounds = YES; + [_mainCollectionView registerClass:[CRBoxInputCell class] forCellWithReuseIdentifier:CRBoxInputCellID]; + } + + return _mainCollectionView; +} + +- (CRBoxFlowLayout *)boxFlowLayout +{ + if (!_boxFlowLayout) { + _boxFlowLayout = [CRBoxFlowLayout new]; + _boxFlowLayout.itemSize = CGSizeMake(42, 47); + } + + return _boxFlowLayout; +} + +- (void)setCodeLength:(NSInteger)codeLength +{ + _codeLength = codeLength; + self.boxFlowLayout.itemNum = codeLength; +} + +- (void)setKeyBoardType:(UIKeyboardType)keyBoardType{ + _keyBoardType = keyBoardType; + self.textView.keyboardType = keyBoardType; +} + +- (CRBoxTextView *)textView{ + if (!_textView) { + _textView = [CRBoxTextView new]; +// _textView.alpha = 0.1; +// _textView.tintColor = [UIColor clearColor]; +// _textView.backgroundColor = [UIColor clearColor]; +// _textView.textColor = [UIColor clearColor]; + _textView.delegate = self; + [_textView addTarget:self action:@selector(textDidChange:) forControlEvents:UIControlEventEditingChanged]; + } + return _textView; +} + +- (void)setTextContentType:(UITextContentType)textContentType +{ + _textContentType = textContentType; + + _textView.textContentType = textContentType; +} + +- (CRBoxInputCellProperty *)customCellProperty +{ + if (!_customCellProperty) { + _customCellProperty = [CRBoxInputCellProperty new]; + } + + return _customCellProperty; +} + +- (NSMutableArray *)cellPropertyArr +{ + if (!_cellPropertyArr) { + _cellPropertyArr = [NSMutableArray new]; + } + + return _cellPropertyArr; +} + +- (NSString *)textValue +{ + return _textView.text; +} + +@synthesize inputAccessoryView = _inputAccessoryView; +- (void)setInputAccessoryView:(UIView *)inputAccessoryView +{ + _inputAccessoryView = inputAccessoryView; + self.textView.inputAccessoryView = _inputAccessoryView; +} + +- (UIView *)inputAccessoryView +{ + return _inputAccessoryView; +} + +- (void)setIfNeedSecurity:(BOOL)ifNeedSecurity +{ + _ifNeedSecurity = ifNeedSecurity; + + if (ifNeedSecurity == YES) { + [self allSecurityOpen]; + }else{ + [self allSecurityClose]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self reloadAllCell]; + }); +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.h b/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.h new file mode 100644 index 0000000..61c12ca --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.h @@ -0,0 +1,16 @@ +// +// CRBoxTextView.h +// CRBoxInputView +// +// Created by Chobits on 2019/1/3. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CRBoxTextView : UITextField + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.m b/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.m new file mode 100644 index 0000000..5ca98d6 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRBoxTextView.m @@ -0,0 +1,20 @@ +// +// CRBoxTextView.m +// CRBoxInputView +// +// Created by Chobits on 2019/1/3. +// + +#import "CRBoxTextView.h" + +@implementation CRBoxTextView + +/** + * /禁止可被粘贴复制 + */ +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender +{ + return NO; +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRLineView.h b/keyBoard/Class/Vender/CRBoxInputView/CRLineView.h new file mode 100644 index 0000000..9832a8b --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRLineView.h @@ -0,0 +1,52 @@ +// +// CRLineView.h +// CRBoxInputView_Example +// +// Created by Chobits on 2019/6/10. +// Copyright © 2019 BearRan. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define CRColorMaster [UIColor colorWithRed:49/255.0 green:51/255.0 blue:64/255.0 alpha:1] + +@interface CRLineView : UIView + +typedef void(^CRLineViewSelectChangeBlock)(CRLineView *lineView, BOOL selected); + +@property (strong, nonatomic) UIView *lineView; +@property (assign, nonatomic) BOOL selected; + +/** + 下划线颜色 + 状态:未选中状态,且没有填充文字时 + 默认:[UIColor colorWithRed:49/255.0 green:51/255.0 blue:64/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *underlineColorNormal; + +/** + 下划线颜色 + 状态:选中状态时 + 默认:[UIColor colorWithRed:49/255.0 green:51/255.0 blue:64/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *underlineColorSelected; + +/** + 下划线颜色 + 状态:未选中状态,且有填充文字时 + 默认:[UIColor colorWithRed:49/255.0 green:51/255.0 blue:64/255.0 alpha:1] + */ +@property (copy, nonatomic) UIColor *underlineColorFilled; + +/** + 选择状态改变时回调 + */ +@property (copy, nonatomic) CRLineViewSelectChangeBlock __nullable selectChangeBlock; + +- (instancetype)initWithFrame:(CGRect)frame UNAVAILABLE_ATTRIBUTE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRLineView.m b/keyBoard/Class/Vender/CRBoxInputView/CRLineView.m new file mode 100644 index 0000000..353b49d --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRLineView.m @@ -0,0 +1,60 @@ +// +// CRLineView.m +// CRBoxInputView_Example +// +// Created by Chobits on 2019/6/10. +// Copyright © 2019 BearRan. All rights reserved. +// + +#import "CRLineView.h" +#import + +@interface CRLineView() +{ + +} +@end + +@implementation CRLineView + +- (instancetype)init +{ + self = [super init]; + if (self) { + _underlineColorNormal = CRColorMaster; + _underlineColorSelected = CRColorMaster; + _underlineColorFilled = CRColorMaster; + [self createUI]; + } + return self; +} + +- (void)createUI +{ + static CGFloat sepLineViewHeight = 4; + + _lineView = [UIView new]; + [self addSubview:_lineView]; + _lineView.backgroundColor = _underlineColorNormal; + _lineView.layer.cornerRadius = sepLineViewHeight / 2.0; + [_lineView mas_makeConstraints:^(MASConstraintMaker *make) { + make.height.mas_equalTo(sepLineViewHeight); + make.left.right.bottom.offset(0); + }]; + + _lineView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.2].CGColor; + _lineView.layer.shadowOpacity = 1; + _lineView.layer.shadowOffset = CGSizeMake(0, 2); + _lineView.layer.shadowRadius = 4; +} + +- (void)setSelected:(BOOL)selected { + _selected = selected; + + if (self.selectChangeBlock) { + __weak __typeof(self)weakSelf = self; + self.selectChangeBlock(weakSelf, selected); + } +} + +@end diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.h b/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.h new file mode 100644 index 0000000..74ad8a1 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.h @@ -0,0 +1,21 @@ +// +// CRSecrectImageView.h +// CRBoxInputView_Example +// +// Created by Chobits on 2019/6/10. +// Copyright © 2019 BearRan. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CRSecrectImageView : UIView + +@property (strong, nonatomic) UIImage *image; +@property (assign, nonatomic) CGFloat imageWidth; +@property (assign, nonatomic) CGFloat imageHeight; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.m b/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.m new file mode 100644 index 0000000..a3a0bf6 --- /dev/null +++ b/keyBoard/Class/Vender/CRBoxInputView/CRSecrectImageView.m @@ -0,0 +1,65 @@ +// +// CRSecrectImageView.m +// CRBoxInputView_Example +// +// Created by Chobits on 2019/6/10. +// Copyright © 2019 BearRan. All rights reserved. +// + +#import "CRSecrectImageView.h" +#import + +@interface CRSecrectImageView() +{ + UIImageView *_lockImgView; +} +@end + +@implementation CRSecrectImageView + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self createUI]; + } + return self; +} + +- (void)createUI +{ + _lockImgView = [UIImageView new]; + _lockImgView.image = [UIImage imageNamed:@"smallLock"]; + [self addSubview:_lockImgView]; + [_lockImgView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.offset(0); + make.centerY.offset(0); + make.width.mas_equalTo(23); + make.height.mas_equalTo(27); + }]; +} + +#pragma mark - Setter & Getter +- (void)setImage:(UIImage *)image +{ + _image = image; + _lockImgView.image = image; +} + +- (void)setImageWidth:(CGFloat)imageWidth +{ + _imageWidth = imageWidth; + [_lockImgView mas_updateConstraints:^(MASConstraintMaker *make) { + make.width.mas_equalTo(imageWidth); + }]; +} + +- (void)setImageHeight:(CGFloat)imageHeight +{ + _imageHeight = imageHeight; + [_lockImgView mas_updateConstraints:^(MASConstraintMaker *make) { + make.height.mas_equalTo(imageHeight); + }]; +} + +@end diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index 0dd7a9a..ceeb056 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -25,6 +25,8 @@ #import "KBHUD.h" #import "KBAlert.h" #import "KBNetworkManager.h" +#import "KBUserSessionManager.h" + /// 项目