Files
keyboard/keyBoard/Class/Login/VC/KBEmailLoginVC.m
2025-12-03 15:46:26 +08:00

530 lines
21 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBEmailLoginVC.m
// keyBoard
//
// Created by Mac on 2025/12/3.
//
#import "KBEmailLoginVC.h"
@interface KBEmailLoginVC () <UITextViewDelegate, UITextFieldDelegate>
// 背景与顶部装饰
@property (nonatomic, strong) UIImageView *bgImageView; // 整体背景图login_bg_icon
@property (nonatomic, strong) UIImageView *topRightImageView; // 顶部右侧装饰图login_jianp_icon
@property (nonatomic, strong) UIButton *backButton; // 顶部左侧返回按钮
// 底部白色容器(仅上圆角 26
@property (nonatomic, strong) UIView *contentContainerView;
// 可滚动区域(小屏内容多时允许纵向滚动)
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *scrollContentView;
// 标题
@property (nonatomic, strong) UILabel *titleLabel;
// 输入框
@property (nonatomic, strong) UIView *emailFieldContainer;
@property (nonatomic, strong) UITextField *emailTextField;
@property (nonatomic, strong) UIView *passwordFieldContainer;
@property (nonatomic, strong) UITextField *passwordTextField;
@property (nonatomic, strong) UIButton *passwordToggleButton; // 显示/隐藏密码
// 操作按钮
@property (nonatomic, strong) UIButton *forgotPasswordButton;
@property (nonatomic, strong) UIButton *submitButton; // Login
// 协议 & 底部文案
@property (nonatomic, strong) UITextView *agreementTextView;
@property (nonatomic, strong) UILabel *accountTipLabel; // “Don't Have An Account?”
@property (nonatomic, strong) UIButton *signUpButton; // “Sign Up”
@end
@implementation KBEmailLoginVC
- (void)viewDidLoad {
[super viewDidLoad];
// 与登录/注册页一致:隐藏通用导航栏,使用自定义背景 + 返回按钮
self.kb_enableCustomNavBar = NO;
self.view.backgroundColor = [UIColor whiteColor];
[self setupUI];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// 仅白色容器顶部圆角
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.backButton];
[self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.top.equalTo(self.view).offset(KB_StatusBarHeight() + 8);
make.width.height.mas_equalTo(32);
}];
// 顶部右侧装饰图
[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(10));
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(KBFit(272));
}];
// Scroll 区域
[self.contentContainerView addSubview:self.scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentContainerView);
}];
[self.scrollView addSubview:self.scrollContentView];
[self.scrollContentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
// 标题Login With Email Password
[self.scrollContentView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.scrollContentView).offset(27);
make.centerX.equalTo(self.scrollContentView);
}];
// 邮箱输入
[self.scrollContentView addSubview:self.emailFieldContainer];
[self.emailFieldContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).offset(24);
make.left.equalTo(self.scrollContentView).offset(30);
make.right.equalTo(self.scrollContentView).offset(-30);
make.height.mas_equalTo(52);
}];
[self.emailFieldContainer addSubview:self.emailTextField];
[self.emailTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.emailFieldContainer).insets(UIEdgeInsetsMake(0, 16, 0, 16));
}];
// 密码输入
[self.scrollContentView addSubview:self.passwordFieldContainer];
[self.passwordFieldContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.emailFieldContainer.mas_bottom).offset(16);
make.left.right.equalTo(self.emailFieldContainer);
make.height.equalTo(self.emailFieldContainer);
}];
[self.passwordFieldContainer addSubview:self.passwordToggleButton];
[self.passwordToggleButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.passwordFieldContainer);
make.right.equalTo(self.passwordFieldContainer).offset(-16);
make.width.height.mas_equalTo(24);
}];
[self.passwordFieldContainer addSubview:self.passwordTextField];
[self.passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(self.passwordFieldContainer);
make.left.equalTo(self.passwordFieldContainer).offset(16);
make.right.equalTo(self.passwordToggleButton.mas_left).offset(-8);
}];
// Forgot Password?
[self.scrollContentView addSubview:self.forgotPasswordButton];
[self.forgotPasswordButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.passwordFieldContainer.mas_bottom).offset(12);
make.right.equalTo(self.passwordFieldContainer);
}];
// Login 按钮
[self.scrollContentView addSubview:self.submitButton];
[self.submitButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.forgotPasswordButton.mas_bottom).offset(24);
make.left.right.equalTo(self.emailFieldContainer);
make.height.mas_equalTo(56);
}];
// 协议文案
[self.scrollContentView addSubview:self.agreementTextView];
[self.agreementTextView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.submitButton.mas_bottom).offset(24);
make.left.equalTo(self.scrollContentView).offset(30);
make.right.equalTo(self.scrollContentView).offset(-30);
}];
// 底部账号切换文案
UIView *accountLine = [UIView new];
[self.scrollContentView addSubview:accountLine];
[accountLine mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.agreementTextView.mas_bottom).offset(12);
make.centerX.equalTo(self.scrollContentView);
make.bottom.equalTo(self.scrollContentView).offset(-KB_SAFE_BOTTOM - 16);
}];
[accountLine addSubview:self.accountTipLabel];
[accountLine addSubview:self.signUpButton];
[self.accountTipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.bottom.equalTo(accountLine);
}];
[self.signUpButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.accountTipLabel.mas_right).offset(4);
make.right.equalTo(accountLine);
make.centerY.equalTo(self.accountTipLabel);
}];
}
#pragma mark - Actions
- (void)onTapBack {
if (self.navigationController && self.navigationController.viewControllers.count > 1) {
[self.navigationController popViewControllerAnimated:YES];
} else if (self.presentingViewController) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)onTapSubmit {
KBLOG(@"KBEmailLoginVC onTapSubmit, email=%@, pwdLen=%zd",
self.emailTextField.text,
self.passwordTextField.text.length);
}
- (void)onTapForgotPassword {
KBLOG(@"KBEmailLoginVC onTapForgotPassword");
}
- (void)onTapSignUp {
KBLOG(@"KBEmailLoginVC onTapSignUp");
}
- (void)onTapTogglePassword:(UIButton *)sender {
sender.selected = !sender.selected;
self.passwordTextField.secureTextEntry = !sender.selected;
}
- (void)kb_handleAgreementTap:(UITapGestureRecognizer *)tap {
UITextView *textView = self.agreementTextView;
CGPoint point = [tap locationInView:textView];
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) {
KBLOG(@"KBEmailLoginVC tap policy");
}
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.emailTextField) {
[self.passwordTextField becomeFirstResponder];
} else if (textField == self.passwordTextField) {
[textField resignFirstResponder];
[self onTapSubmit];
}
return YES;
}
#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;
}
- (UIButton *)backButton {
if (!_backButton) {
_backButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *img = [UIImage imageNamed:@"back"];
if (!img) { img = [UIImage imageNamed:@"back_black_icon"]; }
if (img) {
[_backButton setImage:img forState:UIControlStateNormal];
}
_backButton.adjustsImageWhenHighlighted = YES;
_backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[_backButton addTarget:self action:@selector(onTapBack) forControlEvents:UIControlEventTouchUpInside];
}
return _backButton;
}
- (UIView *)contentContainerView {
if (!_contentContainerView) {
_contentContainerView = [UIView new];
_contentContainerView.backgroundColor = [UIColor whiteColor];
}
return _contentContainerView;
}
- (UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [UIScrollView new];
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.alwaysBounceVertical = NO;
_scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
}
return _scrollView;
}
- (UIView *)scrollContentView {
if (!_scrollContentView) {
_scrollContentView = [UIView new];
_scrollContentView.backgroundColor = [UIColor clearColor];
}
return _scrollContentView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.text = KBLocalized(@"Login With Email Password");
_titleLabel.textColor = [UIColor colorWithHex:KBBlackValue];
_titleLabel.font = [KBFont bold:18];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
- (UIView *)emailFieldContainer {
if (!_emailFieldContainer) {
_emailFieldContainer = [UIView new];
_emailFieldContainer.backgroundColor = [UIColor colorWithHex:0xF7F7F7];
_emailFieldContainer.layer.cornerRadius = 10.0;
_emailFieldContainer.layer.masksToBounds = YES;
}
return _emailFieldContainer;
}
- (UITextField *)emailTextField {
if (!_emailTextField) {
_emailTextField = [[UITextField alloc] init];
_emailTextField.delegate = self;
_emailTextField.keyboardType = UIKeyboardTypeEmailAddress;
_emailTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
_emailTextField.autocorrectionType = UITextAutocorrectionTypeNo;
_emailTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
_emailTextField.textColor = [UIColor colorWithHex:KBBlackValue];
_emailTextField.font = [KBFont regular:14];
_emailTextField.returnKeyType = UIReturnKeyNext;
NSString *ph = KBLocalized(@"Please Enter Your Email Address");
_emailTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:ph
attributes:@{
NSForegroundColorAttributeName : [UIColor colorWithWhite:0.75 alpha:1.0],
NSFontAttributeName : [KBFont regular:14]
}];
}
return _emailTextField;
}
- (UIView *)passwordFieldContainer {
if (!_passwordFieldContainer) {
_passwordFieldContainer = [UIView new];
_passwordFieldContainer.backgroundColor = [UIColor colorWithHex:0xF7F7F7];
_passwordFieldContainer.layer.cornerRadius = 10.0;
_passwordFieldContainer.layer.masksToBounds = YES;
}
return _passwordFieldContainer;
}
- (UITextField *)passwordTextField {
if (!_passwordTextField) {
_passwordTextField = [[UITextField alloc] init];
_passwordTextField.delegate = self;
_passwordTextField.secureTextEntry = YES;
_passwordTextField.textColor = [UIColor colorWithHex:KBBlackValue];
_passwordTextField.font = [KBFont regular:14];
_passwordTextField.returnKeyType = UIReturnKeyDone;
NSString *ph = KBLocalized(@"Please Enter The Password");
_passwordTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:ph
attributes:@{
NSForegroundColorAttributeName : [UIColor colorWithWhite:0.75 alpha:1.0],
NSFontAttributeName : [KBFont regular:14]
}];
}
return _passwordTextField;
}
- (UIButton *)passwordToggleButton {
if (!_passwordToggleButton) {
_passwordToggleButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *eye = [UIImage imageNamed:@"login_eye_icon"];
UIImage *eyeSlash = [UIImage imageNamed:@"login_eyeslash_icon"];
[_passwordToggleButton setImage:eye forState:UIControlStateNormal];
[_passwordToggleButton setImage:eyeSlash forState:UIControlStateSelected];
[_passwordToggleButton addTarget:self
action:@selector(onTapTogglePassword:)
forControlEvents:UIControlEventTouchUpInside];
}
return _passwordToggleButton;
}
- (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:10];
[_forgotPasswordButton addTarget:self
action:@selector(onTapForgotPassword)
forControlEvents:UIControlEventTouchUpInside];
}
return _forgotPasswordButton;
}
- (UIButton *)submitButton {
if (!_submitButton) {
_submitButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_submitButton setTitle:KBLocalized(@"Login") forState:UIControlStateNormal];
[_submitButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_submitButton.titleLabel.font = [KBFont medium:16];
_submitButton.backgroundColor = [UIColor colorWithHex:KBColorValue];
_submitButton.layer.cornerRadius = 28.0;
_submitButton.layer.masksToBounds = YES;
[_submitButton addTarget:self
action:@selector(onTapSubmit)
forControlEvents:UIControlEventTouchUpInside];
}
return _submitButton;
}
- (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;
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";
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.alignment = NSTextAlignmentCenter;
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:fullText
attributes:@{
NSFontAttributeName : [KBFont regular:10],
NSForegroundColorAttributeName : [UIColor colorWithHex:0x717171],
NSParagraphStyleAttributeName : paragraph
}];
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 _agreementTextView;
}
- (UILabel *)accountTipLabel {
if (!_accountTipLabel) {
_accountTipLabel = [UILabel new];
_accountTipLabel.text = KBLocalized(@"Don't Have An Account?");
_accountTipLabel.font = [KBFont regular:10];
_accountTipLabel.textColor = [UIColor colorWithHex:KBBlackValue];
}
return _accountTipLabel;
}
- (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:10];
[_signUpButton addTarget:self
action:@selector(onTapSignUp)
forControlEvents:UIControlEventTouchUpInside];
}
return _signUpButton;
}
@end