From 5e4c16c577e0100527247e6192d6f789e5b9354a Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Tue, 2 Dec 2025 19:19:25 +0800 Subject: [PATCH] 2 --- Shared/KBConfig.h | 2 +- keyBoard/Class/Login/VC/KBLoginVC.m | 188 +++++++++++++++++++++------- 2 files changed, 147 insertions(+), 43 deletions(-) diff --git a/Shared/KBConfig.h b/Shared/KBConfig.h index ccbf5ea..a7f141c 100644 --- a/Shared/KBConfig.h +++ b/Shared/KBConfig.h @@ -32,7 +32,7 @@ // 基础baseUrl #ifndef KB_BASE_URL //#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/" -#define KB_BASE_URL @"http://192.168.1.144:7529/api" +#define KB_BASE_URL @"http://192.168.2.21:7529/api" #endif #import "KBFont.h" diff --git a/keyBoard/Class/Login/VC/KBLoginVC.m b/keyBoard/Class/Login/VC/KBLoginVC.m index 4aaf537..6c910e4 100644 --- a/keyBoard/Class/Login/VC/KBLoginVC.m +++ b/keyBoard/Class/Login/VC/KBLoginVC.m @@ -7,8 +7,9 @@ #import "KBLoginVC.h" #import +#import "KBLoginVM.h" -@interface KBLoginVC () +@interface KBLoginVC () // 背景 @property (nonatomic, strong) UIImageView *bgImageView; // 整体背景图:login_bg_icon @@ -25,8 +26,7 @@ @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) UITextView *agreementTextView; // 底部协议富文本 @property (nonatomic, strong) UILabel *noAccountLabel; // “Don't Have An Account?” @property (nonatomic, strong) UIButton *signUpButton; // “Sign Up” @@ -58,6 +58,11 @@ mask.path = path.CGPath; self.contentContainerView.layer.mask = mask; } + + // 让按钮内部的图片+文字整体居中,图片与文字间距为 50(按设计稿缩放) +// if (self.emailLoginButton.currentImage && self.emailLoginButton.currentTitle.length > 0) { +// [self kb_centerImageAndTitleForButton:self.emailLoginButton spacing:KBFit(50)]; +// } } #pragma mark - UI @@ -83,13 +88,13 @@ [self.contentContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.bottom.equalTo(self.view); // 顶部位置大致贴合设计稿,可根据实际效果微调 - make.top.equalTo(self.view).offset(272); + make.top.equalTo(self.view).offset(KBFit(272)); }]; // 标题 [self.contentContainerView addSubview:self.titleLabel]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.contentContainerView).offset(32); + make.top.equalTo(self.contentContainerView).offset(27); make.centerX.equalTo(self.contentContainerView); }]; @@ -105,24 +110,19 @@ // 邮箱登录按钮 [self.contentContainerView addSubview:self.emailLoginButton]; [self.emailLoginButton mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.appleLoginButton.mas_bottom).offset(16); + make.top.equalTo(self.appleLoginButton.mas_bottom).offset(15); make.left.right.height.equalTo(self.appleLoginButton); }]; - // 底部协议文案 - [self.contentContainerView addSubview:self.policyButton]; - [self.contentContainerView addSubview:self.agreementLabel]; - - [self.policyButton mas_makeConstraints:^(MASConstraintMaker *make) { + // 底部协议文案(单个富文本视图,内部 terms/privacy 文案可点击) + [self.contentContainerView addSubview:self.agreementTextView]; + [self.agreementTextView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.contentContainerView); + make.left.greaterThanOrEqualTo(self.contentContainerView).offset(30); + make.right.lessThanOrEqualTo(self.contentContainerView).offset(-30); 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]; @@ -156,22 +156,35 @@ - (void)onTapAppleLogin { // 后续可接入 Apple Sign-In 逻辑,这里先占位 + KBLOG(@"onTapAppleLogin"); + [[KBLoginVM shared] signInWithAppleFromViewController:KB_CURRENT_NAV completion:^(BOOL success, NSError * _Nullable error) { + if (success) { + [KBHUD showInfo:KBLocalized(@"Signed in successfully")]; + } else { + NSString *msg = error.localizedDescription ?: KBLocalized(@"Sign-in failed"); + [KBHUD showInfo:msg]; + } + }]; } - (void)onTapEmailLogin { // 后续接入邮箱登录逻辑 + KBLOG(@"onTapEmailLogin"); } - (void)onTapPolicy { // 打开服务条款/隐私政策 + KBLOG(@"onTapPolicy"); } - (void)onTapSignUp { // 打开注册页 + KBLOG(@"onTapSignUp"); } - (void)onTapForgotPassword { // 打开忘记密码页 + KBLOG(@"onTapForgotPassword"); } #pragma mark - Lazy UI @@ -221,6 +234,8 @@ btn.cornerRadius = 10.0; btn.layer.borderColor = [UIColor colorWithHex:0xE5E5E5].CGColor; btn.layer.borderWidth = 1; + btn.layer.cornerRadius = 10.0; + btn.layer.masksToBounds = true; _appleLoginButton = btn; } return _appleLoginButton; @@ -232,17 +247,27 @@ [_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.backgroundColor = [UIColor colorWithHex:0xF7F7F7]; _emailLoginButton.layer.cornerRadius = 10.0; _emailLoginButton.layer.masksToBounds = YES; - + _emailLoginButton.layer.borderColor = [UIColor colorWithHex:0xE5E5E5].CGColor; + _emailLoginButton.layer.borderWidth = 1; + _emailLoginButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); + _emailLoginButton.titleEdgeInsets = UIEdgeInsetsMake(0, 15, 0, 0); + 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); + // 将邮箱图标缩小一点,让视觉上更接近系统 Apple 按钮的图标大小 + CGFloat targetHeight = 12; // 设计上略小于按钮高度 + CGFloat scale = targetHeight / icon.size.height; + CGSize targetSize = CGSizeMake(icon.size.width * scale, targetHeight); + + UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0.0); + [icon drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)]; + UIImage *scaledIcon = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [_emailLoginButton setImage:scaledIcon forState:UIControlStateNormal]; } [_emailLoginButton addTarget:self action:@selector(onTapEmailLogin) forControlEvents:UIControlEventTouchUpInside]; @@ -250,27 +275,57 @@ 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; -} +- (UITextView *)agreementTextView { + if (!_agreementTextView) { + _agreementTextView = [UITextView new]; + _agreementTextView.backgroundColor = [UIColor clearColor]; + _agreementTextView.editable = NO; // 不可编辑 + _agreementTextView.selectable = NO; // 不可选中文本 + _agreementTextView.scrollEnabled = NO; + _agreementTextView.textAlignment = NSTextAlignmentCenter; + _agreementTextView.textContainerInset = UIEdgeInsetsZero; + _agreementTextView.textContainer.lineFragmentPadding = 0; -- (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]; + // 协议文案:terms of service / privacy policy 为纯黑色并可点击,其余为 #717171 + NSString *fullText = @"By continuing, you agree to our terms of service and confirm that you have read our privacy policy"; + NSString *termsText = @"terms of service"; + NSString *privacyText = @"privacy policy"; + + NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:fullText + attributes:@{ + NSFontAttributeName : [KBFont regular:12], + NSForegroundColorAttributeName : [UIColor colorWithHex:0x717171] + }]; + + NSString *lowerFull = fullText.lowercaseString; + NSRange termsRange = [lowerFull rangeOfString:termsText.lowercaseString]; + if (termsRange.location != NSNotFound) { + [attr addAttributes:@{ + NSForegroundColorAttributeName : [UIColor colorWithHex:KBBlackValue], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleNone) + } range:termsRange]; + } + + NSRange privacyRange = [lowerFull rangeOfString:privacyText.lowercaseString]; + if (privacyRange.location != NSNotFound) { + [attr addAttributes:@{ + NSForegroundColorAttributeName : [UIColor colorWithHex:KBBlackValue], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleNone) + } range:privacyRange]; + } + + _agreementTextView.linkTextAttributes = @{ + NSForegroundColorAttributeName : [UIColor colorWithHex:KBBlackValue], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleNone) + }; + _agreementTextView.attributedText = attr; + + // 自定义点击识别,避免系统选择/复制 + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(kb_handleAgreementTap:)]; + _agreementTextView.userInteractionEnabled = YES; + [_agreementTextView addGestureRecognizer:tap]; } - return _policyButton; + return _agreementTextView; } - (UILabel *)noAccountLabel { @@ -305,4 +360,53 @@ return _forgotPasswordButton; } +#pragma mark - Helper + +/// 让按钮内部图片和文字整体居中,并设置固定间距(spacing 为设计稿值,调用时已做 KBFit) +- (void)kb_centerImageAndTitleForButton:(UIButton *)button spacing:(CGFloat)spacing { + if (!button.currentImage || button.currentTitle.length == 0) { return; } + + button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; + + CGSize imageSize = button.imageView.image.size; + CGSize titleSize = button.titleLabel.intrinsicContentSize; + CGFloat totalWidth = imageSize.width + spacing + titleSize.width; + + CGFloat imageOffsetX = -(totalWidth / 2.0 - imageSize.width / 2.0); + CGFloat titleOffsetX = totalWidth / 2.0 - titleSize.width / 2.0; + + button.imageEdgeInsets = UIEdgeInsetsMake(0, imageOffsetX, 0, -imageOffsetX); + button.titleEdgeInsets = UIEdgeInsetsMake(0, titleOffsetX, 0, -titleOffsetX); +} + +#pragma mark - Agreement Tap + +- (void)kb_handleAgreementTap:(UITapGestureRecognizer *)tap { + UITextView *textView = self.agreementTextView; + CGPoint point = [tap locationInView:textView]; + + // 将点击点转换到 textContainer 坐标系 + CGPoint location = point; + location.x -= textView.textContainerInset.left; + location.y -= textView.textContainerInset.top; + + NSLayoutManager *layoutManager = textView.layoutManager; + NSTextContainer *textContainer = textView.textContainer; + NSUInteger glyphIndex = [layoutManager glyphIndexForPoint:location + inTextContainer:textContainer]; + if (glyphIndex >= textView.textStorage.length) { return; } + + NSUInteger charIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex]; + + NSString *lowerFull = textView.text.lowercaseString ?: @""; + NSRange termsRange = [lowerFull rangeOfString:@"terms of service"]; + NSRange privacyRange = [lowerFull rangeOfString:@"privacy policy"]; + + BOOL hitTerms = (termsRange.location != NSNotFound && NSLocationInRange(charIndex, termsRange)); + BOOL hitPrivacy = (privacyRange.location != NSNotFound && NSLocationInRange(charIndex, privacyRange)); + if (hitTerms || hitPrivacy) { + [self onTapPolicy]; + } +} + @end