
主要变更: 1. 新增 ep_splash.png 作为应用启动时的展示图像。 2. 更新 Info.plist 中的应用名称和相关描述,替换为 "E-Parti"。 3. 引入 EPSignatureColorGuideView 和 EPEmotionColorStorage,支持用户选择和保存专属情绪颜色。 4. 在 AppDelegate 中集成情绪颜色引导逻辑,确保用户首次登录时能够选择专属颜色。 此更新旨在提升用户体验,增强应用的品牌识别度,并提供个性化的情绪表达功能。
300 lines
11 KiB
Swift
300 lines
11 KiB
Swift
//
|
||
// EPLoginViewController.swift
|
||
// YuMi
|
||
//
|
||
// Created by AI on 2025-01-27.
|
||
//
|
||
|
||
import UIKit
|
||
|
||
@objc class EPLoginViewController: UIViewController {
|
||
|
||
// MARK: - Properties
|
||
|
||
private let backgroundImageView = UIImageView()
|
||
private let logoImageView = UIImageView()
|
||
private let epartiTitleLabel = UILabel()
|
||
|
||
private let idLoginButton = EPLoginButton()
|
||
private let emailLoginButton = EPLoginButton()
|
||
|
||
private let agreeCheckbox = UIButton(type: .custom)
|
||
private let policyLabel = EPPolicyLabel()
|
||
|
||
private let feedbackButton = UIButton(type: .custom)
|
||
|
||
#if DEBUG
|
||
private let debugButton = UIButton(type: .custom)
|
||
#endif
|
||
|
||
private let policySelectedKey = EPLoginConfig.Keys.policyAgreed
|
||
|
||
// MARK: - Lifecycle
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||
setupUI()
|
||
loadPolicyStatus()
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
|
||
}
|
||
|
||
override func viewWillDisappear(_ animated: Bool) {
|
||
super.viewWillDisappear(animated)
|
||
}
|
||
|
||
// MARK: - Setup
|
||
|
||
private func setupUI() {
|
||
setupBackground()
|
||
setupLogo()
|
||
setupLoginButtons()
|
||
setupPolicyArea()
|
||
setupNavigationBar()
|
||
}
|
||
|
||
private func setupBackground() {
|
||
view.addSubview(backgroundImageView)
|
||
backgroundImageView.image = kImage(EPLoginConfig.Images.background)
|
||
backgroundImageView.contentMode = .scaleAspectFill
|
||
|
||
backgroundImageView.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
}
|
||
}
|
||
|
||
private func setupLogo() {
|
||
view.addSubview(logoImageView)
|
||
logoImageView.image = kImage(EPLoginConfig.Images.loginBg)
|
||
|
||
logoImageView.snp.makeConstraints { make in
|
||
make.top.leading.trailing.equalTo(view)
|
||
make.height.equalTo(EPLoginConfig.Layout.logoHeight)
|
||
}
|
||
|
||
// E-PARTI 标题
|
||
view.addSubview(epartiTitleLabel)
|
||
epartiTitleLabel.text = "E-PARTI"
|
||
epartiTitleLabel.font = .systemFont(ofSize: EPLoginConfig.Layout.epartiTitleFontSize, weight: .bold)
|
||
epartiTitleLabel.textColor = EPLoginConfig.Colors.textLight
|
||
epartiTitleLabel.transform = CGAffineTransform(a: 1, b: 0, c: -0.2, d: 1, tx: 0, ty: 0) // 斜体效果
|
||
|
||
epartiTitleLabel.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(EPLoginConfig.Layout.epartiTitleLeading)
|
||
make.bottom.equalTo(logoImageView.snp.bottom).offset(EPLoginConfig.Layout.epartiTitleBottomOffset)
|
||
}
|
||
}
|
||
|
||
private func setupLoginButtons() {
|
||
// 配置按钮
|
||
idLoginButton.configure(
|
||
icon: EPLoginConfig.Images.iconLoginId,
|
||
title: YMLocalizedString(EPLoginConfig.LocalizedKeys.idLogin)
|
||
)
|
||
idLoginButton.delegate = self
|
||
|
||
emailLoginButton.configure(
|
||
icon: EPLoginConfig.Images.iconLoginEmail,
|
||
title: YMLocalizedString(EPLoginConfig.LocalizedKeys.emailLogin)
|
||
)
|
||
emailLoginButton.delegate = self
|
||
|
||
// StackView 布局
|
||
let stackView = UIStackView(arrangedSubviews: [idLoginButton, emailLoginButton])
|
||
stackView.axis = .vertical
|
||
stackView.spacing = EPLoginConfig.Layout.loginButtonSpacing
|
||
stackView.distribution = .fillEqually
|
||
view.addSubview(stackView)
|
||
|
||
stackView.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(EPLoginConfig.Layout.loginButtonHorizontalPadding)
|
||
make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.loginButtonHorizontalPadding)
|
||
make.top.equalTo(logoImageView.snp.bottom)
|
||
}
|
||
|
||
idLoginButton.snp.makeConstraints { make in
|
||
make.height.equalTo(EPLoginConfig.Layout.loginButtonHeight)
|
||
}
|
||
|
||
emailLoginButton.snp.makeConstraints { make in
|
||
make.height.equalTo(EPLoginConfig.Layout.loginButtonHeight)
|
||
}
|
||
}
|
||
|
||
private func setupPolicyArea() {
|
||
view.addSubview(agreeCheckbox)
|
||
view.addSubview(policyLabel)
|
||
|
||
agreeCheckbox.setImage(kImage("login_privace_select"), for: .selected)
|
||
agreeCheckbox.setImage(kImage("login_privace_unselect"), for: .normal)
|
||
agreeCheckbox.addTarget(self, action: #selector(togglePolicyCheckbox), for: .touchUpInside)
|
||
|
||
policyLabel.onUserAgreementTapped = { [weak self] in
|
||
print("[EPLogin] User agreement tapped callback triggered")
|
||
let url = self?.getUserAgreementURL() ?? ""
|
||
print("[EPLogin] User agreement URL: \(url)")
|
||
self?.openPolicyInExternalBrowser(url)
|
||
}
|
||
policyLabel.onPrivacyPolicyTapped = { [weak self] in
|
||
print("[EPLogin] Privacy policy tapped callback triggered")
|
||
let url = self?.getPrivacyPolicyURL() ?? ""
|
||
print("[EPLogin] Privacy policy URL: \(url)")
|
||
self?.openPolicyInExternalBrowser(url)
|
||
}
|
||
|
||
agreeCheckbox.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(EPLoginConfig.Layout.horizontalPadding)
|
||
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30)
|
||
make.size.equalTo(EPLoginConfig.Layout.checkboxSize)
|
||
}
|
||
|
||
policyLabel.snp.makeConstraints { make in
|
||
make.leading.equalTo(agreeCheckbox.snp.trailing).offset(8)
|
||
make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.horizontalPadding)
|
||
make.centerY.equalTo(agreeCheckbox)
|
||
}
|
||
}
|
||
|
||
private func setupNavigationBar() {
|
||
view.addSubview(feedbackButton)
|
||
feedbackButton.setTitle(YMLocalizedString(EPLoginConfig.LocalizedKeys.feedback), for: .normal)
|
||
feedbackButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.smallFontSize)
|
||
feedbackButton.backgroundColor = EPLoginConfig.Colors.backgroundTransparent
|
||
feedbackButton.layer.cornerRadius = EPLoginConfig.Layout.feedbackButtonCornerRadius
|
||
feedbackButton.addTarget(self, action: #selector(handleFeedback), for: .touchUpInside)
|
||
|
||
feedbackButton.snp.makeConstraints { make in
|
||
make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.compactHorizontalPadding)
|
||
make.top.equalTo(view.safeAreaLayoutGuide).offset(8)
|
||
make.height.equalTo(EPLoginConfig.Layout.feedbackButtonHeight)
|
||
}
|
||
|
||
#if DEBUG
|
||
view.addSubview(debugButton)
|
||
debugButton.setTitle("切换环境", for: .normal)
|
||
debugButton.setTitleColor(.blue, for: .normal)
|
||
debugButton.addTarget(self, action: #selector(handleDebug), for: .touchUpInside)
|
||
|
||
debugButton.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(EPLoginConfig.Layout.compactHorizontalPadding)
|
||
make.top.equalTo(view.safeAreaLayoutGuide).offset(8)
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// MARK: - Actions
|
||
|
||
private func handleIDLogin() {
|
||
let vc = EPLoginTypesViewController()
|
||
vc.displayType = .id
|
||
navigationController?.pushViewController(vc, animated: true)
|
||
}
|
||
|
||
private func handleEmailLogin() {
|
||
let vc = EPLoginTypesViewController()
|
||
vc.displayType = .email
|
||
navigationController?.pushViewController(vc, animated: true)
|
||
}
|
||
|
||
@objc private func togglePolicyCheckbox() {
|
||
agreeCheckbox.isSelected.toggle()
|
||
UserDefaults.standard.set(agreeCheckbox.isSelected, forKey: policySelectedKey)
|
||
}
|
||
|
||
@objc private func handleFeedback() {
|
||
print("[EPLogin] Feedback - 占位,Phase 2 实现")
|
||
}
|
||
|
||
#if DEBUG
|
||
@objc private func handleDebug() {
|
||
print("[EPLogin] Debug - 占位,Phase 2 实现")
|
||
}
|
||
#endif
|
||
|
||
private func openPolicyInExternalBrowser(_ urlString: String) {
|
||
print("[EPLogin] Original URL: \(urlString)")
|
||
|
||
// 如果不是完整 URL,拼接域名(参考 XPWebViewController.m 第 697-698 行)
|
||
var fullUrl = urlString
|
||
if !urlString.hasPrefix("http") && !urlString.hasPrefix("https") {
|
||
let hostUrl = HttpRequestHelper.getHostUrl()
|
||
fullUrl = "\(hostUrl)/\(urlString)"
|
||
print("[EPLogin] Added host URL, full URL: \(fullUrl)")
|
||
}
|
||
|
||
print("[EPLogin] Opening URL in external browser: \(fullUrl)")
|
||
|
||
guard let url = URL(string: fullUrl) else {
|
||
print("[EPLogin] ❌ Invalid URL: \(fullUrl)")
|
||
return
|
||
}
|
||
|
||
print("[EPLogin] URL object created: \(url)")
|
||
|
||
// 在外部浏览器中打开
|
||
if UIApplication.shared.canOpenURL(url) {
|
||
print("[EPLogin] ✅ Can open URL, attempting to open...")
|
||
UIApplication.shared.open(url, options: [:]) { success in
|
||
print("[EPLogin] Open external browser: \(success ? "✅ Success" : "❌ Failed")")
|
||
}
|
||
} else {
|
||
print("[EPLogin] ❌ Cannot open URL: \(fullUrl)")
|
||
}
|
||
}
|
||
|
||
// MARK: - Helpers
|
||
|
||
private func loadPolicyStatus() {
|
||
agreeCheckbox.isSelected = UserDefaults.standard.bool(forKey: policySelectedKey)
|
||
// 默认勾选
|
||
if !UserDefaults.standard.bool(forKey: EPLoginConfig.Keys.hasLaunchedBefore) {
|
||
agreeCheckbox.isSelected = true
|
||
UserDefaults.standard.set(true, forKey: policySelectedKey)
|
||
UserDefaults.standard.set(true, forKey: EPLoginConfig.Keys.hasLaunchedBefore)
|
||
}
|
||
}
|
||
|
||
/// 获取用户协议 URL
|
||
private func getUserAgreementURL() -> String {
|
||
// kUserProtocalURL 对应枚举值 4
|
||
let url = URLWithType(URLType(rawValue: 4)!) as String
|
||
print("[EPLogin] User agreement URL from URLWithType: \(url)")
|
||
return url
|
||
}
|
||
|
||
/// 获取隐私政策 URL
|
||
private func getPrivacyPolicyURL() -> String {
|
||
// kPrivacyURL 对应枚举值 0
|
||
let url = URLWithType(URLType(rawValue: 0)!) as String
|
||
print("[EPLogin] Privacy policy URL from URLWithType: \(url)")
|
||
return url
|
||
}
|
||
|
||
private func checkPolicyAgreed() -> Bool {
|
||
if !agreeCheckbox.isSelected {
|
||
// Phase 2: 显示提示
|
||
print("[EPLogin] Please agree to policy first")
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
}
|
||
|
||
// MARK: - EPLoginButtonDelegate
|
||
|
||
extension EPLoginViewController: EPLoginButtonDelegate {
|
||
func loginButtonDidTap(_ button: EPLoginButton) {
|
||
guard checkPolicyAgreed() else { return }
|
||
|
||
if button == idLoginButton {
|
||
handleIDLogin()
|
||
} else if button == emailLoginButton {
|
||
handleEmailLogin()
|
||
}
|
||
}
|
||
}
|