2
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
|
||||
#import "KBLoginVC.h"
|
||||
#import <AuthenticationServices/AuthenticationServices.h>
|
||||
#import "KBLoginVM.h"
|
||||
|
||||
@interface KBLoginVC ()
|
||||
@interface KBLoginVC () <UITextViewDelegate>
|
||||
|
||||
// 背景
|
||||
@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];
|
||||
}
|
||||
return _policyButton;
|
||||
|
||||
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 _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
|
||||
|
||||
Reference in New Issue
Block a user