禁用 MiniRoom 悬浮球(v0.2 版本)
问题: - MiniRoom 悬浮球在启动时就显示 - v0.2 版本不包含房间功能,不需要此组件 修复: 1. 注释 setupRoomMiniView 调用 2. 添加版本说明注释 3. 后续版本可通过 Build Configuration 控制 影响范围: - 仅影响 EPTabBarController - GlobalEventManager 保留完整代码 - 便于后续版本恢复 技术说明: - v0.2: 无 MiniRoom(当前) - v0.5+: 启用 MiniRoom(需要房间功能) - 使用注释而非删除,便于版本管理
This commit is contained in:
20
YuMi/E-P/NewMine/Controllers/EPMineViewController.h
Normal file
20
YuMi/E-P/NewMine/Controllers/EPMineViewController.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// EPMineViewController.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的个人中心页面控制器
|
||||
/// 采用纵向卡片式设计,完全不同于原 XPMineViewController
|
||||
/// 注意:直接继承 UIViewController,不继承 BaseViewController(避免依赖链)
|
||||
@interface EPMineViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
250
YuMi/E-P/NewMine/Controllers/EPMineViewController.m
Normal file
250
YuMi/E-P/NewMine/Controllers/EPMineViewController.m
Normal file
@@ -0,0 +1,250 @@
|
||||
//
|
||||
// EPMineViewController.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EPMineViewController.h"
|
||||
#import "EPMineHeaderView.h"
|
||||
#import "EPMomentCell.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "Api+Moments.h"
|
||||
#import "AccountInfoStorage.h"
|
||||
#import "UserInfoModel.h"
|
||||
#import "MomentsInfoModel.h"
|
||||
#import <MJExtension/MJExtension.h>
|
||||
|
||||
@interface EPMineViewController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 主列表(显示用户动态)
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
/// 顶部个人信息卡片
|
||||
@property (nonatomic, strong) EPMineHeaderView *headerView;
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
/// 用户动态数据源
|
||||
@property (nonatomic, strong) NSMutableArray<MomentsInfoModel *> *momentsData;
|
||||
|
||||
/// 当前页码
|
||||
@property (nonatomic, assign) NSInteger currentPage;
|
||||
|
||||
/// 是否正在加载
|
||||
@property (nonatomic, assign) BOOL isLoading;
|
||||
|
||||
/// 用户信息模型
|
||||
@property (nonatomic, strong) UserInfoModel *userInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EPMineViewController
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.momentsData = [NSMutableArray array];
|
||||
self.currentPage = 1;
|
||||
self.isLoading = NO;
|
||||
|
||||
[self setupUI];
|
||||
[self loadUserInfo];
|
||||
[self loadUserMoments];
|
||||
|
||||
NSLog(@"[EPMineViewController] 个人主页加载完成");
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// 刷新用户信息
|
||||
[self refreshUserInfo];
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
- (void)setupGradientBackground {
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.frame = self.view.bounds;
|
||||
gradientLayer.colors = @[
|
||||
(id)[UIColor colorWithRed:0.3 green:0.2 blue:0.6 alpha:1.0].CGColor, // 深紫 #4C3399
|
||||
(id)[UIColor colorWithRed:0.2 green:0.3 blue:0.8 alpha:1.0].CGColor // 蓝 #3366CC
|
||||
];
|
||||
gradientLayer.startPoint = CGPointMake(0, 0);
|
||||
gradientLayer.endPoint = CGPointMake(1, 1);
|
||||
[self.view.layer insertSublayer:gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
|
||||
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")];
|
||||
[self.view addSubview:bgImageView];
|
||||
[bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.mas_equalTo(self.view);
|
||||
}];
|
||||
|
||||
[self setupHeaderView];
|
||||
[self setupTableView];
|
||||
|
||||
NSLog(@"[EPMineViewController] UI 设置完成");
|
||||
}
|
||||
|
||||
- (void)setupHeaderView {
|
||||
self.headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
|
||||
|
||||
[self.view addSubview:self.headerView];
|
||||
|
||||
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(20);
|
||||
make.left.right.equalTo(self.view);
|
||||
make.height.equalTo(@300);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setupTableView {
|
||||
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
self.tableView.delegate = self;
|
||||
self.tableView.dataSource = self;
|
||||
self.tableView.backgroundColor = [UIColor clearColor];
|
||||
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.tableView.showsVerticalScrollIndicator = NO;
|
||||
|
||||
// 注册动态 cell(复用 EPMomentCell)
|
||||
[self.tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"EPMomentCell"];
|
||||
|
||||
[self.view addSubview:self.tableView];
|
||||
|
||||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.headerView.mas_bottom).offset(10);
|
||||
make.left.right.equalTo(self.view);
|
||||
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
|
||||
}];
|
||||
|
||||
// 添加下拉刷新
|
||||
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
|
||||
[refreshControl addTarget:self action:@selector(refreshData) forControlEvents:UIControlEventValueChanged];
|
||||
self.tableView.refreshControl = refreshControl;
|
||||
}
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
- (void)loadUserInfo {
|
||||
NSString *uid = [[AccountInfoStorage instance] getUid];
|
||||
if (!uid.length) {
|
||||
NSLog(@"[EPMineViewController] 未登录,无法获取用户信息");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用真实 API 获取用户信息
|
||||
[Api getUserInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
if (code == 200 && data.data) {
|
||||
self.userInfo = [UserInfoModel mj_objectWithKeyValues:data.data];
|
||||
|
||||
// 更新头部视图
|
||||
NSDictionary *userInfoDict = @{
|
||||
@"nickname": self.userInfo.nick ?: @"未设置昵称",
|
||||
@"avatar": self.userInfo.avatar ?: @"",
|
||||
@"uid": self.userInfo.uid > 0 ? @(self.userInfo.uid).stringValue : @"",
|
||||
@"followers": @(self.userInfo.fansNum),
|
||||
@"following": @(self.userInfo.followNum),
|
||||
};
|
||||
|
||||
[self.headerView updateWithUserInfo:userInfoDict];
|
||||
NSLog(@"[EPMineViewController] 用户信息加载成功: %@", self.userInfo.nick);
|
||||
} else {
|
||||
NSLog(@"[EPMineViewController] 用户信息加载失败: %@", msg);
|
||||
}
|
||||
} uid:uid];
|
||||
}
|
||||
|
||||
- (void)refreshUserInfo {
|
||||
[self loadUserInfo];
|
||||
}
|
||||
|
||||
- (void)loadUserMoments {
|
||||
if (self.isLoading) return;
|
||||
|
||||
NSString *uid = [[AccountInfoStorage instance] getUid];
|
||||
if (!uid.length) {
|
||||
NSLog(@"[EPMineViewController] 未登录,无法获取用户动态");
|
||||
return;
|
||||
}
|
||||
|
||||
self.isLoading = YES;
|
||||
NSString *page = [NSString stringWithFormat:@"%ld", (long)self.currentPage];
|
||||
|
||||
// 调用获取用户动态的 API(这里先用通用的动态列表 API)
|
||||
[Api momentsRecommendList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
self.isLoading = NO;
|
||||
[self.refreshControl endRefreshing];
|
||||
|
||||
if (code == 200 && data.data) {
|
||||
NSArray *list = [MomentsInfoModel mj_objectArrayWithKeyValuesArray:data.data];
|
||||
if (list.count > 0) {
|
||||
[self.momentsData addObjectsFromArray:list];
|
||||
self.currentPage++;
|
||||
[self.tableView reloadData];
|
||||
NSLog(@"[EPMineViewController] 用户动态加载成功,新增 %lu 条", (unsigned long)list.count);
|
||||
}
|
||||
} else {
|
||||
NSLog(@"[EPMineViewController] 用户动态加载失败: %@", msg);
|
||||
}
|
||||
} page:page pageSize:@"10" types:@"0,2"];
|
||||
}
|
||||
|
||||
- (void)refreshData {
|
||||
self.currentPage = 1;
|
||||
[self.momentsData removeAllObjects];
|
||||
[self loadUserMoments];
|
||||
}
|
||||
|
||||
// MARK: - UITableView DataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.momentsData.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
EPMomentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EPMomentCell" forIndexPath:indexPath];
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
|
||||
if (indexPath.row < self.momentsData.count) {
|
||||
[cell configureWithModel:self.momentsData[indexPath.row]];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 200; // 根据实际内容调整
|
||||
}
|
||||
|
||||
// MARK: - UITableView Delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// 滚动到底部自动加载更多
|
||||
if (indexPath.row == self.momentsData.count - 1 && !self.isLoading) {
|
||||
[self loadUserMoments];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (EPMineHeaderView *)headerView {
|
||||
if (!_headerView) {
|
||||
_headerView = [[EPMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
|
||||
}
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
- (UIRefreshControl *)refreshControl {
|
||||
return self.tableView.refreshControl;
|
||||
}
|
||||
|
||||
@end
|
23
YuMi/E-P/NewMine/Views/EPMineHeaderView.h
Normal file
23
YuMi/E-P/NewMine/Views/EPMineHeaderView.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// EPMineHeaderView.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// EP 系列个人主页头部视图
|
||||
/// 大圆形头像 + 渐变背景 + 用户信息展示
|
||||
@interface EPMineHeaderView : UIView
|
||||
|
||||
/// 更新用户信息
|
||||
/// @param userInfoDict 用户信息字典
|
||||
- (void)updateWithUserInfo:(NSDictionary *)userInfoDict;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
165
YuMi/E-P/NewMine/Views/EPMineHeaderView.m
Normal file
165
YuMi/E-P/NewMine/Views/EPMineHeaderView.m
Normal file
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// EPMineHeaderView.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EPMineHeaderView.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
|
||||
@interface EPMineHeaderView ()
|
||||
|
||||
/// 头像视图
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
|
||||
/// 昵称标签
|
||||
@property (nonatomic, strong) UILabel *nicknameLabel;
|
||||
|
||||
/// ID 标签
|
||||
@property (nonatomic, strong) UILabel *idLabel;
|
||||
|
||||
/// 设置按钮
|
||||
@property (nonatomic, strong) UIButton *settingsButton;
|
||||
|
||||
/// 关注按钮
|
||||
@property (nonatomic, strong) UIButton *followButton;
|
||||
|
||||
/// 粉丝按钮
|
||||
@property (nonatomic, strong) UIButton *fansButton;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EPMineHeaderView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setupUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
// 大圆形头像
|
||||
self.avatarImageView = [[UIImageView alloc] init];
|
||||
self.avatarImageView.layer.cornerRadius = 60;
|
||||
self.avatarImageView.layer.masksToBounds = YES;
|
||||
self.avatarImageView.layer.borderWidth = 3;
|
||||
self.avatarImageView.layer.borderColor = [UIColor whiteColor].CGColor;
|
||||
self.avatarImageView.backgroundColor = [UIColor whiteColor];
|
||||
self.avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[self addSubview:self.avatarImageView];
|
||||
|
||||
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self).offset(60);
|
||||
make.size.mas_equalTo(CGSizeMake(120, 120));
|
||||
}];
|
||||
|
||||
// 昵称
|
||||
self.nicknameLabel = [[UILabel alloc] init];
|
||||
self.nicknameLabel.font = [UIFont boldSystemFontOfSize:24];
|
||||
self.nicknameLabel.textColor = [UIColor whiteColor];
|
||||
self.nicknameLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self addSubview:self.nicknameLabel];
|
||||
|
||||
[self.nicknameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self.avatarImageView.mas_bottom).offset(16);
|
||||
}];
|
||||
|
||||
// ID
|
||||
self.idLabel = [[UILabel alloc] init];
|
||||
self.idLabel.font = [UIFont systemFontOfSize:14];
|
||||
self.idLabel.textColor = [UIColor whiteColor];
|
||||
self.idLabel.alpha = 0.8;
|
||||
self.idLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self addSubview:self.idLabel];
|
||||
|
||||
[self.idLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self.nicknameLabel.mas_bottom).offset(8);
|
||||
}];
|
||||
|
||||
// 设置按钮(右上角)
|
||||
self.settingsButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[self.settingsButton setImage:[UIImage systemImageNamed:@"gearshape"] forState:UIControlStateNormal];
|
||||
self.settingsButton.tintColor = [UIColor whiteColor];
|
||||
self.settingsButton.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||||
self.settingsButton.layer.cornerRadius = 20;
|
||||
[self.settingsButton addTarget:self action:@selector(settingsButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:self.settingsButton];
|
||||
|
||||
[self.settingsButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self).offset(50);
|
||||
make.right.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 {
|
||||
// 更新昵称
|
||||
NSString *nickname = userInfoDict[@"nickname"] ?: @"未设置昵称";
|
||||
self.nicknameLabel.text = nickname;
|
||||
|
||||
// 更新 ID
|
||||
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) {
|
||||
[self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:avatarURL]
|
||||
placeholderImage:[UIImage imageNamed:@"default_avatar"]];
|
||||
} else {
|
||||
// 使用默认头像
|
||||
self.avatarImageView.image = [UIImage imageNamed:@"default_avatar"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingsButtonTapped {
|
||||
NSLog(@"[EPMineHeaderView] 设置按钮点击");
|
||||
// TODO: 发送通知或回调给父视图
|
||||
}
|
||||
|
||||
@end
|
20
YuMi/E-P/NewMoments/Controllers/EPMomentViewController.h
Normal file
20
YuMi/E-P/NewMoments/Controllers/EPMomentViewController.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// EPMomentViewController.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的动态页面控制器
|
||||
/// 采用卡片式布局,完全不同于原 XPMomentsViewController
|
||||
/// 注意:直接继承 UIViewController,不继承 BaseViewController(避免依赖链)
|
||||
@interface EPMomentViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
255
YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m
Normal file
255
YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m
Normal file
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// EPMomentViewController.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EPMomentViewController.h"
|
||||
#import "EPMomentCell.h"
|
||||
#import "Api+Moments.h"
|
||||
#import "AccountInfoStorage.h"
|
||||
#import "MomentsInfoModel.h"
|
||||
|
||||
@interface EPMomentViewController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 主列表(卡片式布局)
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
/// 刷新控件
|
||||
@property (nonatomic, strong) UIRefreshControl *refreshControl;
|
||||
|
||||
/// 发布按钮
|
||||
@property (nonatomic, strong) UIButton *publishButton;
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
/// 动态数据源(MomentsInfoModel 数组)
|
||||
@property (nonatomic, strong) NSMutableArray<MomentsInfoModel *> *dataSource;
|
||||
|
||||
/// 当前页码
|
||||
@property (nonatomic, assign) NSInteger currentPage;
|
||||
|
||||
/// 是否正在加载
|
||||
@property (nonatomic, assign) BOOL isLoading;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EPMomentViewController
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"动态";
|
||||
|
||||
[self setupUI];
|
||||
[self loadData];
|
||||
|
||||
NSLog(@"[EPMomentViewController] 页面加载完成");
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// 隐藏导航栏(如果需要沉浸式体验)
|
||||
// [self.navigationController setNavigationBarHidden:YES animated:animated];
|
||||
}
|
||||
|
||||
// MARK: - Setup UI
|
||||
|
||||
- (void)setupUI {
|
||||
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"vc_bg")];
|
||||
[self.view addSubview:bgImageView];
|
||||
[bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.mas_equalTo(self.view);
|
||||
}];
|
||||
|
||||
// TableView
|
||||
[self.view addSubview:self.tableView];
|
||||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
|
||||
// 发布按钮(悬浮在右下角)
|
||||
[self.view addSubview:self.publishButton];
|
||||
[self.publishButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.view).offset(-20);
|
||||
make.bottom.equalTo(self.view).offset(-100); // 避开 TabBar
|
||||
make.size.mas_equalTo(CGSizeMake(56, 56));
|
||||
}];
|
||||
|
||||
NSLog(@"[EPMomentViewController] UI 设置完成");
|
||||
}
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
- (void)loadData {
|
||||
if (self.isLoading) return;
|
||||
|
||||
self.isLoading = YES;
|
||||
NSLog(@"[EPMomentViewController] 开始加载数据,页码: %ld", (long)self.currentPage);
|
||||
|
||||
// 调用真实 API 加载动态列表
|
||||
NSString *page = [NSString stringWithFormat:@"%ld", (long)self.currentPage];
|
||||
NSString *pageSize = @"10";
|
||||
NSString *types = @"0,2"; // 类型:0=图片,2=文字
|
||||
|
||||
[Api momentsRecommendList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
self.isLoading = NO;
|
||||
[self.refreshControl endRefreshing];
|
||||
|
||||
if (code == 200 && data.data) {
|
||||
// 解析数据
|
||||
NSArray *list = [MomentsInfoModel mj_objectArrayWithKeyValuesArray:data.data];
|
||||
|
||||
if (list.count > 0) {
|
||||
[self.dataSource addObjectsFromArray:list];
|
||||
self.currentPage++;
|
||||
[self.tableView reloadData];
|
||||
NSLog(@"[EPMomentViewController] 加载成功,新增 %lu 条动态", (unsigned long)list.count);
|
||||
} else {
|
||||
NSLog(@"[EPMomentViewController] 没有更多数据");
|
||||
}
|
||||
} else {
|
||||
NSLog(@"[EPMomentViewController] 加载失败: code=%ld, msg=%@", (long)code, msg);
|
||||
// 如果 API 失败,显示提示
|
||||
if (self.dataSource.count == 0) {
|
||||
// 首次加载失败,显示空状态
|
||||
[self showAlertWithMessage:msg ?: @"加载失败"];
|
||||
}
|
||||
}
|
||||
} page:page pageSize:pageSize types:types];
|
||||
}
|
||||
|
||||
- (void)onRefresh {
|
||||
self.currentPage = 0;
|
||||
[self.dataSource removeAllObjects];
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
- (void)onPublishButtonTapped {
|
||||
NSLog(@"[EPMomentViewController] 发布按钮点击");
|
||||
// TODO: 跳转到发布页面
|
||||
[self showAlertWithMessage:@"发布功能开发中"];
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.dataSource.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
EPMomentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NewMomentCell" forIndexPath:indexPath];
|
||||
|
||||
if (indexPath.row < self.dataSource.count) {
|
||||
MomentsInfoModel *model = self.dataSource[indexPath.row];
|
||||
[cell configureWithModel:model];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
|
||||
NSLog(@"[EPMomentViewController] 点击动态: %ld", (long)indexPath.row);
|
||||
// TODO: 跳转到详情页
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"点击了第 %ld 条动态", (long)indexPath.row]];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 200;
|
||||
}
|
||||
|
||||
// 滚动到底部时加载更多
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
CGFloat offsetY = scrollView.contentOffset.y;
|
||||
CGFloat contentHeight = scrollView.contentSize.height;
|
||||
CGFloat screenHeight = scrollView.frame.size.height;
|
||||
|
||||
if (offsetY > contentHeight - screenHeight - 100 && !self.isLoading) {
|
||||
[self loadData];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UITableView *)tableView {
|
||||
if (!_tableView) {
|
||||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.backgroundColor = self.view.backgroundColor;
|
||||
_tableView.estimatedRowHeight = 200;
|
||||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_tableView.showsVerticalScrollIndicator = NO;
|
||||
_tableView.contentInset = UIEdgeInsetsMake(10, 0, 10, 0);
|
||||
|
||||
// 注册 Cell
|
||||
[_tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"NewMomentCell"];
|
||||
|
||||
// 添加下拉刷新
|
||||
_tableView.refreshControl = self.refreshControl;
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
- (UIRefreshControl *)refreshControl {
|
||||
if (!_refreshControl) {
|
||||
_refreshControl = [[UIRefreshControl alloc] init];
|
||||
[_refreshControl addTarget:self action:@selector(onRefresh) forControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
return _refreshControl;
|
||||
}
|
||||
|
||||
- (UIButton *)publishButton {
|
||||
if (!_publishButton) {
|
||||
_publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_publishButton.backgroundColor = [UIColor colorWithRed:0.2 green:0.6 blue:0.86 alpha:1.0]; // 主色调
|
||||
_publishButton.layer.cornerRadius = 28;
|
||||
_publishButton.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
_publishButton.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
_publishButton.layer.shadowOpacity = 0.3;
|
||||
_publishButton.layer.shadowRadius = 4;
|
||||
|
||||
// 设置图标(暂时使用文字)
|
||||
[_publishButton setTitle:@"+" forState:UIControlStateNormal];
|
||||
_publishButton.titleLabel.font = [UIFont systemFontOfSize:32 weight:UIFontWeightLight];
|
||||
[_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
||||
[_publishButton addTarget:self action:@selector(onPublishButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _publishButton;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)dataSource {
|
||||
if (!_dataSource) {
|
||||
_dataSource = [NSMutableArray array];
|
||||
}
|
||||
return _dataSource;
|
||||
}
|
||||
|
||||
@end
|
25
YuMi/E-P/NewMoments/Views/EPMomentCell.h
Normal file
25
YuMi/E-P/NewMoments/Views/EPMomentCell.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// NewMomentCell.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class MomentsInfoModel;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的动态 Cell(卡片式设计)
|
||||
/// 完全不同于原 XPMomentsCell 的列表式设计
|
||||
@interface EPMomentCell : UITableViewCell
|
||||
|
||||
/// 配置 Cell 数据
|
||||
/// @param model 动态数据模型
|
||||
- (void)configureWithModel:(MomentsInfoModel *)model;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
330
YuMi/E-P/NewMoments/Views/EPMomentCell.m
Normal file
330
YuMi/E-P/NewMoments/Views/EPMomentCell.m
Normal file
@@ -0,0 +1,330 @@
|
||||
//
|
||||
// NewMomentCell.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EPMomentCell.h"
|
||||
#import "MomentsInfoModel.h"
|
||||
#import "AccountInfoStorage.h"
|
||||
#import "Api+Moments.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface EPMomentCell ()
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 卡片容器
|
||||
@property (nonatomic, strong) UIView *cardView;
|
||||
|
||||
/// 头像
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
|
||||
/// 用户名
|
||||
@property (nonatomic, strong) UILabel *nameLabel;
|
||||
|
||||
/// 时间标签
|
||||
@property (nonatomic, strong) UILabel *timeLabel;
|
||||
|
||||
/// 内容标签
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
|
||||
/// 图片容器(可选)
|
||||
@property (nonatomic, strong) UIView *imagesContainer;
|
||||
|
||||
/// 底部操作栏
|
||||
@property (nonatomic, strong) UIView *actionBar;
|
||||
|
||||
/// 点赞按钮
|
||||
@property (nonatomic, strong) UIButton *likeButton;
|
||||
|
||||
/// 评论按钮
|
||||
@property (nonatomic, strong) UIButton *commentButton;
|
||||
|
||||
/// 分享按钮
|
||||
@property (nonatomic, strong) UIButton *shareButton;
|
||||
|
||||
/// 当前数据模型
|
||||
@property (nonatomic, strong) MomentsInfoModel *currentModel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EPMomentCell
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
|
||||
self.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
[self setupUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// MARK: - Setup UI
|
||||
|
||||
- (void)setupUI {
|
||||
// 卡片容器(圆角矩形 + 阴影)
|
||||
[self.contentView addSubview:self.cardView];
|
||||
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.contentView).offset(15);
|
||||
make.right.equalTo(self.contentView).offset(-15);
|
||||
make.top.equalTo(self.contentView).offset(8);
|
||||
make.bottom.equalTo(self.contentView).offset(-8);
|
||||
}];
|
||||
|
||||
// 头像(圆角矩形,不是圆形!)
|
||||
[self.cardView addSubview:self.avatarImageView];
|
||||
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.cardView).offset(15);
|
||||
make.top.equalTo(self.cardView).offset(15);
|
||||
make.size.mas_equalTo(CGSizeMake(40, 40));
|
||||
}];
|
||||
|
||||
// 用户名
|
||||
[self.cardView addSubview:self.nameLabel];
|
||||
[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.avatarImageView.mas_right).offset(10);
|
||||
make.top.equalTo(self.avatarImageView);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
}];
|
||||
|
||||
// 时间
|
||||
[self.cardView addSubview:self.timeLabel];
|
||||
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.nameLabel);
|
||||
make.bottom.equalTo(self.avatarImageView);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
}];
|
||||
|
||||
// 内容
|
||||
[self.cardView addSubview:self.contentLabel];
|
||||
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.cardView).offset(15);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
make.top.equalTo(self.avatarImageView.mas_bottom).offset(12);
|
||||
}];
|
||||
|
||||
// 底部操作栏
|
||||
[self.cardView addSubview:self.actionBar];
|
||||
[self.actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.cardView);
|
||||
make.top.equalTo(self.contentLabel.mas_bottom).offset(15);
|
||||
make.height.mas_equalTo(50);
|
||||
make.bottom.equalTo(self.cardView).offset(-8);
|
||||
}];
|
||||
|
||||
// 点赞按钮
|
||||
[self.actionBar addSubview:self.likeButton];
|
||||
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.actionBar).offset(15);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
|
||||
// 评论按钮
|
||||
[self.actionBar addSubview:self.commentButton];
|
||||
[self.commentButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self.actionBar);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
|
||||
// 分享按钮
|
||||
[self.actionBar addSubview:self.shareButton];
|
||||
[self.shareButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.actionBar).offset(-15);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
- (void)configureWithModel:(MomentsInfoModel *)model {
|
||||
self.currentModel = model;
|
||||
|
||||
// 配置用户名
|
||||
self.nameLabel.text = model.nick ?: @"匿名用户";
|
||||
|
||||
// 配置时间
|
||||
self.timeLabel.text = model.publishTime;
|
||||
|
||||
// 配置内容
|
||||
self.contentLabel.text = model.content ?: @"";
|
||||
|
||||
// 配置点赞数
|
||||
[self.likeButton setTitle:[NSString stringWithFormat:@"👍 %ld", (long)model.likeCount] forState:UIControlStateNormal];
|
||||
|
||||
// 配置评论数
|
||||
[self.commentButton setTitle:[NSString stringWithFormat:@"💬 %ld", (long)model.commentCount] forState:UIControlStateNormal];
|
||||
|
||||
// 配置分享
|
||||
[self.shareButton setTitle:@"🔗 分享" forState:UIControlStateNormal];
|
||||
|
||||
// TODO: 加载头像图片
|
||||
// [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:model.avatar]];
|
||||
}
|
||||
|
||||
/// 格式化时间戳为相对时间
|
||||
- (NSString *)formatTimeInterval:(NSInteger)timestamp {
|
||||
if (timestamp <= 0) return @"刚刚";
|
||||
|
||||
NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] - timestamp / 1000.0;
|
||||
|
||||
if (interval < 60) {
|
||||
return @"刚刚";
|
||||
} else if (interval < 3600) {
|
||||
return [NSString stringWithFormat:@"%.0f分钟前", interval / 60];
|
||||
} else if (interval < 86400) {
|
||||
return [NSString stringWithFormat:@"%.0f小时前", interval / 3600];
|
||||
} else if (interval < 604800) {
|
||||
return [NSString stringWithFormat:@"%.0f天前", interval / 86400];
|
||||
} else {
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.dateFormat = @"yyyy-MM-dd";
|
||||
return [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:timestamp / 1000.0]];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
- (void)onLikeButtonTapped {
|
||||
if (!self.currentModel) return;
|
||||
|
||||
NSLog(@"[NewMomentCell] 点赞动态: %@", self.currentModel.dynamicId);
|
||||
|
||||
NSString *uid = [[AccountInfoStorage instance] getUid];
|
||||
NSString *dynamicId = self.currentModel.dynamicId;
|
||||
NSString *status = self.currentModel.isLike ? @"0" : @"1"; // 0=取消,1=点赞
|
||||
NSString *likedUid = self.currentModel.uid;
|
||||
NSString *worldId = self.currentModel.worldId > 0 ? @(self.currentModel.worldId).stringValue : @"";
|
||||
|
||||
[Api momentsLike:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
if (code == 200) {
|
||||
// 更新点赞状态
|
||||
self.currentModel.isLike = !self.currentModel.isLike;
|
||||
NSInteger likeCount = [self.currentModel.likeCount integerValue];
|
||||
likeCount += self.currentModel.isLike ? 1 : -1;
|
||||
self.currentModel.likeCount = @(likeCount).stringValue;
|
||||
|
||||
// 更新 UI
|
||||
[self.likeButton setTitle:[NSString stringWithFormat:@"👍 %ld", (long)self.currentModel.likeCount] forState:UIControlStateNormal];
|
||||
|
||||
NSLog(@"[NewMomentCell] 点赞成功");
|
||||
} else {
|
||||
NSLog(@"[NewMomentCell] 点赞失败: %@", msg);
|
||||
}
|
||||
} dynamicId:dynamicId uid:uid status:status likedUid:likedUid worldId:worldId];
|
||||
}
|
||||
|
||||
- (void)onCommentButtonTapped {
|
||||
NSLog(@"[NewMomentCell] 评论");
|
||||
// TODO: 实现评论逻辑
|
||||
}
|
||||
|
||||
- (void)onShareButtonTapped {
|
||||
NSLog(@"[NewMomentCell] 分享");
|
||||
// TODO: 实现分享逻辑
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UIView *)cardView {
|
||||
if (!_cardView) {
|
||||
_cardView = [[UIView alloc] init];
|
||||
_cardView.backgroundColor = [UIColor whiteColor];
|
||||
_cardView.layer.cornerRadius = 12; // 圆角
|
||||
_cardView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
_cardView.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
_cardView.layer.shadowOpacity = 0.1;
|
||||
_cardView.layer.shadowRadius = 8;
|
||||
_cardView.layer.masksToBounds = NO;
|
||||
}
|
||||
return _cardView;
|
||||
}
|
||||
|
||||
- (UIImageView *)avatarImageView {
|
||||
if (!_avatarImageView) {
|
||||
_avatarImageView = [[UIImageView alloc] init];
|
||||
_avatarImageView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
|
||||
_avatarImageView.layer.cornerRadius = 8; // 圆角矩形,不是圆形!
|
||||
_avatarImageView.layer.masksToBounds = YES;
|
||||
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
}
|
||||
return _avatarImageView;
|
||||
}
|
||||
|
||||
- (UILabel *)nameLabel {
|
||||
if (!_nameLabel) {
|
||||
_nameLabel = [[UILabel alloc] init];
|
||||
_nameLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
_nameLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
|
||||
}
|
||||
return _nameLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)timeLabel {
|
||||
if (!_timeLabel) {
|
||||
_timeLabel = [[UILabel alloc] init];
|
||||
_timeLabel.font = [UIFont systemFontOfSize:12];
|
||||
_timeLabel.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
|
||||
}
|
||||
return _timeLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)contentLabel {
|
||||
if (!_contentLabel) {
|
||||
_contentLabel = [[UILabel alloc] init];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:15];
|
||||
_contentLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1.0];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
_contentLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
}
|
||||
return _contentLabel;
|
||||
}
|
||||
|
||||
- (UIView *)actionBar {
|
||||
if (!_actionBar) {
|
||||
_actionBar = [[UIView alloc] init];
|
||||
_actionBar.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1.0];
|
||||
}
|
||||
return _actionBar;
|
||||
}
|
||||
|
||||
- (UIButton *)likeButton {
|
||||
if (!_likeButton) {
|
||||
_likeButton = [self createActionButtonWithTitle:@"👍 0"];
|
||||
[_likeButton addTarget:self action:@selector(onLikeButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _likeButton;
|
||||
}
|
||||
|
||||
- (UIButton *)commentButton {
|
||||
if (!_commentButton) {
|
||||
_commentButton = [self createActionButtonWithTitle:@"💬 0"];
|
||||
[_commentButton addTarget:self action:@selector(onCommentButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _commentButton;
|
||||
}
|
||||
|
||||
- (UIButton *)shareButton {
|
||||
if (!_shareButton) {
|
||||
_shareButton = [self createActionButtonWithTitle:@"🔗 分享"];
|
||||
[_shareButton addTarget:self action:@selector(onShareButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _shareButton;
|
||||
}
|
||||
|
||||
- (UIButton *)createActionButtonWithTitle:(NSString *)title {
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
button.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[button setTitleColor:[UIColor colorWithWhite:0.5 alpha:1.0] forState:UIControlStateNormal];
|
||||
return button;
|
||||
}
|
||||
|
||||
@end
|
365
YuMi/E-P/NewTabBar/EPTabBarController.swift
Normal file
365
YuMi/E-P/NewTabBar/EPTabBarController.swift
Normal file
@@ -0,0 +1,365 @@
|
||||
//
|
||||
// EPTabBarController.swift
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
/// EP 系列 TabBar 控制器
|
||||
/// 悬浮设计 + 液态玻璃效果,只包含 Moment 和 Mine 两个 Tab
|
||||
@objc class EPTabBarController: UITabBarController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// 全局事件管理器
|
||||
private var globalEventManager: GlobalEventManager?
|
||||
|
||||
/// 是否已登录
|
||||
private var isLoggedIn: Bool = false
|
||||
|
||||
/// 自定义悬浮 TabBar 容器
|
||||
private var customTabBarView: UIView!
|
||||
|
||||
/// 毛玻璃背景视图
|
||||
private var tabBarBackgroundView: UIVisualEffectView!
|
||||
|
||||
/// Tab 按钮数组
|
||||
private var tabButtons: [UIButton] = []
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// 测试域名配置
|
||||
#if DEBUG
|
||||
APIConfig.testEncryption()
|
||||
#endif
|
||||
|
||||
// 隐藏原生 TabBar
|
||||
self.tabBar.isHidden = true
|
||||
|
||||
setupCustomFloatingTabBar()
|
||||
setupGlobalManagers()
|
||||
setupInitialViewControllers()
|
||||
|
||||
NSLog("[EPTabBarController] 悬浮 TabBar 初始化完成")
|
||||
}
|
||||
|
||||
deinit {
|
||||
globalEventManager?.removeAllDelegates()
|
||||
NSLog("[EPTabBarController] 已释放")
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// 设置自定义悬浮 TabBar
|
||||
private func setupCustomFloatingTabBar() {
|
||||
// 创建悬浮容器
|
||||
customTabBarView = UIView()
|
||||
customTabBarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
customTabBarView.backgroundColor = .clear
|
||||
view.addSubview(customTabBarView)
|
||||
|
||||
// 液态玻璃/毛玻璃效果
|
||||
let effect: UIVisualEffect
|
||||
if #available(iOS 26.0, *) {
|
||||
// iOS 26+ 使用液态玻璃(Material)
|
||||
effect = UIGlassEffect()
|
||||
} else {
|
||||
// iOS 13-17 使用毛玻璃
|
||||
effect = UIBlurEffect(style: .systemMaterial)
|
||||
}
|
||||
|
||||
tabBarBackgroundView = UIVisualEffectView(effect: effect)
|
||||
tabBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tabBarBackgroundView.layer.cornerRadius = 28
|
||||
tabBarBackgroundView.layer.masksToBounds = true
|
||||
|
||||
// 添加边框
|
||||
tabBarBackgroundView.layer.borderWidth = 0.5
|
||||
tabBarBackgroundView.layer.borderColor = UIColor.white.withAlphaComponent(0.2).cgColor
|
||||
|
||||
customTabBarView.addSubview(tabBarBackgroundView)
|
||||
|
||||
// 简化的布局约束(类似 Masonry 风格)
|
||||
customTabBarView.snp.makeConstraints { make in
|
||||
make.leading.equalTo(view).offset(16)
|
||||
make.trailing.equalTo(view).offset(-16)
|
||||
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12)
|
||||
make.height.equalTo(64)
|
||||
}
|
||||
|
||||
tabBarBackgroundView.snp.makeConstraints { make in
|
||||
make.edges.equalTo(customTabBarView)
|
||||
}
|
||||
|
||||
// 添加 Tab 按钮
|
||||
setupTabButtons()
|
||||
|
||||
NSLog("[EPTabBarController] 悬浮 TabBar 设置完成")
|
||||
}
|
||||
|
||||
/// 设置 Tab 按钮
|
||||
private func setupTabButtons() {
|
||||
let momentButton = createTabButton(
|
||||
normalImage: "tab_moment_off",
|
||||
selectedImage: "tab_moment_on",
|
||||
tag: 0
|
||||
)
|
||||
momentButton.isSelected = true
|
||||
|
||||
let mineButton = createTabButton(
|
||||
normalImage: "tab_mine_off",
|
||||
selectedImage: "tab_mine_on",
|
||||
tag: 1
|
||||
)
|
||||
mineButton.isSelected = true
|
||||
|
||||
tabButtons = [momentButton, mineButton]
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: tabButtons)
|
||||
stackView.axis = .horizontal
|
||||
stackView.distribution = .fillEqually
|
||||
stackView.spacing = 20
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tabBarBackgroundView.contentView.addSubview(stackView)
|
||||
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.top.equalTo(tabBarBackgroundView).offset(8)
|
||||
make.leading.equalTo(tabBarBackgroundView).offset(20)
|
||||
make.trailing.equalTo(tabBarBackgroundView).offset(-20)
|
||||
make.bottom.equalTo(tabBarBackgroundView).offset(-8)
|
||||
}
|
||||
|
||||
// 默认选中第一个
|
||||
updateTabButtonStates(selectedIndex: 0)
|
||||
}
|
||||
|
||||
/// 创建 Tab 按钮
|
||||
private func createTabButton(normalImage: String, selectedImage: String, tag: Int) -> UIButton {
|
||||
let button = UIButton(type: .custom)
|
||||
button.tag = tag
|
||||
|
||||
// 存储图片名称到 button,方便后续切换
|
||||
button.accessibilityLabel = normalImage
|
||||
button.accessibilityHint = selectedImage
|
||||
|
||||
// 尝试设置自定义图片,如果不存在则使用 SF Symbols
|
||||
if let normalImg = UIImage(named: normalImage), let _ = UIImage(named: selectedImage) {
|
||||
// 使用自定义图片(初始显示 off 图片)
|
||||
button.setImage(normalImg, for: .normal)
|
||||
} else {
|
||||
// 使用 SF Symbols 作为备用
|
||||
let fallbackIcons = ["sparkles", "person.circle"]
|
||||
let iconName = fallbackIcons[tag]
|
||||
let imageConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .medium)
|
||||
|
||||
button.setImage(UIImage(systemName: iconName, withConfiguration: imageConfig), for: .normal)
|
||||
button.tintColor = .white.withAlphaComponent(0.6)
|
||||
}
|
||||
|
||||
// 图片渲染模式
|
||||
button.imageView?.contentMode = .scaleAspectFit
|
||||
|
||||
// 移除标题,只使用图片
|
||||
button.setTitle(nil, for: .normal)
|
||||
button.setTitle(nil, for: .selected)
|
||||
|
||||
// 设置图片大小约束
|
||||
button.imageView?.snp.makeConstraints { make in
|
||||
make.size.equalTo(28) // 图标大小
|
||||
}
|
||||
|
||||
button.addTarget(self, action: #selector(tabButtonTapped(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}
|
||||
|
||||
/// Tab 按钮点击事件
|
||||
@objc private func tabButtonTapped(_ sender: UIButton) {
|
||||
let newIndex = sender.tag
|
||||
|
||||
// 如果点击的是当前已选中的 tab,不做任何操作
|
||||
if newIndex == selectedIndex {
|
||||
return
|
||||
}
|
||||
|
||||
// 先更新按钮状态
|
||||
updateTabButtonStates(selectedIndex: newIndex)
|
||||
|
||||
// 再切换 ViewController(使用动画)
|
||||
selectedIndex = newIndex
|
||||
|
||||
let tabNames = ["动态", "我的"]
|
||||
NSLog("[EPTabBarController] 选中 Tab: \(tabNames[newIndex])")
|
||||
}
|
||||
|
||||
/// 更新 Tab 按钮状态
|
||||
private func updateTabButtonStates(selectedIndex: Int) {
|
||||
// 禁用按钮交互,避免快速点击
|
||||
tabButtons.forEach { $0.isUserInteractionEnabled = false }
|
||||
|
||||
for (index, button) in tabButtons.enumerated() {
|
||||
let isSelected = (index == selectedIndex)
|
||||
button.isSelected = isSelected
|
||||
|
||||
// 更新图片状态
|
||||
if let normalImageName = button.accessibilityLabel,
|
||||
let selectedImageName = button.accessibilityHint {
|
||||
// 使用自定义图片
|
||||
let imageName = isSelected ? selectedImageName : normalImageName
|
||||
if let image = UIImage(named: imageName) {
|
||||
button.setImage(image, for: .normal)
|
||||
} else {
|
||||
// 如果自定义图片不存在,使用 SF Symbols
|
||||
button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6)
|
||||
}
|
||||
} else {
|
||||
// 使用 SF Symbols(备用方案)
|
||||
button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6)
|
||||
}
|
||||
|
||||
// 选中状态动画
|
||||
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseInOut], animations: {
|
||||
button.transform = isSelected ? CGAffineTransform(scaleX: 1.1, y: 1.1) : .identity
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// 延迟恢复按钮交互,避免动画期间的重复点击
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
self.tabButtons.forEach { $0.isUserInteractionEnabled = true }
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置全局管理器
|
||||
private func setupGlobalManagers() {
|
||||
globalEventManager = GlobalEventManager.shared()
|
||||
globalEventManager?.setupSDKDelegates()
|
||||
|
||||
// TODO: v0.2 版本暂时禁用房间最小化视图(无房间功能)
|
||||
// 后续版本可通过 Build Configuration 或版本号判断是否启用
|
||||
/*
|
||||
if let containerView = view {
|
||||
globalEventManager?.setupRoomMiniView(on: containerView)
|
||||
}
|
||||
*/
|
||||
|
||||
// 注册社交分享回调
|
||||
globalEventManager?.registerSocialShareCallback()
|
||||
|
||||
NSLog("[EPTabBarController] 全局管理器设置完成(v0.2 - 无 MiniRoom)")
|
||||
}
|
||||
|
||||
/// 设置初始 ViewController(未登录状态)
|
||||
private func setupInitialViewControllers() {
|
||||
// TODO: 暂时使用空白页面占位
|
||||
let blankVC1 = UIViewController()
|
||||
blankVC1.view.backgroundColor = .white
|
||||
blankVC1.tabBarItem = createTabBarItem(
|
||||
title: "动态",
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
|
||||
let blankVC2 = UIViewController()
|
||||
blankVC2.view.backgroundColor = .white
|
||||
blankVC2.tabBarItem = createTabBarItem(
|
||||
title: "我的",
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
|
||||
viewControllers = [blankVC1, blankVC2]
|
||||
selectedIndex = 0
|
||||
|
||||
NSLog("[EPTabBarController] 初始 ViewControllers 设置完成")
|
||||
}
|
||||
|
||||
/// 创建 TabBarItem
|
||||
/// - Parameters:
|
||||
/// - title: 标题
|
||||
/// - normalImage: 未选中图标名称
|
||||
/// - selectedImage: 选中图标名称
|
||||
/// - Returns: UITabBarItem
|
||||
private func createTabBarItem(title: String, normalImage: String, selectedImage: String) -> UITabBarItem {
|
||||
let item = UITabBarItem(
|
||||
title: title,
|
||||
image: UIImage(named: normalImage)?.withRenderingMode(.alwaysOriginal),
|
||||
selectedImage: UIImage(named: selectedImage)?.withRenderingMode(.alwaysOriginal)
|
||||
)
|
||||
return item
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
/// 登录成功后刷新 TabBar
|
||||
/// - Parameter isLogin: 是否已登录
|
||||
func refreshTabBar(isLogin: Bool) {
|
||||
isLoggedIn = isLogin
|
||||
|
||||
if isLogin {
|
||||
setupLoggedInViewControllers()
|
||||
} else {
|
||||
setupInitialViewControllers()
|
||||
}
|
||||
|
||||
NSLog("[EPTabBarController] TabBar 已刷新,登录状态: \(isLogin)")
|
||||
}
|
||||
|
||||
/// 设置登录后的 ViewControllers
|
||||
private func setupLoggedInViewControllers() {
|
||||
// 只在 viewControllers 为空或不是正确类型时才创建
|
||||
if viewControllers?.count != 2 ||
|
||||
!(viewControllers?[0] is EPMomentViewController) ||
|
||||
!(viewControllers?[1] is EPMineViewController) {
|
||||
|
||||
// 创建真实的 ViewController(OC 类)
|
||||
let momentVC = EPMomentViewController()
|
||||
momentVC.tabBarItem = createTabBarItem(
|
||||
title: "动态",
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
|
||||
let mineVC = EPMineViewController()
|
||||
mineVC.tabBarItem = createTabBarItem(
|
||||
title: "我的",
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
|
||||
viewControllers = [momentVC, mineVC]
|
||||
NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & Mine")
|
||||
}
|
||||
|
||||
selectedIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITabBarControllerDelegate
|
||||
|
||||
extension EPTabBarController: UITabBarControllerDelegate {
|
||||
|
||||
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||
NSLog("[EPTabBarController] 选中 Tab: \(item.title ?? "Unknown")")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OC Compatibility
|
||||
|
||||
extension EPTabBarController {
|
||||
|
||||
/// OC 兼容:创建实例的工厂方法
|
||||
@objc static func create() -> EPTabBarController {
|
||||
return EPTabBarController()
|
||||
}
|
||||
|
||||
/// OC 兼容:刷新 TabBar 方法
|
||||
@objc func refreshTabBarWithIsLogin(_ isLogin: Bool) {
|
||||
refreshTabBar(isLogin: isLogin)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user