3 Commits

Author SHA1 Message Date
edwinQQQ
de8627a230 feat: 更新 EPLoginTypesViewController 和 EPLoginInputView 以增强布局和用户体验
主要变更:
1. 在 EPLoginTypesViewController 中添加了对多个 UI 组件的约束设置,确保布局更加灵活。
2. 更新了标题标签的文本内容,使用本地化字符串替代硬编码文本,提升国际化支持。
3. 在 EPLoginInputView 中为多个组件添加了自动布局支持,确保在不同屏幕尺寸下的适配性。

此更新旨在提升用户界面的可用性和美观性,确保更好的用户体验。
2025-10-14 17:46:37 +08:00
edwinQQQ
9466b65b40 refactor: 更新 EPLoginTypesViewController 以简化表单验证和错误处理
主要变更:
1. 将 EPLoginTypesViewController 继承自 BaseViewController,提升代码结构。
2. 简化表单验证逻辑,仅检查输入是否为空,减少对 EPLoginValidator 的依赖。
3. 更新错误处理方式,使用 showErrorToast 替代 showAlert,提升用户体验。
4. 在 EPLoginService 中直接使用字符串常量替代 grantType 变量,简化代码。

此更新旨在提升代码可读性和用户交互体验,确保登录流程更加流畅。
2025-10-14 16:47:47 +08:00
edwinQQQ
955cc3622f feat: 更新 EPEditSettingViewController 以增强用户信息管理功能
主要变更:
1. 在 EPEditSettingViewController 中添加了用户头像和相机图标的布局,提升用户界面友好性。
2. 引入 EPMineAPIHelper 以支持头像更新功能,简化 API 调用。
3. 优化了导航栏的显示和隐藏逻辑,确保用户体验流畅。
4. 更新了 UITableView 的数据源和布局,确保信息展示清晰。

此更新旨在提升用户体验,简化用户信息的管理和更新流程。
2025-10-14 14:46:08 +08:00
18 changed files with 545 additions and 368 deletions

View File

@@ -425,7 +425,6 @@
4C1E98C62E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */; }; 4C1E98C62E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */; };
4C1E98C92E9A4DFD0031AE79 /* EPQCloudConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.swift */; }; 4C1E98C92E9A4DFD0031AE79 /* EPQCloudConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.swift */; };
4C1E98CA2E9A4DFD0031AE79 /* EPSDKManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C82E9A4DFD0031AE79 /* EPSDKManager.swift */; }; 4C1E98CA2E9A4DFD0031AE79 /* EPSDKManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C82E9A4DFD0031AE79 /* EPSDKManager.swift */; };
4C1E98CD2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98CC2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m */; };
4C3475C42DD1FE590099B984 /* CreateEventSelectRoomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3475C32DD1FE590099B984 /* CreateEventSelectRoomViewController.m */; }; 4C3475C42DD1FE590099B984 /* CreateEventSelectRoomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3475C32DD1FE590099B984 /* CreateEventSelectRoomViewController.m */; };
4C3851992DD5F4D50089CFCC /* EventConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3851982DD5F4D50089CFCC /* EventConfigModel.m */; }; 4C3851992DD5F4D50089CFCC /* EventConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3851982DD5F4D50089CFCC /* EventConfigModel.m */; };
4C38C2AD2D84064400CFA4A8 /* LoginInputItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C38C2AC2D84064300CFA4A8 /* LoginInputItemView.m */; }; 4C38C2AD2D84064400CFA4A8 /* LoginInputItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C38C2AC2D84064300CFA4A8 /* LoginInputItemView.m */; };
@@ -2485,8 +2484,6 @@
4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPMomentAPISwiftHelper.swift; sourceTree = "<group>"; }; 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPMomentAPISwiftHelper.swift; sourceTree = "<group>"; };
4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPQCloudConfig.swift; sourceTree = "<group>"; }; 4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPQCloudConfig.swift; sourceTree = "<group>"; };
4C1E98C82E9A4DFD0031AE79 /* EPSDKManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPSDKManager.swift; sourceTree = "<group>"; }; 4C1E98C82E9A4DFD0031AE79 /* EPSDKManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPSDKManager.swift; sourceTree = "<group>"; };
4C1E98CB2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMomentAPIHelper_Deprecated.h; sourceTree = "<group>"; };
4C1E98CC2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMomentAPIHelper_Deprecated.m; sourceTree = "<group>"; };
4C3475C22DD1FE590099B984 /* CreateEventSelectRoomViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CreateEventSelectRoomViewController.h; sourceTree = "<group>"; }; 4C3475C22DD1FE590099B984 /* CreateEventSelectRoomViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CreateEventSelectRoomViewController.h; sourceTree = "<group>"; };
4C3475C32DD1FE590099B984 /* CreateEventSelectRoomViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CreateEventSelectRoomViewController.m; sourceTree = "<group>"; }; 4C3475C32DD1FE590099B984 /* CreateEventSelectRoomViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CreateEventSelectRoomViewController.m; sourceTree = "<group>"; };
4C3851972DD5F4D50089CFCC /* EventConfigModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EventConfigModel.h; sourceTree = "<group>"; }; 4C3851972DD5F4D50089CFCC /* EventConfigModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EventConfigModel.h; sourceTree = "<group>"; };
@@ -6459,8 +6456,6 @@
4C0642952E98F76F00BAF413 /* Services */ = { 4C0642952E98F76F00BAF413 /* Services */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4C1E98CB2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.h */,
4C1E98CC2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m */,
4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */, 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */,
); );
path = Services; path = Services;
@@ -12766,7 +12761,6 @@
E8751E5F28A62A970056EF44 /* XPSailingPresenter.m in Sources */, E8751E5F28A62A970056EF44 /* XPSailingPresenter.m in Sources */,
E84A2E962A5280F900D6AF8A /* XPExchangeDiamondsView.m in Sources */, E84A2E962A5280F900D6AF8A /* XPExchangeDiamondsView.m in Sources */,
23F9636A2BB6919D00F440A6 /* PINobleRebateModel.m in Sources */, 23F9636A2BB6919D00F440A6 /* PINobleRebateModel.m in Sources */,
4C1E98CD2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m in Sources */,
E8DAC5AC2858305A00012CFD /* XPRoomMessageBubbleView.m in Sources */, E8DAC5AC2858305A00012CFD /* XPRoomMessageBubbleView.m in Sources */,
1427218929A75F6F00C7C423 /* HTTPDataResponse.m in Sources */, 1427218929A75F6F00C7C423 /* HTTPDataResponse.m in Sources */,
E8B9843028AB90200022D026 /* XPMoentsTopicListView.m in Sources */, E8B9843028AB90200022D026 /* XPMoentsTopicListView.m in Sources */,
@@ -13789,6 +13783,7 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "YuMi/YuMi-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "YuMi/YuMi-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
}; };

View File

@@ -56,6 +56,11 @@
value = "disable" value = "disable"
isEnabled = "NO"> isEnabled = "NO">
</EnvironmentVariable> </EnvironmentVariable>
<EnvironmentVariable
key = "SWIFT_DISABLE_SAFETY_CHECKS"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables> </EnvironmentVariables>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction

View File

@@ -7,14 +7,13 @@
import UIKit import UIKit
class EPLoginTypesViewController: UIViewController { class EPLoginTypesViewController: BaseViewController {
// MARK: - Properties // MARK: - Properties
var displayType: EPLoginDisplayType = .id var displayType: EPLoginDisplayType = .id
private let loginService = EPLoginService() private let loginService = EPLoginService()
private let validator = EPLoginValidator()
private let backgroundImageView = UIImageView() private let backgroundImageView = UIImageView()
private let titleLabel = UILabel() private let titleLabel = UILabel()
@@ -72,6 +71,7 @@ class EPLoginTypesViewController: UIViewController {
private func setupBackground() { private func setupBackground() {
view.addSubview(backgroundImageView) view.addSubview(backgroundImageView)
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
backgroundImageView.image = kImage(EPLoginConfig.Images.background) backgroundImageView.image = kImage(EPLoginConfig.Images.background)
backgroundImageView.contentMode = .scaleAspectFill backgroundImageView.contentMode = .scaleAspectFill
@@ -82,6 +82,7 @@ class EPLoginTypesViewController: UIViewController {
private func setupNavigationBar() { private func setupNavigationBar() {
view.addSubview(backButton) view.addSubview(backButton)
backButton.translatesAutoresizingMaskIntoConstraints = false
backButton.setImage(UIImage(systemName: EPLoginConfig.Images.iconBack), for: .normal) backButton.setImage(UIImage(systemName: EPLoginConfig.Images.iconBack), for: .normal)
backButton.tintColor = EPLoginConfig.Colors.textLight backButton.tintColor = EPLoginConfig.Colors.textLight
backButton.addTarget(self, action: #selector(handleBack), for: .touchUpInside) backButton.addTarget(self, action: #selector(handleBack), for: .touchUpInside)
@@ -95,16 +96,20 @@ class EPLoginTypesViewController: UIViewController {
private func setupTitle() { private func setupTitle() {
view.addSubview(titleLabel) view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.font = .systemFont(ofSize: EPLoginConfig.Layout.titleFontSize, weight: .bold) titleLabel.font = .systemFont(ofSize: EPLoginConfig.Layout.titleFontSize, weight: .bold)
titleLabel.textColor = EPLoginConfig.Colors.textLight titleLabel.textColor = EPLoginConfig.Colors.textLight
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalTo(view.safeAreaLayoutGuide).offset(100) make.centerY.equalTo(backButton) //
} }
} }
private func setupInputViews() { private func setupInputViews() {
firstInputView.translatesAutoresizingMaskIntoConstraints = false
secondInputView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(firstInputView) view.addSubview(firstInputView)
view.addSubview(secondInputView) view.addSubview(secondInputView)
@@ -124,6 +129,7 @@ class EPLoginTypesViewController: UIViewController {
private func setupActionButton() { private func setupActionButton() {
view.addSubview(actionButton) view.addSubview(actionButton)
actionButton.translatesAutoresizingMaskIntoConstraints = false
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
actionButton.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal) actionButton.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal)
actionButton.layer.cornerRadius = EPLoginConfig.Layout.uniformCornerRadius actionButton.layer.cornerRadius = EPLoginConfig.Layout.uniformCornerRadius
@@ -233,7 +239,7 @@ class EPLoginTypesViewController: UIViewController {
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
case .emailReset: case .emailReset:
titleLabel.text = "Recover Password" titleLabel.text = YMLocalizedString("20.20.51_text_20")
firstInputView.configure(with: EPLoginInputConfig( firstInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: false, showCodeButton: false,
@@ -264,7 +270,7 @@ class EPLoginTypesViewController: UIViewController {
actionButton.setTitle("Confirm", for: .normal) actionButton.setTitle("Confirm", for: .normal)
case .phoneReset: case .phoneReset:
titleLabel.text = "Recover Password" titleLabel.text = YMLocalizedString("20.20.51_text_20")
firstInputView.configure(with: EPLoginInputConfig( firstInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: false, showCodeButton: false,
@@ -298,6 +304,7 @@ class EPLoginTypesViewController: UIViewController {
private func setupForgotPasswordButton() { private func setupForgotPasswordButton() {
let button = UIButton(type: .system) let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Forgot Password?", for: .normal) button.setTitle("Forgot Password?", for: .normal)
button.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal) button.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.smallFontSize) button.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.smallFontSize)
@@ -315,6 +322,7 @@ class EPLoginTypesViewController: UIViewController {
private func setupThirdInputView() { private func setupThirdInputView() {
let inputView = EPLoginInputView() let inputView = EPLoginInputView()
inputView.translatesAutoresizingMaskIntoConstraints = false
inputView.configure(with: EPLoginInputConfig( inputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: false, showCodeButton: false,
@@ -380,14 +388,14 @@ class EPLoginTypesViewController: UIViewController {
let id = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) let id = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
let password = secondInputView.text let password = secondInputView.text
// //
guard !id.isEmpty else { guard !id.isEmpty else {
showAlert("请输入用户ID") showErrorToast(YMLocalizedString("LoginPresenter0"))
return return
} }
guard !password.isEmpty else { guard !password.isEmpty else {
showAlert("请输入密码") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
@@ -397,13 +405,14 @@ class EPLoginTypesViewController: UIViewController {
loginService.loginWithID(id: id, password: password) { [weak self] (accountModel: AccountModel) in loginService.loginWithID(id: id, password: password) { [weak self] (accountModel: AccountModel) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
print("[EPLogin] ID登录成功: \(accountModel.uid ?? "")") print("[EPLogin] ID登录成功: \(accountModel.uid)")
self?.showSuccessToast(YMLocalizedString("XPLoginPhoneViewController1"))
EPLoginManager.jumpToHome(from: self!) EPLoginManager.jumpToHome(from: self!)
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("登录失败: \(msg)") self?.showErrorToast(msg)
} }
} }
} }
@@ -412,14 +421,14 @@ class EPLoginTypesViewController: UIViewController {
let email = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) let email = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
let code = secondInputView.text let code = secondInputView.text
// //
guard validator.validateEmail(email) else { guard !email.isEmpty else {
showAlert("请输入正确的邮箱地址") showErrorToast(YMLocalizedString("LoginPresenter0"))
return return
} }
guard validator.validateCode(code) else { guard !code.isEmpty else {
showAlert("请输入6位数字验证码") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
@@ -428,13 +437,14 @@ class EPLoginTypesViewController: UIViewController {
loginService.loginWithEmail(email: email, code: code) { [weak self] (accountModel: AccountModel) in loginService.loginWithEmail(email: email, code: code) { [weak self] (accountModel: AccountModel) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
print("[EPLogin] 邮箱登录成功: \(accountModel.uid ?? "")") print("[EPLogin] 邮箱登录成功: \(accountModel.uid)")
self?.showSuccessToast(YMLocalizedString("XPLoginPhoneViewController1"))
EPLoginManager.jumpToHome(from: self!) EPLoginManager.jumpToHome(from: self!)
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("登录失败: \(msg)") self?.showErrorToast(msg)
} }
} }
} }
@@ -443,14 +453,14 @@ class EPLoginTypesViewController: UIViewController {
let phone = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) let phone = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
let code = secondInputView.text let code = secondInputView.text
// //
guard validator.validatePhone(phone) else { guard !phone.isEmpty else {
showAlert("请输入正确的手机号") showErrorToast(YMLocalizedString("XPLoginPhoneViewController0"))
return return
} }
guard validator.validateCode(code) else { guard !code.isEmpty else {
showAlert("请输入6位数字验证码") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
@@ -459,13 +469,14 @@ class EPLoginTypesViewController: UIViewController {
loginService.loginWithPhone(phone: phone, code: code, areaCode: "+86") { [weak self] (accountModel: AccountModel) in loginService.loginWithPhone(phone: phone, code: code, areaCode: "+86") { [weak self] (accountModel: AccountModel) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
print("[EPLogin] 手机登录成功: \(accountModel.uid ?? "")") print("[EPLogin] 手机登录成功: \(accountModel.uid)")
self?.showSuccessToast(YMLocalizedString("XPLoginPhoneViewController1"))
EPLoginManager.jumpToHome(from: self!) EPLoginManager.jumpToHome(from: self!)
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("登录失败: \(msg)") self?.showErrorToast(msg)
} }
} }
} }
@@ -477,19 +488,19 @@ class EPLoginTypesViewController: UIViewController {
let code = secondInputView.text let code = secondInputView.text
let newPassword = thirdInput.text let newPassword = thirdInput.text
// //
guard validator.validateEmail(email) else { guard !email.isEmpty else {
showAlert("请输入正确的邮箱地址") showErrorToast(YMLocalizedString("LoginPresenter0"))
return return
} }
guard validator.validateCode(code) else { guard !code.isEmpty else {
showAlert("请输入6位数字验证码") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
guard validator.validatePassword(newPassword) else { guard !newPassword.isEmpty else {
showAlert("密码需6-16位包含字母和数字") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
@@ -498,14 +509,13 @@ class EPLoginTypesViewController: UIViewController {
loginService.resetEmailPassword(email: email, code: code, newPassword: newPassword) { [weak self] in loginService.resetEmailPassword(email: email, code: code, newPassword: newPassword) { [weak self] in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("密码重置成功", completion: { self?.showSuccessToast(YMLocalizedString("XPForgetPwdViewController1"))
self?.navigationController?.popViewController(animated: true) self?.navigationController?.popViewController(animated: true)
})
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("重置失败: \(msg)") self?.showErrorToast(msg)
} }
} }
} }
@@ -517,19 +527,19 @@ class EPLoginTypesViewController: UIViewController {
let code = secondInputView.text let code = secondInputView.text
let newPassword = thirdInput.text let newPassword = thirdInput.text
// //
guard validator.validatePhone(phone) else { guard !phone.isEmpty else {
showAlert("请输入正确的手机号") showErrorToast(YMLocalizedString("XPLoginPhoneViewController0"))
return return
} }
guard validator.validateCode(code) else { guard !code.isEmpty else {
showAlert("请输入6位数字验证码") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
guard validator.validatePassword(newPassword) else { guard !newPassword.isEmpty else {
showAlert("密码需6-16位包含字母和数字") showErrorToast(YMLocalizedString("LoginPresenter1"))
return return
} }
@@ -538,14 +548,13 @@ class EPLoginTypesViewController: UIViewController {
loginService.resetPhonePassword(phone: phone, code: code, areaCode: "+86", newPassword: newPassword) { [weak self] in loginService.resetPhonePassword(phone: phone, code: code, areaCode: "+86", newPassword: newPassword) { [weak self] in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("密码重置成功", completion: { self?.showSuccessToast(YMLocalizedString("XPForgetPwdViewController1"))
self?.navigationController?.popViewController(animated: true) self?.navigationController?.popViewController(animated: true)
})
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showLoading(false) self?.showLoading(false)
self?.showAlert("重置失败: \(msg)") self?.showErrorToast(msg)
} }
} }
} }
@@ -555,8 +564,9 @@ class EPLoginTypesViewController: UIViewController {
private func sendEmailCode() { private func sendEmailCode() {
let email = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) let email = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
guard validator.validateEmail(email) else { //
showAlert("请输入正确的邮箱地址") guard !email.isEmpty else {
secondInputView.stopCountdown()
return return
} }
@@ -565,11 +575,13 @@ class EPLoginTypesViewController: UIViewController {
loginService.sendEmailCode(email: email, type: type) { [weak self] in loginService.sendEmailCode(email: email, type: type) { [weak self] in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.secondInputView.startCountdown() self?.secondInputView.startCountdown()
self?.showAlert("验证码已发送") self?.secondInputView.displayKeyboard()
self?.showSuccessToast(YMLocalizedString("XPLoginPhoneViewController2"))
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showAlert("发送失败: \(msg)") self?.secondInputView.stopCountdown()
self?.showErrorToast(msg)
} }
} }
} }
@@ -577,8 +589,10 @@ class EPLoginTypesViewController: UIViewController {
private func sendPhoneCode() { private func sendPhoneCode() {
let phone = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) let phone = firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
guard validator.validatePhone(phone) else { //
showAlert("请输入正确的手机号") guard !phone.isEmpty else {
showErrorToast(YMLocalizedString("XPLoginPhoneViewController0"))
secondInputView.stopCountdown()
return return
} }
@@ -591,11 +605,13 @@ class EPLoginTypesViewController: UIViewController {
self.loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in self.loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.secondInputView.startCountdown() self?.secondInputView.startCountdown()
self?.showAlert("验证码已发送") self?.secondInputView.displayKeyboard()
self?.showSuccessToast(YMLocalizedString("XPLoginPhoneViewController2"))
} }
} failure: { [weak self] (code: Int, msg: String) in } failure: { [weak self] (code: Int, msg: String) in
DispatchQueue.main.async { DispatchQueue.main.async {
self?.showAlert("发送失败: \(msg)") self?.secondInputView.stopCountdown()
self?.showErrorToast(msg)
} }
} }
} }
@@ -653,14 +669,6 @@ class EPLoginTypesViewController: UIViewController {
actionButton.alpha = isEnabled ? 1.0 : 0.5 actionButton.alpha = isEnabled ? 1.0 : 0.5
} }
private func showAlert(_ message: String, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in
completion?()
})
present(alert, animated: true)
}
/// Captcha WebView /// Captcha WebView
/// - Parameter completion: /// - Parameter completion:
private func loadCaptchaWebView(completion: @escaping () -> Void) { private func loadCaptchaWebView(completion: @escaping () -> Void) {

View File

@@ -15,7 +15,6 @@ import Foundation
private let clientSecret = EPLoginConfig.API.clientSecret private let clientSecret = EPLoginConfig.API.clientSecret
private let clientId = EPLoginConfig.API.clientId private let clientId = EPLoginConfig.API.clientId
private let grantType = EPLoginConfig.API.grantType
private let version = EPLoginConfig.API.version private let version = EPLoginConfig.API.version
// MARK: - Private Helper Methods // MARK: - Private Helper Methods
@@ -151,7 +150,7 @@ import Foundation
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
client_id: clientId, client_id: clientId,
grant_type: grantType) grant_type: "password")
} }
/// + /// +
@@ -182,7 +181,7 @@ import Foundation
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
client_id: clientId, client_id: clientId,
grant_type: grantType) grant_type: "email")
} }
/// + /// +
@@ -215,7 +214,7 @@ import Foundation
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
client_id: clientId, client_id: clientId,
grant_type: grantType, grant_type: "password",
phoneAreaCode: areaCode) phoneAreaCode: areaCode)
} }

View File

@@ -94,6 +94,7 @@ class EPLoginInputView: UIView {
stackView.alignment = .center stackView.alignment = .center
stackView.distribution = .fill stackView.distribution = .fill
stackView.spacing = 8 stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView) addSubview(stackView)
setupAreaCodeView() setupAreaCodeView()
@@ -120,19 +121,23 @@ class EPLoginInputView: UIView {
areaStackView.alignment = .center areaStackView.alignment = .center
areaStackView.distribution = .fill areaStackView.distribution = .fill
areaStackView.spacing = 8 areaStackView.spacing = 8
areaStackView.translatesAutoresizingMaskIntoConstraints = false
// //
areaCodeButton.setTitle("+86", for: .normal) areaCodeButton.setTitle("+86", for: .normal)
areaCodeButton.setTitleColor(EPLoginConfig.Colors.inputText, for: .normal) areaCodeButton.setTitleColor(EPLoginConfig.Colors.inputText, for: .normal)
areaCodeButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) areaCodeButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
areaCodeButton.isUserInteractionEnabled = false areaCodeButton.isUserInteractionEnabled = false
areaCodeButton.translatesAutoresizingMaskIntoConstraints = false
// //
areaArrowImageView.image = kImage("login_area_arrow") areaArrowImageView.image = kImage("login_area_arrow")
areaArrowImageView.contentMode = .scaleAspectFit areaArrowImageView.contentMode = .scaleAspectFit
areaArrowImageView.isUserInteractionEnabled = false areaArrowImageView.isUserInteractionEnabled = false
areaArrowImageView.translatesAutoresizingMaskIntoConstraints = false
// //
areaTapButton.translatesAutoresizingMaskIntoConstraints = false
areaTapButton.addTarget(self, action: #selector(handleAreaTap), for: .touchUpInside) areaTapButton.addTarget(self, action: #selector(handleAreaTap), for: .touchUpInside)
areaStackView.addSubview(areaTapButton) areaStackView.addSubview(areaTapButton)
@@ -147,7 +152,6 @@ class EPLoginInputView: UIView {
areaCodeButton.snp.makeConstraints { make in areaCodeButton.snp.makeConstraints { make in
make.width.lessThanOrEqualTo(60) make.width.lessThanOrEqualTo(60)
make.height.equalTo(stackView)
} }
areaArrowImageView.snp.makeConstraints { make in areaArrowImageView.snp.makeConstraints { make in
@@ -160,6 +164,7 @@ class EPLoginInputView: UIView {
// Icon () // Icon ()
iconImageView.contentMode = .scaleAspectFit iconImageView.contentMode = .scaleAspectFit
iconImageView.tintColor = EPLoginConfig.Colors.icon iconImageView.tintColor = EPLoginConfig.Colors.icon
iconImageView.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(iconImageView) stackView.addArrangedSubview(iconImageView)
iconImageView.snp.makeConstraints { make in iconImageView.snp.makeConstraints { make in
@@ -170,12 +175,9 @@ class EPLoginInputView: UIView {
inputTextField.textColor = EPLoginConfig.Colors.textLight inputTextField.textColor = EPLoginConfig.Colors.textLight
inputTextField.font = .systemFont(ofSize: 14) inputTextField.font = .systemFont(ofSize: 14)
inputTextField.tintColor = EPLoginConfig.Colors.textLight inputTextField.tintColor = EPLoginConfig.Colors.textLight
inputTextField.translatesAutoresizingMaskIntoConstraints = false
inputTextField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) inputTextField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
stackView.addArrangedSubview(inputTextField) stackView.addArrangedSubview(inputTextField)
inputTextField.snp.makeConstraints { make in
make.height.equalTo(stackView)
}
} }
@objc private func textFieldDidChange() { @objc private func textFieldDidChange() {
@@ -183,6 +185,7 @@ class EPLoginInputView: UIView {
} }
private func setupEyeButton() { private func setupEyeButton() {
eyeButton.translatesAutoresizingMaskIntoConstraints = false
eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordUnsee), for: .normal) eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordUnsee), for: .normal)
eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordSee), for: .selected) eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordSee), for: .selected)
eyeButton.addTarget(self, action: #selector(handleEyeTap), for: .touchUpInside) eyeButton.addTarget(self, action: #selector(handleEyeTap), for: .touchUpInside)
@@ -194,6 +197,7 @@ class EPLoginInputView: UIView {
} }
private func setupCodeButton() { private func setupCodeButton() {
codeButton.translatesAutoresizingMaskIntoConstraints = false
codeButton.setTitle(YMLocalizedString("XPLoginInputView0"), for: .normal) codeButton.setTitle(YMLocalizedString("XPLoginInputView0"), for: .normal)
codeButton.setTitleColor(.white, for: .normal) codeButton.setTitleColor(.white, for: .normal)
codeButton.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) codeButton.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
@@ -249,6 +253,11 @@ class EPLoginInputView: UIView {
inputTextField.text = "" inputTextField.text = ""
} }
///
func displayKeyboard() {
inputTextField.becomeFirstResponder()
}
// MARK: - Actions // MARK: - Actions
@objc private func handleAreaTap() { @objc private func handleAreaTap() {

View File

@@ -11,40 +11,51 @@ import SnapKit
/// ///
/// 退 /// 退
class EPEditSettingViewController: UIViewController { class EPEditSettingViewController: BaseViewController {
// MARK: - UI Components // MARK: - UI Components
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.backgroundColor = UIColor(hex: "#0C0527")
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "SettingCell")
return tableView
}()
private lazy var profileImageView: UIImageView = { private lazy var profileImageView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 50 imageView.layer.cornerRadius = 60 // 120/2 = 60
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
imageView.backgroundColor = .systemGray5 imageView.backgroundColor = .systemGray5
imageView.isUserInteractionEnabled = true imageView.isUserInteractionEnabled = true
return imageView return imageView
}() }()
private lazy var cameraIconView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "icon_setting_camear")
imageView.backgroundColor = UIColor(hex: "#0C0527")
imageView.layer.cornerRadius = 15 // 30/2 = 15
imageView.layer.masksToBounds = true
return imageView
}()
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.backgroundColor = UIColor(hex: "#0C0527")
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "SettingCell")
tableView.isScrollEnabled = true //
return tableView
}()
// MARK: - Data // MARK: - Data
private var settingItems: [SettingItem] = [] private var settingItems: [SettingItem] = []
private var userInfo: UserInfoModel? private var userInfo: UserInfoModel?
private var apiHelper: EPMineAPIHelper = EPMineAPIHelper()
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false) setupNavigationBar()
setupUI() setupUI()
setupData() setupData()
loadUserInfo() loadUserInfo()
@@ -52,35 +63,96 @@ class EPEditSettingViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
//
navigationController?.setNavigationBarHidden(false, animated: animated) navigationController?.setNavigationBarHidden(false, animated: animated)
navigationController?.navigationBar.titleTextAttributes = [ }
.foregroundColor: UIColor.white,
.font: UIFont.systemFont(ofSize: 18, weight: .medium) override func viewWillDisappear(_ animated: Bool) {
] super.viewWillDisappear(animated)
navigationController?.navigationBar.barTintColor = UIColor(hex: "#0C0527") //
navigationController?.navigationBar.tintColor = .white restoreParentNavigationBarStyle()
navigationController?.navigationBar.isTranslucent = false
} }
// MARK: - Setup // MARK: - Setup
private func setupUI() { private func setupNavigationBar() {
view.backgroundColor = UIColor(hex: "#0C0527")
title = YMLocalizedString("EPEditSetting.Title") title = YMLocalizedString("EPEditSetting.Title")
// iOS 13+
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor(hex: "#0C0527")
appearance.titleTextAttributes = [
.foregroundColor: UIColor.white,
.font: UIFont.systemFont(ofSize: 18, weight: .medium)
]
appearance.shadowColor = .clear // 线
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
navigationController?.navigationBar.tintColor = .white //
//
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
// push backButtonTitle
navigationController?.navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(
title: "",
style: .plain,
target: nil,
action: nil
)
}
private func restoreParentNavigationBarStyle() {
// EPMineViewController 使
let transparentAppearance = UINavigationBarAppearance()
transparentAppearance.configureWithTransparentBackground()
transparentAppearance.backgroundColor = .clear
transparentAppearance.shadowColor = .clear
navigationController?.navigationBar.standardAppearance = transparentAppearance
navigationController?.navigationBar.scrollEdgeAppearance = transparentAppearance
navigationController?.navigationBar.compactAppearance = transparentAppearance
}
private func setupUI() {
view.backgroundColor = UIColor(hex: "#0C0527")
//
view.addSubview(profileImageView)
profileImageView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(40)
make.centerX.equalTo(view)
make.size.equalTo(120)
}
//
view.addSubview(cameraIconView)
cameraIconView.snp.makeConstraints { make in
make.bottom.equalTo(profileImageView.snp.bottom)
make.trailing.equalTo(profileImageView.snp.trailing)
make.size.equalTo(30)
}
// TableView
view.addSubview(tableView) view.addSubview(tableView)
tableView.snp.makeConstraints { make in tableView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top) make.top.equalTo(profileImageView.snp.bottom).offset(40)
make.leading.trailing.bottom.equalToSuperview() make.leading.trailing.bottom.equalTo(view)
} }
// //
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped)) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped))
profileImageView.addGestureRecognizer(tapGesture) profileImageView.addGestureRecognizer(tapGesture)
//
let cameraTapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped))
cameraIconView.addGestureRecognizer(cameraTapGesture)
} }
private func setupData() { private func setupData() {
settingItems = [ settingItems = [
SettingItem( SettingItem(
@@ -109,9 +181,17 @@ class EPEditSettingViewController: UIViewController {
action: { [weak self] in self?.handleReservedAction("AboutUs") } action: { [weak self] in self?.handleReservedAction("AboutUs") }
) )
] ]
NSLog("[EPEditSetting] setupData 完成,设置项数量: \(settingItems.count)")
} }
private func loadUserInfo() { private func loadUserInfo() {
// EPMineViewController
if userInfo != nil {
updateProfileImage()
tableView.reloadData()
return
}
// //
guard let uid = AccountInfoStorage.instance().getUid(), !uid.isEmpty else { guard let uid = AccountInfoStorage.instance().getUid(), !uid.isEmpty else {
print("[EPEditSetting] 未登录,无法获取用户信息") print("[EPEditSetting] 未登录,无法获取用户信息")
@@ -159,7 +239,7 @@ class EPEditSettingViewController: UIViewController {
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Camera"), style: .default) { [weak self] _ in alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Camera"), style: .default) { [weak self] _ in
self?.checkCameraPermissionAndPresent() self?.checkCameraPermissionAndPresent()
}) })
// //
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.PhotoLibrary"), style: .default) { [weak self] _ in alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.PhotoLibrary"), style: .default) { [weak self] _ in
self?.checkPhotoLibraryPermissionAndPresent() self?.checkPhotoLibraryPermissionAndPresent()
@@ -237,19 +317,33 @@ class EPEditSettingViewController: UIViewController {
} }
private func updateNickname(_ newNickname: String) { private func updateNickname(_ newNickname: String) {
// UserInfoModel //
let userInfo = UserInfoModel() showLoading()
userInfo.nick = newNickname
// Presenter (OC) // API
let presenter = XPMineUserInfoEditPresenter() apiHelper.updateNickname(withNick: newNickname,
presenter.getUserInfoEditDataSource(withUserInfo: userInfo) completion: { [weak self] in
self?.hideHUD()
//
self.userInfo?.nick = newNickname //
tableView.reloadData() self?.userInfo?.nick = newNickname
self?.tableView.reloadData()
print("[EPEditSetting] 昵称更新为: \(newNickname)")
//
self?.showSuccessToast(YMLocalizedString("XPMineUserInfoEditViewController13"))
print("[EPEditSetting] 昵称更新成功: \(newNickname)")
},
failure: { [weak self] (code: Int, msg: String?) in
self?.hideHUD()
//
let errorMsg = msg ?? "昵称更新失败,请稍后重试"
self?.showErrorToast(errorMsg)
print("[EPEditSetting] 昵称更新失败: \(code) - \(errorMsg)")
}
)
} }
private func showLogoutConfirm() { private func showLogoutConfirm() {
@@ -307,6 +401,16 @@ class EPEditSettingViewController: UIViewController {
alert.addAction(UIAlertAction(title: "OK", style: .default)) alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true) present(alert, animated: true)
} }
// MARK: - Public Methods
/// EPMineViewController
@objc func updateWithUserInfo(_ userInfo: UserInfoModel) {
self.userInfo = userInfo
updateProfileImage()
tableView.reloadData()
NSLog("[EPEditSetting] 已更新用户信息: \(userInfo.nick ?? "未知")")
}
} }
// MARK: - UITableViewDataSource & UITableViewDelegate // MARK: - UITableViewDataSource & UITableViewDelegate
@@ -314,15 +418,13 @@ class EPEditSettingViewController: UIViewController {
extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegate { extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int { func numberOfSections(in tableView: UITableView) -> Int {
return 2 // section + section return 1 // section
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 { let count = settingItems.count + 1 // +1 for nickname row
return 2 // + NSLog("[EPEditSetting] TableView rows count: \(count), settingItems: \(settingItems.count)")
} else { return count
return settingItems.count
}
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -331,35 +433,50 @@ extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegat
cell.textLabel?.textColor = .white cell.textLabel?.textColor = .white
cell.selectionStyle = .none cell.selectionStyle = .none
if indexPath.section == 0 { //
// section cell.contentView.subviews.forEach { $0.removeFromSuperview() }
if indexPath.row == 0 {
// if indexPath.row == 0 {
cell.textLabel?.text = YMLocalizedString("EPEditSetting.Avatar") //
cell.accessoryType = .disclosureIndicator cell.textLabel?.text = YMLocalizedString("EPEditSetting.Nickname")
// //
if cell.contentView.subviews.contains(profileImageView) { let arrowImageView = UIImageView()
profileImageView.removeFromSuperview() arrowImageView.image = UIImage(named: "icon_setting_right_arrow")
} arrowImageView.contentMode = .scaleAspectFit
cell.contentView.addSubview(profileImageView) cell.contentView.addSubview(arrowImageView)
profileImageView.snp.makeConstraints { make in arrowImageView.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-50) make.trailing.equalToSuperview().offset(-20)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.size.equalTo(100) make.size.equalTo(22)
}
} else {
//
cell.textLabel?.text = YMLocalizedString("EPEditSetting.Nickname")
cell.detailTextLabel?.text = userInfo?.nick ?? "未设置"
cell.detailTextLabel?.textColor = .lightGray
cell.accessoryType = .disclosureIndicator
} }
//
let nicknameLabel = UILabel()
nicknameLabel.text = userInfo?.nick ?? "未设置"
nicknameLabel.textColor = .lightGray
nicknameLabel.font = UIFont.systemFont(ofSize: 16)
cell.contentView.addSubview(nicknameLabel)
nicknameLabel.snp.makeConstraints { make in
make.trailing.equalTo(arrowImageView.snp.leading).offset(-12)
make.centerY.equalToSuperview()
}
} else { } else {
// section //
let item = settingItems[indexPath.row] let item = settingItems[indexPath.row - 1]
cell.textLabel?.text = item.title cell.textLabel?.text = item.title
cell.accessoryType = .disclosureIndicator
//
let arrowImageView = UIImageView()
arrowImageView.image = UIImage(named: "icon_setting_right_arrow")
arrowImageView.contentMode = .scaleAspectFit
cell.contentView.addSubview(arrowImageView)
arrowImageView.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-20)
make.centerY.equalToSuperview()
make.size.equalTo(22)
}
if item.style == .default { if item.style == .default {
cell.textLabel?.textColor = .systemRed cell.textLabel?.textColor = .systemRed
@@ -372,48 +489,36 @@ extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegat
} }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 { return 60 // 60pt
return 120 //
}
return 60
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 { if indexPath.row == 0 {
if indexPath.row == 0 { //
// showNicknameEditAlert()
showAvatarSelectionSheet()
} else {
//
showNicknameEditAlert()
}
} else { } else {
// //
let item = settingItems[indexPath.row] let item = settingItems[indexPath.row - 1]
item.action() item.action()
} }
} }
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return section == 0 ? 20 : 10 return 0
} }
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView() return nil
view.backgroundColor = UIColor(hex: "#0C0527")
return view
} }
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10 return 0
} }
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView() return nil
view.backgroundColor = UIColor(hex: "#0C0527")
return view
} }
} }
@@ -441,41 +546,75 @@ extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINaviga
} }
private func uploadAvatar(_ image: UIImage) { private func uploadAvatar(_ image: UIImage) {
// //
guard let imageData = image.jpegData(compressionQuality: 0.5) else { EPProgressHUD.showProgress(0, total: 1)
print("[EPEditSetting] 图片压缩失败")
return
}
// // 使 EPSDKManager OCR
let format = "jpg" EPSDKManager.shared.uploadImages([image],
let name = "image/\(UUID().uuidString).\(format)" progress: { uploaded, total in
EPProgressHUD.showProgress(uploaded, total: total)
// },
UploadFile.share().qCloudUploadImage(imageData, named: name, success: { [weak self] (key, resp) in success: { [weak self] resList in
print("[EPEditSetting] 头像上传成功: \(key)") EPProgressHUD.dismiss()
// API guard !resList.isEmpty,
self?.updateAvatarAPI(avatarUrl: key) let firstRes = resList.first,
let avatarUrl = firstRes["resUrl"] as? String else {
}, failure: { (resCode, message) in print("[EPEditSetting] 头像上传成功但无法获取URL")
print("[EPEditSetting] 头像上传失败: \(message)")
}) return
}
print("[EPEditSetting] 头像上传成功: \(avatarUrl)")
// API
self?.updateAvatarAPI(avatarUrl: avatarUrl)
},
failure: { [weak self] errorMsg in
EPProgressHUD.dismiss()
print("[EPEditSetting] 头像上传失败: \(errorMsg)")
//
DispatchQueue.main.async {
let alert = UIAlertController(title: "上传失败", message: errorMsg, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default))
self?.present(alert, animated: true)
}
}
)
} }
private func updateAvatarAPI(avatarUrl: String) { private func updateAvatarAPI(avatarUrl: String) {
// API // 使 API Helper
Api.userV2UploadAvatar({ [weak self] (data, code, msg) in apiHelper.updateAvatar(withUrl: avatarUrl, completion: { [weak self] in
print("[EPEditSetting] 头像更新成功")
//
self?.userInfo?.avatar = avatarUrl
//
self?.notifyParentAvatarUpdated(avatarUrl)
}, failure: { [weak self] (code: Int, msg: String?) in
print("[EPEditSetting] 头像更新失败: \(code) - \(msg ?? "未知错误")")
//
DispatchQueue.main.async { DispatchQueue.main.async {
if code == 200 { let alert = UIAlertController(
print("[EPEditSetting] 头像更新成功") title: "更新失败",
// message: msg ?? "头像更新失败,请稍后重试",
self?.userInfo?.avatar = avatarUrl preferredStyle: .alert
} else { )
print("[EPEditSetting] 头像更新失败: \(String(describing: msg))") alert.addAction(UIAlertAction(title: "确定", style: .default))
} self?.present(alert, animated: true)
} }
}, avatarUrl: avatarUrl, needPay: NSNumber(value: false)) })
}
private func notifyParentAvatarUpdated(_ avatarUrl: String) {
// EPMineViewController
let userInfo = ["avatarUrl": avatarUrl]
NotificationCenter.default.post(name: NSNotification.Name("EPEditSettingAvatarUpdated"), object: nil, userInfo: userInfo)
} }
} }

View File

@@ -41,7 +41,6 @@
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
[self setupUI]; [self setupUI];
NSLog(@"[EPMineViewController] viewDidLoad 完成"); NSLog(@"[EPMineViewController] viewDidLoad 完成");
@@ -49,10 +48,7 @@
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
//
[self.navigationController setNavigationBarHidden:YES animated:animated]; [self.navigationController setNavigationBarHidden:YES animated:animated];
// //
[self loadUserDetailInfo]; [self loadUserDetailInfo];
} }
@@ -60,8 +56,13 @@
// MARK: - Setup // MARK: - Setup
- (void)setupUI { - (void)setupUI {
// UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")];
self.view.backgroundColor = [UIColor colorWithRed:0.047 green:0.020 blue:0.153 alpha:1.0]; // #0C0527 bgImageView.contentMode = UIViewContentModeScaleAspectFill;
bgImageView.clipsToBounds = YES;
[self.view addSubview:bgImageView];
[bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}];
[self setupHeaderView]; [self setupHeaderView];
[self setupMomentListView]; [self setupMomentListView];
@@ -73,19 +74,25 @@
self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectZero]; self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.headerView]; [self.view addSubview:self.headerView];
// 使 // 使 Masonry
self.headerView.translatesAutoresizingMaskIntoConstraints = NO; [self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
[NSLayoutConstraint activateConstraints:@[ make.top.mas_equalTo(self.view);
[self.headerView.topAnchor constraintEqualToAnchor:self.view.topAnchor], make.leading.mas_equalTo(self.view);
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], make.trailing.mas_equalTo(self.view);
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], make.height.mas_equalTo(kGetScaleWidth(260));
[self.headerView.heightAnchor constraintEqualToConstant:320] }];
]];
// //
__weak typeof(self) weakSelf = self;
self.headerView.onSettingsButtonTapped = ^{
__strong typeof(weakSelf) self = weakSelf;
[self openSettings];
};
//
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(openSettings) selector:@selector(onAvatarUpdated:)
name:@"EPMineHeaderSettingsButtonTapped" name:@"EPEditSettingAvatarUpdated"
object:nil]; object:nil];
} }
@@ -93,14 +100,12 @@
self.momentListView = [[EPMomentListView alloc] initWithFrame:CGRectZero]; self.momentListView = [[EPMomentListView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.momentListView]; [self.view addSubview:self.momentListView];
// 使 [self.momentListView mas_makeConstraints:^(MASConstraintMaker *make) {
self.momentListView.translatesAutoresizingMaskIntoConstraints = NO; make.top.mas_equalTo(self.headerView.mas_bottom);
[NSLayoutConstraint activateConstraints:@[ make.bottom.mas_equalTo(self.view);
[self.momentListView.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor], make.leading.mas_equalTo(self.view);
[self.momentListView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], make.trailing.mas_equalTo(self.view);
[self.momentListView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], }];
[self.momentListView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]
]];
} }
// MARK: - Data Loading // MARK: - Data Loading
@@ -112,10 +117,10 @@
return; return;
} }
__weak typeof(self) weakSelf = self; @kWeakify(self);
[self.apiHelper getUserDetailInfoWithUid:uid [self.apiHelper getUserDetailInfoWithUid:uid
completion:^(UserInfoModel * _Nullable userInfo) { completion:^(UserInfoModel * _Nullable userInfo) {
__strong typeof(weakSelf) self = weakSelf; @kStrongify(self);
if (!userInfo) { if (!userInfo) {
NSLog(@"[EPMineViewController] 加载用户信息失败"); NSLog(@"[EPMineViewController] 加载用户信息失败");
return; return;
@@ -172,13 +177,37 @@
// MARK: - Actions // MARK: - Actions
- (void)openSettings { - (void)openSettings {
//
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@""
style:UIBarButtonItemStylePlain
target:nil
action:nil];
EPEditSettingViewController *settingsVC = [[EPEditSettingViewController alloc] init]; EPEditSettingViewController *settingsVC = [[EPEditSettingViewController alloc] init];
//
if (self.userInfo) {
[settingsVC updateWithUserInfo:self.userInfo];
}
[self.navigationController pushViewController:settingsVC animated:YES]; [self.navigationController pushViewController:settingsVC animated:YES];
NSLog(@"[EPMineViewController] 打开设置页面"); NSLog(@"[EPMineViewController] 打开设置页面,已传递用户信息");
}
- (void)onAvatarUpdated:(NSNotification *)notification {
NSString *avatarUrl = notification.userInfo[@"avatarUrl"];
if (avatarUrl && self.userInfo) {
//
self.userInfo.avatar = avatarUrl;
// UI
[self updateHeaderWithUserInfo:self.userInfo];
NSLog(@"[EPMineViewController] 头像已更新: %@", avatarUrl);
}
} }
- (void)dealloc { - (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self]; // 使 block
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"EPEditSettingAvatarUpdated" object:nil];
} }
@end @end

View File

@@ -24,6 +24,16 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^)(UserInfoModel * _Nullable userInfo))completion completion:(void (^)(UserInfoModel * _Nullable userInfo))completion
failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure; failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure;
/// 更新用户头像
- (void)updateAvatarWithUrl:(NSString *)avatarUrl
completion:(void (^)(void))completion
failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure;
/// 更新用户昵称
- (void)updateNicknameWithNick:(NSString *)nickname
completion:(void (^)(void))completion
failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -9,6 +9,7 @@
#import "Api+Mine.h" #import "Api+Mine.h"
#import "UserInfoModel.h" #import "UserInfoModel.h"
#import "BaseModel.h" #import "BaseModel.h"
#import "AccountInfoStorage.h"
@implementation EPMineAPIHelper @implementation EPMineAPIHelper
@@ -38,5 +39,39 @@
} uid:uid page:@"1" pageSize:@"20"]; } uid:uid page:@"1" pageSize:@"20"];
} }
- (void)updateAvatarWithUrl:(NSString *)avatarUrl
completion:(void (^)(void))completion
failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure {
[Api userV2UploadAvatar:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
if (completion) completion();
} else {
if (failure) failure(code, msg);
}
} avatarUrl:avatarUrl needPay:@NO];
}
- (void)updateNicknameWithNick:(NSString *)nickname
completion:(void (^)(void))completion
failure:(void (^)(NSInteger code, NSString * _Nullable msg))failure {
NSString *uid = [[AccountInfoStorage instance] getUid];
NSString *ticket = [[AccountInfoStorage instance] getTicket];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
if (nickname.length > 0) {
[params setValue:nickname forKey:@"nick"];
}
[params setObject:uid forKey:@"uid"];
[params setObject:ticket forKey:@"ticket"];
[Api completeUserInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
if (completion) completion();
} else {
if (failure) failure(code, msg);
}
} userInfo:params];
}
@end @end

View File

@@ -14,6 +14,9 @@ NS_ASSUME_NONNULL_BEGIN
/// 大圆形头像 + 渐变背景 + 用户信息展示 /// 大圆形头像 + 渐变背景 + 用户信息展示
@interface EPMineHeaderView : UIView @interface EPMineHeaderView : UIView
/// 设置按钮点击回调
@property (nonatomic, copy, nullable) void(^onSettingsButtonTapped)(void);
/// 更新用户信息 /// 更新用户信息
/// @param userInfoDict 用户信息字典 /// @param userInfoDict 用户信息字典
- (void)updateWithUserInfo:(NSDictionary *)userInfoDict; - (void)updateWithUserInfo:(NSDictionary *)userInfoDict;

View File

@@ -24,12 +24,6 @@
/// ///
@property (nonatomic, strong) UIButton *settingsButton; @property (nonatomic, strong) UIButton *settingsButton;
///
@property (nonatomic, strong) UIButton *followButton;
///
@property (nonatomic, strong) UIButton *fansButton;
@end @end
@implementation EPMineHeaderView @implementation EPMineHeaderView
@@ -97,36 +91,6 @@
make.trailing.equalTo(self).offset(-20); make.trailing.equalTo(self).offset(-20);
make.size.mas_equalTo(CGSizeMake(40, 40)); make.size.mas_equalTo(CGSizeMake(40, 40));
}]; }];
//
self.followButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.followButton setTitle:@"关注" forState:UIControlStateNormal];
[self.followButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.followButton.titleLabel.font = [UIFont systemFontOfSize:16];
self.followButton.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
self.followButton.layer.cornerRadius = 20;
[self addSubview:self.followButton];
[self.followButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.idLabel.mas_bottom).offset(20);
make.centerX.equalTo(self).offset(-50);
make.size.mas_equalTo(CGSizeMake(80, 40));
}];
//
self.fansButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.fansButton setTitle:@"粉丝" forState:UIControlStateNormal];
[self.fansButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.fansButton.titleLabel.font = [UIFont systemFontOfSize:16];
self.fansButton.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
self.fansButton.layer.cornerRadius = 20;
[self addSubview:self.fansButton];
[self.fansButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.idLabel.mas_bottom).offset(20);
make.centerX.equalTo(self).offset(50);
make.size.mas_equalTo(CGSizeMake(80, 40));
}];
} }
- (void)updateWithUserInfo:(NSDictionary *)userInfoDict { - (void)updateWithUserInfo:(NSDictionary *)userInfoDict {
@@ -138,14 +102,6 @@
NSString *uid = userInfoDict[@"uid"] ?: @""; NSString *uid = userInfoDict[@"uid"] ?: @"";
self.idLabel.text = [NSString stringWithFormat:@"ID:%@", uid]; self.idLabel.text = [NSString stringWithFormat:@"ID:%@", uid];
//
NSNumber *following = userInfoDict[@"following"] ?: @0;
[self.followButton setTitle:[NSString stringWithFormat:@"关注 %@", following] forState:UIControlStateNormal];
//
NSNumber *followers = userInfoDict[@"followers"] ?: @0;
[self.fansButton setTitle:[NSString stringWithFormat:@"粉丝 %@", followers] forState:UIControlStateNormal];
// //
NSString *avatarURL = userInfoDict[@"avatar"]; NSString *avatarURL = userInfoDict[@"avatar"];
if (avatarURL && avatarURL.length > 0) { if (avatarURL && avatarURL.length > 0) {
@@ -159,8 +115,10 @@
- (void)settingsButtonTapped { - (void)settingsButtonTapped {
NSLog(@"[EPMineHeaderView] 设置按钮点击"); NSLog(@"[EPMineHeaderView] 设置按钮点击");
// // 使 block
[[NSNotificationCenter defaultCenter] postNotificationName:@"EPMineHeaderSettingsButtonTapped" object:nil]; if (self.onSettingsButtonTapped) {
self.onSettingsButtonTapped();
}
} }
@end @end

View File

@@ -90,10 +90,16 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot
make.leading.trailing.equalTo(self.textView); make.leading.trailing.equalTo(self.textView);
make.height.mas_equalTo(1); make.height.mas_equalTo(1);
}]; }];
// 3
// itemW = ( - 30 - 20) / 3
// = 3itemW + 2(10*2)
CGFloat itemW = (KScreenWidth - 15*2 - 10*2)/3.0;
CGFloat collectionHeight = itemW * 3 + 10 * 2;
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.contentView).inset(15); make.leading.trailing.equalTo(self.contentView).inset(15);
make.top.equalTo(self.lineView.mas_bottom).offset(10); make.top.equalTo(self.lineView.mas_bottom).offset(10);
make.height.mas_equalTo(110); make.height.mas_equalTo(collectionHeight);
}]; }];
// //

View File

@@ -54,9 +54,6 @@
// MARK: - Setup UI // MARK: - Setup UI
- (void)setupUI { - (void)setupUI {
//
self.view.backgroundColor = [UIColor colorWithRed:0.95 green:0.95 blue:0.97 alpha:1.0];
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")]; UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")];
bgImageView.contentMode = UIViewContentModeScaleAspectFill; bgImageView.contentMode = UIViewContentModeScaleAspectFill;
bgImageView.clipsToBounds = YES; bgImageView.clipsToBounds = YES;

View File

@@ -1,42 +0,0 @@
//
// EPMomentAPIHelper_Deprecated.h
// YuMi
//
// Created by AI on 2025-10-10.
//
// ⚠️ DEPRECATED: 已被 EPMomentAPISwiftHelper.swift 替代
// 原因:
// 1. 继承 BaseMvpPresenter 会引起 Bridging Header 依赖链问题
// 2. Swift 版本更简洁、类型安全
// 3. 功能已完整迁移到 Swift 版本
//
// 保留此文件仅供参考,后续可删除
#import <Foundation/Foundation.h>
#import "BaseMvpPresenter.h"
#import "MomentsInfoModel.h"
#import "MomentsListInfoModel.h"
NS_ASSUME_NONNULL_BEGIN
/// 推荐/我的动态列表数据源类型
typedef NS_ENUM(NSInteger, EPMomentListSourceType) {
EPMomentListSourceTypeRecommend = 0,
EPMomentListSourceTypeMine = 1
};
/// 统一封装 Moments 列表 API
@interface EPMomentAPIHelper : BaseMvpPresenter
/// 拉取最新动态列表(默认 types:"0,2" 图片+文字)
- (void)fetchLatestMomentsWithNextID:(NSString *)nextID
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSString *nextMomentID))completion
failure:(void(^)(NSInteger code, NSString * _Nullable msg))failure;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,44 +0,0 @@
//
// EPMomentAPIHelper_Deprecated.m
// YuMi
//
// Created by AI on 2025-10-10.
//
// DEPRECATED: EPMomentAPISwiftHelper.swift
//
#import <UIKit/UIKit.h>
#import "EPMomentAPIHelper_Deprecated.h"
#import "Api+Moments.h"
#import "AccountInfoStorage.h"
#import "BaseModel.h"
@implementation EPMomentAPIHelper
// [Api momentsRecommendList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
// if (code == 200 && data.data) {
// NSArray *array = [MomentsInfoModel modelsWithArray:data.data];
// if (completion) completion(array ?: @[], 200, @"success");
// } else {
// if (completion) completion(@[], code, msg);
// }
// } page:pageStr pageSize:pageSizeStr types:types];
- (void)fetchLatestMomentsWithNextID:(NSString *)nextID
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSString *nextMomentID))completion
failure:(void(^)(NSInteger code, NSString * _Nullable msg))failure {
NSString *pageSizeStr = @"20";
NSString *types = @"0,2"; // +
[Api momentsLatestList:[self createHttpCompletion:^(BaseModel * _Nonnull data) {
MomentsListInfoModel *listInfo = [MomentsListInfoModel modelWithDictionary:data.data];
if (completion) completion(listInfo.dynamicList ?: @[],
listInfo.nextDynamicId);
} fail:^(NSInteger code, NSString * _Nullable msg) {
if (failure) failure(code, msg);
}] dynamicId:nextID pageSize:pageSizeStr types:types];
}
@end

View File

@@ -120,7 +120,8 @@
make.leading.trailing.equalTo(self.cardView); make.leading.trailing.equalTo(self.cardView);
make.top.equalTo(self.imagesContainer.mas_bottom).offset(12); make.top.equalTo(self.imagesContainer.mas_bottom).offset(12);
make.height.mas_equalTo(50); make.height.mas_equalTo(50);
make.bottom.equalTo(self.cardView).offset(-8); //
make.bottom.equalTo(self.cardView).offset(-8).priority(UILayoutPriorityDefaultHigh);
}]; }];
// //

View File

@@ -361,8 +361,51 @@ import SnapKit
normalImage: normalImage, normalImage: normalImage,
selectedImage: selectedImage selectedImage: selectedImage
) )
// delegate
nav.delegate = self
return nav return nav
} }
// MARK: - TabBar Visibility Control
/// TabBar
private func showCustomTabBar(animated: Bool = true) {
guard customTabBarView.isHidden else { return }
if animated {
customTabBarView.isHidden = false
customTabBarView.alpha = 0
customTabBarView.transform = CGAffineTransform(translationX: 0, y: 20)
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) {
self.customTabBarView.alpha = 1
self.customTabBarView.transform = .identity
}
} else {
customTabBarView.isHidden = false
customTabBarView.alpha = 1
}
}
/// TabBar
private func hideCustomTabBar(animated: Bool = true) {
guard !customTabBarView.isHidden else { return }
if animated {
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: {
self.customTabBarView.alpha = 0
self.customTabBarView.transform = CGAffineTransform(translationX: 0, y: 20)
}) { _ in
self.customTabBarView.isHidden = true
self.customTabBarView.transform = .identity
}
} else {
customTabBarView.isHidden = true
customTabBarView.alpha = 0
}
}
} }
// MARK: - UITabBarControllerDelegate // MARK: - UITabBarControllerDelegate
@@ -389,6 +432,29 @@ extension EPTabBarController: UITabBarControllerDelegate {
} }
} }
// MARK: - UINavigationControllerDelegate
extension EPTabBarController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool) {
//
let isRootViewController = navigationController.viewControllers.count == 1
if isRootViewController {
// TabBar
showCustomTabBar(animated: animated)
NSLog("[EPTabBarController] 显示 TabBar - 根页面")
} else {
// TabBar
hideCustomTabBar(animated: animated)
NSLog("[EPTabBarController] 隐藏 TabBar - 子页面 (层级: \(navigationController.viewControllers.count))")
}
}
}
// MARK: - OC Compatibility // MARK: - OC Compatibility
extension EPTabBarController { extension EPTabBarController {

View File

@@ -45,6 +45,9 @@
#import "YYUtility.h" #import "YYUtility.h"
#import "SDWebImage.h" #import "SDWebImage.h"
// MARK: - API Helpers
#import "EPMineAPIHelper.h"
// MARK: - Utilities // MARK: - Utilities
#import "UIImage+Utils.h" #import "UIImage+Utils.h"
#import "NSString+Utils.h" #import "NSString+Utils.h"