feat: 添加设置编辑页面及相关功能

主要变更:
1. 新增 EPEditSettingViewController,提供用户头像更新、昵称修改和退出登录功能。
2. 在 Bridging Header 中引入 UserInfoModel、XPMineUserInfoEditPresenter 等新模块,以支持设置页面的功能。
3. 更新多语言文件,添加设置页面相关的本地化字符串。

此更新旨在提升用户体验,简化用户信息管理流程。
This commit is contained in:
edwinQQQ
2025-10-13 19:20:11 +08:00
parent 02a8335d70
commit e4f4557369
13 changed files with 771 additions and 60 deletions

View File

@@ -396,7 +396,6 @@
4C0642852E97BD9500BAF413 /* APIConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0642842E97BD9500BAF413 /* APIConfig.swift */; };
4C0642882E97BDA300BAF413 /* GlobalEventManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0642872E97BDA300BAF413 /* GlobalEventManager.m */; };
4C06428B2E98DC5F00BAF413 /* EPTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06428A2E98DC5F00BAF413 /* EPTabBarController.swift */; };
4C06428E2E98DC7E00BAF413 /* EPMineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C06428D2E98DC7E00BAF413 /* EPMineViewController.m */; };
4C0642912E98DC8700BAF413 /* EPMomentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0642902E98DC8700BAF413 /* EPMomentViewController.m */; };
4C0642992E98F77900BAF413 /* EPMomentListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0642982E98F77900BAF413 /* EPMomentListView.m */; };
4C06429C2E99120600BAF413 /* EPMomentPublishViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C06429B2E99120600BAF413 /* EPMomentPublishViewController.m */; };
@@ -757,6 +756,8 @@
4CD19EAB2E9CDF980069DAA0 /* MoliAvatar.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD19DC02E9CDF980069DAA0 /* MoliAvatar.m */; };
4CD19EAE2E9CDFC30069DAA0 /* EPLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD19EAC2E9CDFC30069DAA0 /* EPLoginButton.swift */; };
4CD19EAF2E9CDFC30069DAA0 /* EPLoginInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD19EAD2E9CDFC30069DAA0 /* EPLoginInputView.swift */; };
4CD19EB12E9D12600069DAA0 /* EPEditSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD19EB02E9D12600069DAA0 /* EPEditSettingViewController.swift */; };
4CD19EB42E9D141A0069DAA0 /* EPMineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD19EB32E9D141A0069DAA0 /* EPMineViewController.m */; };
4CD401472E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD401462E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.m */; };
4CD4014A2E718E36003F5009 /* XPBlankRoomModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD401492E718E36003F5009 /* XPBlankRoomModel.m */; };
4CD47BB52E61514900BCDA46 /* StageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD47BB42E61514900BCDA46 /* StageViewManager.m */; };
@@ -2433,8 +2434,6 @@
4C0642872E97BDA300BAF413 /* GlobalEventManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GlobalEventManager.m; sourceTree = "<group>"; };
4C0642892E97BDC900BAF413 /* YuMi-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YuMi-Bridging-Header.h"; sourceTree = "<group>"; };
4C06428A2E98DC5F00BAF413 /* EPTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPTabBarController.swift; sourceTree = "<group>"; };
4C06428C2E98DC7E00BAF413 /* EPMineViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMineViewController.h; sourceTree = "<group>"; };
4C06428D2E98DC7E00BAF413 /* EPMineViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMineViewController.m; sourceTree = "<group>"; };
4C06428F2E98DC8700BAF413 /* EPMomentViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMomentViewController.h; sourceTree = "<group>"; };
4C0642902E98DC8700BAF413 /* EPMomentViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMomentViewController.m; sourceTree = "<group>"; };
4C0642972E98F77900BAF413 /* EPMomentListView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMomentListView.h; sourceTree = "<group>"; };
@@ -2970,6 +2969,9 @@
4CD19DC82E9CDF980069DAA0 /* XNDJTDDLoadingTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XNDJTDDLoadingTool.m; sourceTree = "<group>"; };
4CD19EAC2E9CDFC30069DAA0 /* EPLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPLoginButton.swift; sourceTree = "<group>"; };
4CD19EAD2E9CDFC30069DAA0 /* EPLoginInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPLoginInputView.swift; sourceTree = "<group>"; };
4CD19EB02E9D12600069DAA0 /* EPEditSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPEditSettingViewController.swift; sourceTree = "<group>"; };
4CD19EB22E9D141A0069DAA0 /* EPMineViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMineViewController.h; sourceTree = "<group>"; };
4CD19EB32E9D141A0069DAA0 /* EPMineViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMineViewController.m; sourceTree = "<group>"; };
4CD401452E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPPartyRoomItemCollectionViewCell.h; sourceTree = "<group>"; };
4CD401462E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPPartyRoomItemCollectionViewCell.m; sourceTree = "<group>"; };
4CD401482E718E36003F5009 /* XPBlankRoomModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPBlankRoomModel.h; sourceTree = "<group>"; };
@@ -6376,8 +6378,9 @@
4C0642712E97BD6D00BAF413 /* Controllers */ = {
isa = PBXGroup;
children = (
4C06428C2E98DC7E00BAF413 /* EPMineViewController.h */,
4C06428D2E98DC7E00BAF413 /* EPMineViewController.m */,
4CD19EB22E9D141A0069DAA0 /* EPMineViewController.h */,
4CD19EB32E9D141A0069DAA0 /* EPMineViewController.m */,
4CD19EB02E9D12600069DAA0 /* EPEditSettingViewController.swift */,
);
path = Controllers;
sourceTree = "<group>";
@@ -12415,7 +12418,6 @@
23FF426D2AA5E4EE0055733C /* XPNewHomePartyAudioView.m in Sources */,
E81060E82987720F00B772F0 /* MessageUnSupportModel.m in Sources */,
E8D34D6028082BA5009C4835 /* XPMineUserDataPresenter.m in Sources */,
4C06428E2E98DC7E00BAF413 /* EPMineViewController.m in Sources */,
E84CBCE4284372D800D43221 /* XPRoomHalfMessageView.m in Sources */,
E8EEB8F226FC2050007C6EBA /* SDPhotoBrowser.m in Sources */,
23CEFC4F2AFB8FC100576D89 /* BSSDLayoutUtil.m in Sources */,
@@ -12640,6 +12642,7 @@
E8412FB02779CB4D006E1101 /* XPRoomSettingPresenter.m in Sources */,
E878893C273A54C300BF1D57 /* Api+Gift.m in Sources */,
E897ABFC28AF2E71003B3587 /* XPSailingGiftView.m in Sources */,
4CD19EB42E9D141A0069DAA0 /* EPMineViewController.m in Sources */,
E8998D8028597B0300C68558 /* XPRoomLuckyBigPrizeView.m in Sources */,
E88E4A80297673DC00019A50 /* SessionNavLiveView.m in Sources */,
544879EA2CD215F400D58DC1 /* CustomRoomBGCell.m in Sources */,
@@ -13064,6 +13067,7 @@
9BAA5FED277A1BBE007453F3 /* XPPrivacyViewController.m in Sources */,
E873EB02280922720071030D /* XPMineUserInfoEmptyCollectionViewCell.m in Sources */,
E872309326E8D31500B90D4F /* LoginVerifCodeView.m in Sources */,
4CD19EB12E9D12600069DAA0 /* EPEditSettingViewController.swift in Sources */,
E82107872987E49100DE7040 /* MessageRedPacketModel.m in Sources */,
23B8D8E12B87715100CA472F /* PIGeneralPublicScreenModel.m in Sources */,
23194DD52AD292F200649F51 /* PIPageControl.m in Sources */,

View File

@@ -0,0 +1,522 @@
//
// EPEditSettingViewController.swift
// YuMi
//
// Created by AI on 2025-01-27.
//
import UIKit
import Photos
import SnapKit
///
/// 退
class EPEditSettingViewController: UIViewController {
// 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 = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 50
imageView.layer.masksToBounds = true
imageView.backgroundColor = .systemGray5
imageView.isUserInteractionEnabled = true
return imageView
}()
// MARK: - Data
private var settingItems: [SettingItem] = []
private var userInfo: UserInfoModel?
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
setupUI()
setupData()
loadUserInfo()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//
navigationController?.setNavigationBarHidden(false, animated: animated)
navigationController?.navigationBar.titleTextAttributes = [
.foregroundColor: UIColor.white,
.font: UIFont.systemFont(ofSize: 18, weight: .medium)
]
navigationController?.navigationBar.barTintColor = UIColor(hex: "#0C0527")
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.isTranslucent = false
}
// MARK: - Setup
private func setupUI() {
view.backgroundColor = UIColor(hex: "#0C0527")
title = YMLocalizedString("EPEditSetting.Title")
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.leading.trailing.bottom.equalToSuperview()
}
//
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped))
profileImageView.addGestureRecognizer(tapGesture)
}
private func setupData() {
settingItems = [
SettingItem(
title: YMLocalizedString("EPEditSetting.PersonalInfo"),
action: { [weak self] in self?.handleReservedAction("PersonalInfo") }
),
SettingItem(
title: YMLocalizedString("EPEditSetting.Help"),
action: { [weak self] in self?.handleReservedAction("Help") }
),
SettingItem(
title: YMLocalizedString("EPEditSetting.ClearCache"),
action: { [weak self] in self?.handleReservedAction("ClearCache") }
),
SettingItem(
title: YMLocalizedString("EPEditSetting.CheckUpdate"),
action: { [weak self] in self?.handleReservedAction("CheckUpdate") }
),
SettingItem(
title: YMLocalizedString("EPEditSetting.Logout"),
style: .default,
action: { [weak self] in self?.showLogoutConfirm() }
),
SettingItem(
title: YMLocalizedString("EPEditSetting.AboutUs"),
action: { [weak self] in self?.handleReservedAction("AboutUs") }
)
]
}
private func loadUserInfo() {
//
guard let uid = AccountInfoStorage.instance().getUid(), !uid.isEmpty else {
print("[EPEditSetting] 未登录,无法获取用户信息")
return
}
// TODO: API
// UserInfoModel
let tempUserInfo = UserInfoModel()
tempUserInfo.nick = "User"
tempUserInfo.avatar = ""
userInfo = tempUserInfo
updateProfileImage()
tableView.reloadData()
}
private func updateProfileImage() {
guard let avatarUrl = userInfo?.avatar, !avatarUrl.isEmpty else {
profileImageView.image = UIImage(systemName: "person.circle.fill")
return
}
// 使SDWebImage
if let url = URL(string: avatarUrl) {
profileImageView.sd_setImage(with: url, placeholderImage: UIImage(systemName: "person.circle.fill"))
}
}
// MARK: - Actions
@objc private func profileImageTapped() {
showAvatarSelectionSheet()
}
@objc private func openSettings() {
//
handleReservedAction("Settings")
}
private func showAvatarSelectionSheet() {
let alert = UIAlertController(title: YMLocalizedString("EPEditSetting.EditNickname"), message: nil, preferredStyle: .actionSheet)
//
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Camera"), style: .default) { [weak self] _ in
self?.checkCameraPermissionAndPresent()
})
//
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.PhotoLibrary"), style: .default) { [weak self] _ in
self?.checkPhotoLibraryPermissionAndPresent()
})
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel))
// iPad
if let popover = alert.popoverPresentationController {
popover.sourceView = profileImageView
popover.sourceRect = profileImageView.bounds
}
present(alert, animated: true)
}
private func checkCameraPermissionAndPresent() {
YYUtility.checkCameraAvailable { [weak self] in
self?.presentImagePicker(sourceType: .camera)
} denied: { [weak self] in
self?.showPermissionAlert(title: "Camera Access", message: "Please allow camera access in Settings")
} restriction: { [weak self] in
self?.showPermissionAlert(title: "Camera Restricted", message: "Camera access is restricted on this device")
}
}
private func checkPhotoLibraryPermissionAndPresent() {
YYUtility.checkAssetsLibrayAvailable { [weak self] in
self?.presentImagePicker(sourceType: .photoLibrary)
} denied: { [weak self] in
self?.showPermissionAlert(title: "Photo Library Access", message: "Please allow photo library access in Settings")
} restriction: { [weak self] in
self?.showPermissionAlert(title: "Photo Library Restricted", message: "Photo library access is restricted on this device")
}
}
private func presentImagePicker(sourceType: UIImagePickerController.SourceType) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = sourceType
imagePicker.allowsEditing = true
present(imagePicker, animated: true)
}
private func showPermissionAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in
if let settingsURL = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsURL)
}
})
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel))
present(alert, animated: true)
}
private func showNicknameEditAlert() {
let alert = UIAlertController(
title: YMLocalizedString("EPEditSetting.EditNickname"),
message: nil,
preferredStyle: .alert
)
alert.addTextField { [weak self] textField in
textField.text = self?.userInfo?.nick ?? ""
textField.placeholder = YMLocalizedString("EPEditSetting.EnterNickname")
}
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel))
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Confirm"), style: .default) { [weak self] _ in
guard let newNickname = alert.textFields?.first?.text, !newNickname.isEmpty else { return }
self?.updateNickname(newNickname)
})
present(alert, animated: true)
}
private func updateNickname(_ newNickname: String) {
// UserInfoModel
let userInfo = UserInfoModel()
userInfo.nick = newNickname
// Presenter (OC)
let presenter = XPMineUserInfoEditPresenter()
presenter.getUserInfoEditDataSource(withUserInfo: userInfo)
//
self.userInfo?.nick = newNickname
tableView.reloadData()
print("[EPEditSetting] 昵称更新为: \(newNickname)")
}
private func showLogoutConfirm() {
let alert = UIAlertController(
title: YMLocalizedString("EPEditSetting.LogoutConfirm"),
message: nil,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel))
alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Logout"), style: .destructive) { [weak self] _ in
self?.performLogout()
})
present(alert, animated: true)
}
private func performLogout() {
guard let account = AccountInfoStorage.instance().accountModel else {
print("[EPEditSetting] 账号信息不存在")
return
}
// API
Api.logoutCurrentAccount({ [weak self] (data, code, msg) in
DispatchQueue.main.async {
//
AccountInfoStorage.instance().saveAccountInfo(nil)
AccountInfoStorage.instance().saveTicket(nil)
//
self?.navigateToLogin()
}
}, access_token: account.access_token)
}
private func navigateToLogin() {
let loginVC = EPLoginViewController()
let nav = UINavigationController(rootViewController: loginVC)
if let window = UIApplication.shared.windows.first {
window.rootViewController = nav
window.makeKeyAndVisible()
}
print("[EPEditSetting] 已跳转到登录页面")
}
private func handleReservedAction(_ title: String) {
print("[\(title)] - 功能预留,待后续实现")
// TODO: Phase 2 implementation
//
let alert = UIAlertController(title: "Coming Soon", message: "This feature will be available in the next update.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
// MARK: - UITableViewDataSource & UITableViewDelegate
extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2 // section + section
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 2 // +
} else {
return settingItems.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SettingCell", for: indexPath)
cell.backgroundColor = UIColor(hex: "#0C0527")
cell.textLabel?.textColor = .white
cell.selectionStyle = .none
if indexPath.section == 0 {
// section
if indexPath.row == 0 {
//
cell.textLabel?.text = YMLocalizedString("EPEditSetting.Avatar")
cell.accessoryType = .disclosureIndicator
//
if cell.contentView.subviews.contains(profileImageView) {
profileImageView.removeFromSuperview()
}
cell.contentView.addSubview(profileImageView)
profileImageView.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-50)
make.centerY.equalToSuperview()
make.size.equalTo(100)
}
} else {
//
cell.textLabel?.text = YMLocalizedString("EPEditSetting.Nickname")
cell.detailTextLabel?.text = userInfo?.nick ?? "未设置"
cell.detailTextLabel?.textColor = .lightGray
cell.accessoryType = .disclosureIndicator
}
} else {
// section
let item = settingItems[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = .disclosureIndicator
if item.style == .default {
cell.textLabel?.textColor = .systemRed
} else {
cell.textLabel?.textColor = .white
}
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 {
return 120 //
}
return 60
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
if indexPath.row == 0 {
//
showAvatarSelectionSheet()
} else {
//
showNicknameEditAlert()
}
} else {
//
let item = settingItems[indexPath.row]
item.action()
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return section == 0 ? 20 : 10
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(hex: "#0C0527")
return view
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(hex: "#0C0527")
return view
}
}
// MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate
extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage else {
print("[EPEditSetting] 未能获取选择的图片")
return
}
//
profileImageView.image = image
//
uploadAvatar(image)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
}
private func uploadAvatar(_ image: UIImage) {
//
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
print("[EPEditSetting] 图片压缩失败")
return
}
//
let format = "jpg"
let name = "image/\(UUID().uuidString).\(format)"
//
UploadFile.share().qCloudUploadImage(imageData, named: name, success: { [weak self] (key, resp) in
print("[EPEditSetting] 头像上传成功: \(key)")
// API
self?.updateAvatarAPI(avatarUrl: key)
}, failure: { (resCode, message) in
print("[EPEditSetting] 头像上传失败: \(message)")
})
}
private func updateAvatarAPI(avatarUrl: String) {
// API
Api.userV2UploadAvatar({ [weak self] (data, code, msg) in
DispatchQueue.main.async {
if code == 200 {
print("[EPEditSetting] 头像更新成功")
//
self?.userInfo?.avatar = avatarUrl
} else {
print("[EPEditSetting] 头像更新失败: \(String(describing: msg))")
}
}
}, avatarUrl: avatarUrl, needPay: NSNumber(value: false))
}
}
// MARK: - Helper Models
private struct SettingItem {
let title: String
let style: UITableViewCell.SelectionStyle
let action: () -> Void
init(title: String, style: UITableViewCell.SelectionStyle = .default, action: @escaping () -> Void) {
self.title = title
self.style = style
self.action = action
}
}
// MARK: - UIColor Extension
private extension UIColor {
convenience init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
red: CGFloat(r) / 255,
green: CGFloat(g) / 255,
blue: CGFloat(b) / 255,
alpha: CGFloat(a) / 255
)
}
}

View File

@@ -12,6 +12,8 @@
#import "EPMineAPIHelper.h"
#import "AccountInfoStorage.h"
#import "UserInfoModel.h"
#import <Masonry/Masonry.h>
#import "YuMi-Swift.h" // Swift
@interface EPMineViewController ()
@@ -42,7 +44,7 @@
[self setupUI];
NSLog(@"[EPMineViewController] 个人主页加载完成");
NSLog(@"[EPMineViewController] viewDidLoad 完成");
}
- (void)viewWillAppear:(BOOL)animated {
@@ -56,17 +58,10 @@
}
// MARK: - Setup
- (void)setupUI {
//
self.view.backgroundColor = [UIColor clearColor];
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")];
bgImageView.contentMode = UIViewContentModeScaleAspectFill;
bgImageView.clipsToBounds = YES;
[self.view addSubview:bgImageView];
[bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}];
//
self.view.backgroundColor = [UIColor colorWithRed:0.047 green:0.020 blue:0.153 alpha:1.0]; // #0C0527
[self setupHeaderView];
[self setupMomentListView];
@@ -75,76 +70,88 @@
}
- (void)setupHeaderView {
self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.headerView];
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(20);
make.leading.trailing.equalTo(self.view);
make.height.equalTo(@300);
}];
// 使
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:320]
]];
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(openSettings)
name:@"EPMineHeaderSettingsButtonTapped"
object:nil];
}
- (void)setupMomentListView {
self.momentListView = [[EPMomentListView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.momentListView];
[self.momentListView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.headerView.mas_bottom).offset(10);
make.leading.trailing.bottom.equalTo(self.view);
}];
// 使
self.momentListView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.momentListView.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor],
[self.momentListView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.momentListView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.momentListView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]
]];
}
// MARK: - Data Loading
- (void)loadUserDetailInfo {
NSString *uid = [[AccountInfoStorage instance] getUid];
if (!uid.length) {
NSLog(@"[EPMineViewController] 未登录,无法获取用户信息");
if (!uid || uid.length == 0) {
NSLog(@"[EPMineViewController] 用户未登录");
return;
}
__weak typeof(self) weakSelf = self;
[self.apiHelper getUserDetailInfoWithUid:uid completion:^(UserInfoModel * _Nullable userInfo) {
[self.apiHelper getUserDetailInfoWithUid:uid
completion:^(UserInfoModel * _Nullable userInfo) {
__strong typeof(weakSelf) self = weakSelf;
if (!userInfo) {
NSLog(@"[EPMineViewController] 加载用户信息失败");
return;
}
self.userInfo = userInfo;
[self updateHeaderWithUserInfo:userInfo];
//
NSDictionary *userInfoDict = @{
@"nickname": userInfo.nick ?: @"未设置昵称",
@"avatar": userInfo.avatar ?: @"",
@"uid": userInfo.uid > 0 ? @(userInfo.uid).stringValue : @"",
@"followers": @(userInfo.fansNum),
@"following": @(userInfo.followNum),
};
[self.headerView updateWithUserInfo:userInfoDict];
// 使
[self.momentListView loadWithDynamicInfo:userInfo.dynamicInfo refreshCallback:^{
[self loadUserDetailInfo];
}];
NSLog(@"[EPMineViewController] 用户详情加载成功: %@ (动态数: %lu)",
userInfo.nick, (unsigned long)userInfo.dynamicInfo.count);
// 使
if (userInfo.dynamicInfo && userInfo.dynamicInfo.count > 0) {
[self.momentListView loadWithDynamicInfo:userInfo.dynamicInfo refreshCallback:^{
[self loadUserDetailInfo]; //
}];
}
} failure:^(NSInteger code, NSString * _Nullable msg) {
NSLog(@"[EPMineViewController] 用户详情加载失败: code=%ld, msg=%@", (long)code, msg);
NSLog(@"[EPMineViewController] 加载用户信息失败: %@", msg);
}];
}
- (void)updateHeaderWithUserInfo:(UserInfoModel *)userInfo {
NSDictionary *userInfoDict = @{
@"nickname": userInfo.nick ?: @"未设置昵称",
@"uid": [NSString stringWithFormat:@"%ld", (long)userInfo.uid],
@"avatar": userInfo.avatar ?: @"",
@"following": @(userInfo.followNum),
@"followers": @(userInfo.fansNum)
};
[self.headerView updateWithUserInfo:userInfoDict];
}
// MARK: - Lazy Loading
- (EPMineHeaderView *)headerView {
if (!_headerView) {
_headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
}
return _headerView;
}
- (EPMomentListView *)momentListView {
if (!_momentListView) {
_momentListView = [[EPMomentListView alloc] initWithFrame:CGRectZero];
_momentListView = [[EPMomentListView alloc] init];
__weak typeof(self) weakSelf = self;
_momentListView.onSelectMoment = ^(NSInteger index) {
__strong typeof(weakSelf) self = weakSelf;
@@ -162,4 +169,16 @@
return _apiHelper;
}
// MARK: - Actions
- (void)openSettings {
EPEditSettingViewController *settingsVC = [[EPEditSettingViewController alloc] init];
[self.navigationController pushViewController:settingsVC animated:YES];
NSLog(@"[EPMineViewController] 打开设置页面");
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

View File

@@ -159,7 +159,8 @@
- (void)settingsButtonTapped {
NSLog(@"[EPMineHeaderView] 设置按钮点击");
// TODO:
//
[[NSNotificationCenter defaultCenter] postNotificationName:@"EPMineHeaderSettingsButtonTapped" object:nil];
}
@end

View File

@@ -39,6 +39,11 @@
#import "AccountInfoStorage.h"
#import "MomentsInfoModel.h"
#import "MomentsListInfoModel.h"
#import "UserInfoModel.h"
#import "XPMineUserInfoEditPresenter.h"
#import "UploadFile.h"
#import "YYUtility.h"
#import "SDWebImage.h"
// MARK: - Utilities
#import "UIImage+Utils.h"

View File

@@ -4256,3 +4256,23 @@ ineHeadView12" = "الحمل";
"20.20.62_text_22" = "لقد شغّلت شاشة ميكروفون CP.";
"20.20.62_text_23" = "لقد أوقفت شاشة ميكروفون CP. شاشة ميكروفون CP غير مرئية في هذه الغرفة. انقر لتفعيلها مرة أخرى.";
"20.20.62_text_24" = "لقد أوقفت وضع التربو.";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";

View File

@@ -4046,3 +4046,23 @@
"20.20.62_text_22" = "You have turn on the CP Mic Display.";
"20.20.62_text_23" = "You have turn off the CP Mic Display. The CP Mic Display is not visible in this room. Click to enable it again.";
"20.20.62_text_24" = "You have turned off Turbo Mode.";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";

View File

@@ -3717,6 +3717,26 @@
"RoomBoom_5" = "Nombre de la sala";//"Room name";
"RoomBoom_6" = "Hora de reinicio: 0:00 (GMT+3) diariamente";
"RoomBoom_7" = " Clasificación de Colaboradores";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";
"RoomBoom_8" = "Super Jackpot";
"RoomBoom_9" = "Reset time: 0:00 (GMT+8) daily";
"RoomBoom_10" = "The rewards are for reference only. The specific gifts are determined by your contribution value and luck.";

View File

@@ -3337,3 +3337,23 @@
"20.20.62_text_22" = "Você ativou a Exibição do Microfone CP.";
"20.20.62_text_23" = "Você desativou a Exibição do Microfone CP. A Exibição do Microfone CP não está visível nesta sala. Clique para ativá-la novamente.";
"20.20.62_text_24" = "Você desativou o Modo Turbo.";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";

View File

@@ -3716,6 +3716,26 @@
"RoomBoom_5" = "";//"Название комнаты:";
"RoomBoom_6" = "Время сброса: 0:00 (GMT+3) ежедневно";
"RoomBoom_7" = " Рейтинг спонсоров";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";
"RoomBoom_8" = "Супер джекпот";
"RoomBoom_9" = "Время сброса: 0:00 (GMT+8) ежедневно";
"RoomBoom_10" = "Награды приведены для справки. Конкретные подарки определяются вашим вкладом и удачей.";

View File

@@ -3837,3 +3837,23 @@
"20.20.62_text_22" = "CP Mikrofon Ekranını açtınız.";
"20.20.62_text_23" = "CP Mikrofon Ekranını kapattınız. CP Mikrofon Ekranı bu odada görünmüyor. Tekrar etkinleştirmek için tıklayın.";
"20.20.62_text_24" = "Turbo Modu'nu kapattınız.";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";

View File

@@ -3717,6 +3717,26 @@ Tasdiqlangandan so'ng, sekretar sizga uni chop etishda yordam beradi va sizni xa
"1.0.18_1" = "Bepul";
"1.0.18_2" = "Pay";
"1.0.18_3" = "Maxsus";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";
"1.0.18_4" = "Yangi yaratish";
"1.0.18_5" = "Siz maksimal 6 ta fonni moslashtirishingiz mumkin.";
"1.0.18_6" = "Maxsus fon sifatida bir vaqtning o'zida maksimal 6 ta rasm yuklashingiz mumkin. \nFon yaratilgandan so'ng, uni bekor qilib bo'lmaydi. \nYuklangan fonni 24 soat ichida tekshiramiz. \nAgar fon rad etilsa, sizga tangalarni qaytarib beramiz.";

View File

@@ -3707,3 +3707,23 @@
"20.20.62_text_22" = "您已開啟 CP 麥克風顯示。";
"20.20.62_text_23" = "您已關閉 CP 麥克風顯示。此房間中不顯示 CP 麥克風顯示。點選可重新啟用。";
"20.20.62_text_24" = "您已關閉 Turbo 模式。";
// EPEditSetting - 设置页面多语言Key
"EPEditSetting.Title" = "Edit";
"EPEditSetting.Avatar" = "Avatar";
"EPEditSetting.Nickname" = "Nickname";
"EPEditSetting.PersonalInfo" = "Personal Information and Permissions";
"EPEditSetting.Help" = "Help";
"EPEditSetting.ClearCache" = "Clear Cache";
"EPEditSetting.CheckUpdate" = "Check for Updates";
"EPEditSetting.AboutUs" = "About Us";
"EPEditSetting.Logout" = "Log out of account";
// Alert
"EPEditSetting.Camera" = "Take Photo";
"EPEditSetting.PhotoLibrary" = "Choose from Album";
"EPEditSetting.EditNickname" = "Edit Nickname";
"EPEditSetting.EnterNickname" = "Enter new nickname";
"EPEditSetting.LogoutConfirm" = "Are you sure you want to log out?";
"EPEditSetting.Cancel" = "Cancel";
"EPEditSetting.Confirm" = "Confirm";