feat: 更新 EPEditSettingViewController 以增强用户信息管理功能

主要变更:
1. 在 EPEditSettingViewController 中添加了用户头像和相机图标的布局,提升用户界面友好性。
2. 引入 EPMineAPIHelper 以支持头像更新功能,简化 API 调用。
3. 优化了导航栏的显示和隐藏逻辑,确保用户体验流畅。
4. 更新了 UITableView 的数据源和布局,确保信息展示清晰。

此更新旨在提升用户体验,简化用户信息的管理和更新流程。
This commit is contained in:
edwinQQQ
2025-10-14 14:46:08 +08:00
parent e4f4557369
commit 955cc3622f
13 changed files with 392 additions and 279 deletions

View File

@@ -425,7 +425,6 @@
4C1E98C62E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */; };
4C1E98C92E9A4DFD0031AE79 /* EPQCloudConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.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 */; };
4C3851992DD5F4D50089CFCC /* EventConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3851982DD5F4D50089CFCC /* EventConfigModel.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>"; };
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>"; };
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>"; };
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>"; };
@@ -6459,8 +6456,6 @@
4C0642952E98F76F00BAF413 /* Services */ = {
isa = PBXGroup;
children = (
4C1E98CB2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.h */,
4C1E98CC2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m */,
4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */,
);
path = Services;
@@ -12766,7 +12761,6 @@
E8751E5F28A62A970056EF44 /* XPSailingPresenter.m in Sources */,
E84A2E962A5280F900D6AF8A /* XPExchangeDiamondsView.m in Sources */,
23F9636A2BB6919D00F440A6 /* PINobleRebateModel.m in Sources */,
4C1E98CD2E9A69B20031AE79 /* EPMomentAPIHelper_Deprecated.m in Sources */,
E8DAC5AC2858305A00012CFD /* XPRoomMessageBubbleView.m in Sources */,
1427218929A75F6F00C7C423 /* HTTPDataResponse.m in Sources */,
E8B9843028AB90200022D026 /* XPMoentsTopicListView.m in Sources */,
@@ -13789,6 +13783,7 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "YuMi/YuMi-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};

View File

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

View File

@@ -14,37 +14,48 @@ 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.cornerRadius = 60 // 120/2 = 60
imageView.layer.masksToBounds = true
imageView.backgroundColor = .systemGray5
imageView.isUserInteractionEnabled = true
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
private var settingItems: [SettingItem] = []
private var userInfo: UserInfoModel?
private var apiHelper: EPMineAPIHelper = EPMineAPIHelper()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
setupNavigationBar()
setupUI()
setupData()
loadUserInfo()
@@ -52,35 +63,96 @@ class EPEditSettingViewController: UIViewController {
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
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
//
restoreParentNavigationBarStyle()
}
// MARK: - Setup
private func setupUI() {
view.backgroundColor = UIColor(hex: "#0C0527")
private func setupNavigationBar() {
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)
tableView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.leading.trailing.bottom.equalToSuperview()
make.top.equalTo(profileImageView.snp.bottom).offset(40)
make.leading.trailing.bottom.equalTo(view)
}
//
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped))
profileImageView.addGestureRecognizer(tapGesture)
//
let cameraTapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped))
cameraIconView.addGestureRecognizer(cameraTapGesture)
}
private func setupData() {
settingItems = [
SettingItem(
@@ -109,9 +181,17 @@ class EPEditSettingViewController: UIViewController {
action: { [weak self] in self?.handleReservedAction("AboutUs") }
)
]
NSLog("[EPEditSetting] setupData 完成,设置项数量: \(settingItems.count)")
}
private func loadUserInfo() {
// EPMineViewController
if userInfo != nil {
updateProfileImage()
tableView.reloadData()
return
}
//
guard let uid = AccountInfoStorage.instance().getUid(), !uid.isEmpty else {
print("[EPEditSetting] 未登录,无法获取用户信息")
@@ -307,6 +387,16 @@ class EPEditSettingViewController: UIViewController {
alert.addAction(UIAlertAction(title: "OK", style: .default))
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
@@ -314,15 +404,13 @@ class EPEditSettingViewController: UIViewController {
extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2 // section + section
return 1 // section
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 2 // +
} else {
return settingItems.count
}
let count = settingItems.count + 1 // +1 for nickname row
NSLog("[EPEditSetting] TableView rows count: \(count), settingItems: \(settingItems.count)")
return count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -331,35 +419,50 @@ extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegat
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
//
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
//
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
if indexPath.row == 0 {
//
cell.textLabel?.text = YMLocalizedString("EPEditSetting.Nickname")
//
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)
}
//
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 {
// section
let item = settingItems[indexPath.row]
//
let item = settingItems[indexPath.row - 1]
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 {
cell.textLabel?.textColor = .systemRed
@@ -372,48 +475,36 @@ extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegat
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 {
return 120 //
}
return 60
return 60 // 60pt
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
if indexPath.row == 0 {
//
showAvatarSelectionSheet()
} else {
//
showNicknameEditAlert()
}
if indexPath.row == 0 {
//
showNicknameEditAlert()
} else {
//
let item = settingItems[indexPath.row]
let item = settingItems[indexPath.row - 1]
item.action()
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return section == 0 ? 20 : 10
return 0
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(hex: "#0C0527")
return view
return nil
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10
return 0
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(hex: "#0C0527")
return view
return nil
}
}
@@ -441,41 +532,75 @@ extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINaviga
}
private func uploadAvatar(_ image: UIImage) {
//
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
print("[EPEditSetting] 图片压缩失败")
return
}
//
EPProgressHUD.showProgress(0, total: 1)
//
let format = "jpg"
let name = "image/\(UUID().uuidString).\(format)"
// 使 EPSDKManager OCR
EPSDKManager.shared.uploadImages([image],
progress: { uploaded, total in
EPProgressHUD.showProgress(uploaded, total: total)
},
success: { [weak self] resList in
EPProgressHUD.dismiss()
//
UploadFile.share().qCloudUploadImage(imageData, named: name, success: { [weak self] (key, resp) in
print("[EPEditSetting] 头像上传成功: \(key)")
guard !resList.isEmpty,
let firstRes = resList.first,
let avatarUrl = firstRes["resUrl"] as? String else {
print("[EPEditSetting] 头像上传成功但无法获取URL")
// API
self?.updateAvatarAPI(avatarUrl: key)
return
}
}, failure: { (resCode, message) in
print("[EPEditSetting] 头像上传失败: \(message)")
})
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) {
// API
Api.userV2UploadAvatar({ [weak self] (data, code, msg) in
// 使 API Helper
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 {
if code == 200 {
print("[EPEditSetting] 头像更新成功")
//
self?.userInfo?.avatar = avatarUrl
} else {
print("[EPEditSetting] 头像更新失败: \(String(describing: msg))")
}
let alert = UIAlertController(
title: "更新失败",
message: msg ?? "头像更新失败,请稍后重试",
preferredStyle: .alert
)
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 {
[super viewDidLoad];
[self setupUI];
NSLog(@"[EPMineViewController] viewDidLoad 完成");
@@ -49,10 +48,7 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//
[self.navigationController setNavigationBarHidden:YES animated:animated];
//
[self loadUserDetailInfo];
}
@@ -60,8 +56,13 @@
// MARK: - Setup
- (void)setupUI {
//
self.view.backgroundColor = [UIColor colorWithRed:0.047 green:0.020 blue:0.153 alpha:1.0]; // #0C0527
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 setupHeaderView];
[self setupMomentListView];
@@ -73,19 +74,25 @@
self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.headerView];
// 使
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]
]];
// 使 Masonry
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.view);
make.leading.mas_equalTo(self.view);
make.trailing.mas_equalTo(self.view);
make.height.mas_equalTo(kGetScaleWidth(320));
}];
//
//
__weak typeof(self) weakSelf = self;
self.headerView.onSettingsButtonTapped = ^{
__strong typeof(weakSelf) self = weakSelf;
[self openSettings];
};
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(openSettings)
name:@"EPMineHeaderSettingsButtonTapped"
selector:@selector(onAvatarUpdated:)
name:@"EPEditSettingAvatarUpdated"
object:nil];
}
@@ -93,14 +100,13 @@
self.momentListView = [[EPMomentListView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.momentListView];
// 使
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]
]];
// 使 Masonry
[self.momentListView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.headerView.mas_bottom);
make.bottom.mas_equalTo(self.view);
make.leading.mas_equalTo(self.view);
make.trailing.mas_equalTo(self.view);
}];
}
// MARK: - Data Loading
@@ -172,13 +178,37 @@
// MARK: - Actions
- (void)openSettings {
//
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@""
style:UIBarButtonItemStylePlain
target:nil
action:nil];
EPEditSettingViewController *settingsVC = [[EPEditSettingViewController alloc] init];
//
if (self.userInfo) {
[settingsVC updateWithUserInfo:self.userInfo];
}
[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 {
[[NSNotificationCenter defaultCenter] removeObserver:self];
// 使 block
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"EPEditSettingAvatarUpdated" object:nil];
}
@end

View File

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

View File

@@ -38,5 +38,17 @@
} 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];
}
@end

View File

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

View File

@@ -24,12 +24,6 @@
///
@property (nonatomic, strong) UIButton *settingsButton;
///
@property (nonatomic, strong) UIButton *followButton;
///
@property (nonatomic, strong) UIButton *fansButton;
@end
@implementation EPMineHeaderView
@@ -97,36 +91,6 @@
make.trailing.equalTo(self).offset(-20);
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 {
@@ -138,14 +102,6 @@
NSString *uid = userInfoDict[@"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"];
if (avatarURL && avatarURL.length > 0) {
@@ -159,8 +115,10 @@
- (void)settingsButtonTapped {
NSLog(@"[EPMineHeaderView] 设置按钮点击");
//
[[NSNotificationCenter defaultCenter] postNotificationName:@"EPMineHeaderSettingsButtonTapped" object:nil];
// 使 block
if (self.onSettingsButtonTapped) {
self.onSettingsButtonTapped();
}
}
@end

View File

@@ -54,9 +54,6 @@
// MARK: - Setup UI
- (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")];
bgImageView.contentMode = UIViewContentModeScaleAspectFill;
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

@@ -361,8 +361,51 @@ import SnapKit
normalImage: normalImage,
selectedImage: selectedImage
)
// delegate
nav.delegate = self
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
@@ -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
extension EPTabBarController {

View File

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