
主要变更: 1. 在 .gitignore 中添加了 Docs/ 文件夹,以忽略文档相关文件。 2. 删除了多个过时的文档,包括构建指南、编译修复指南和当前状态报告等。 此更新旨在清理项目文件,确保版本控制的整洁性。
254 lines
9.3 KiB
Objective-C
254 lines
9.3 KiB
Objective-C
//
|
||
// EPMomentListView.m
|
||
// YuMi
|
||
//
|
||
// Created by AI on 2025-10-10.
|
||
//
|
||
|
||
#import <UIKit/UIKit.h>
|
||
#import "EPMomentListView.h"
|
||
#import "EPMomentCell.h"
|
||
#import <MJRefresh/MJRefresh.h>
|
||
#import "YuMi-Swift.h"
|
||
#import "EPEmotionColorStorage.h"
|
||
|
||
|
||
@interface EPMomentListView () <UITableViewDelegate, UITableViewDataSource>
|
||
|
||
@property (nonatomic, strong) UITableView *tableView;
|
||
@property (nonatomic, strong) UIRefreshControl *refreshControl;
|
||
@property (nonatomic, strong) NSMutableArray *mutableRawList;
|
||
@property (nonatomic, strong) EPMomentAPISwiftHelper *api;
|
||
@property (nonatomic, assign) BOOL isLoading;
|
||
@property (nonatomic, copy) NSString *nextID;
|
||
@property (nonatomic, assign) BOOL isLocalMode;
|
||
@property (nonatomic, copy) void (^refreshCallback)(void);
|
||
@end
|
||
|
||
@implementation EPMomentListView
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame {
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
self.backgroundColor = [UIColor clearColor];
|
||
_api = [[EPMomentAPISwiftHelper alloc] init];
|
||
_mutableRawList = [NSMutableArray array];
|
||
_sourceType = EPMomentListSourceTypeRecommend;
|
||
_isLocalMode = NO; // 明确初始化为网络模式
|
||
|
||
[self addSubview:self.tableView];
|
||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.edges.equalTo(self);
|
||
}];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (NSArray<NSMutableDictionary *> *)rawList {
|
||
return [self.mutableRawList copy];
|
||
}
|
||
|
||
- (void)reloadFirstPage {
|
||
if (self.isLocalMode) {
|
||
// 本地模式:调用外部刷新回调
|
||
if (self.refreshCallback) {
|
||
self.refreshCallback();
|
||
}
|
||
[self.refreshControl endRefreshing];
|
||
return;
|
||
}
|
||
|
||
// 网络模式:重新请求第一页
|
||
self.nextID = @"";
|
||
[self.mutableRawList removeAllObjects];
|
||
[self.tableView reloadData];
|
||
[self.tableView.mj_footer resetNoMoreData];
|
||
[self requestNextPage];
|
||
}
|
||
|
||
- (void)loadWithDynamicInfo:(NSArray<MomentsInfoModel *> *)dynamicInfo
|
||
refreshCallback:(void (^)(void))refreshCallback {
|
||
self.isLocalMode = YES;
|
||
self.refreshCallback = refreshCallback;
|
||
|
||
[self.mutableRawList removeAllObjects];
|
||
if (dynamicInfo.count > 0) {
|
||
[self.mutableRawList addObjectsFromArray:dynamicInfo];
|
||
}
|
||
|
||
// 隐藏加载更多 footer
|
||
self.tableView.mj_footer.hidden = YES;
|
||
|
||
[self.tableView reloadData];
|
||
[self.refreshControl endRefreshing];
|
||
}
|
||
|
||
- (void)requestNextPage {
|
||
if (self.isLoading) return;
|
||
self.isLoading = YES;
|
||
|
||
@kWeakify(self);
|
||
[self.api fetchLatestMomentsWithNextID:self.nextID
|
||
completion:^(NSArray<MomentsInfoModel *> * _Nonnull list, NSString * _Nonnull nextMomentID) {
|
||
@kStrongify(self);
|
||
[self endLoading];
|
||
if (list.count > 0) {
|
||
// 处理情绪颜色
|
||
[self processEmotionColors:list isFirstPage:(self.nextID.length == 0)];
|
||
|
||
self.nextID = nextMomentID;
|
||
[self.mutableRawList addObjectsFromArray:list];
|
||
[self.tableView reloadData];
|
||
if (nextMomentID.length > 0) {
|
||
[self.tableView.mj_footer endRefreshing];
|
||
} else {
|
||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||
}
|
||
} else {
|
||
// 返回空数据:显示 "no more data" 状态
|
||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||
}
|
||
} failure:^(NSInteger code, NSString * _Nonnull msg) {
|
||
@kStrongify(self);
|
||
[self endLoading];
|
||
// TODO: 完全没有数据情况下,后续补充数据异常页面
|
||
[self.tableView.mj_footer endRefreshing];
|
||
}];
|
||
}
|
||
|
||
- (void)endLoading {
|
||
self.isLoading = NO;
|
||
[self.refreshControl endRefreshing];
|
||
}
|
||
|
||
/// 处理动态的情绪颜色(从 UserDefaults 匹配 + 处理临时颜色)
|
||
- (void)processEmotionColors:(NSArray<MomentsInfoModel *> *)list isFirstPage:(BOOL)isFirstPage {
|
||
// 检查是否有待处理的临时情绪颜色
|
||
NSString *pendingColor = [[NSUserDefaults standardUserDefaults] stringForKey:@"EP_Pending_Emotion_Color"];
|
||
NSNumber *pendingTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:@"EP_Pending_Emotion_Timestamp"];
|
||
|
||
for (NSInteger i = 0; i < list.count; i++) {
|
||
MomentsInfoModel *model = list[i];
|
||
|
||
// 优先检查临时颜色(仅第一页第一条)
|
||
if (isFirstPage && i == 0 && pendingColor && pendingTimestamp) {
|
||
// 检查时间戳(5秒内有效,避免误匹配)
|
||
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
||
NSTimeInterval pending = pendingTimestamp.doubleValue;
|
||
if ((now - pending) < 5.0) {
|
||
model.emotionColor = pendingColor;
|
||
// 保存到持久化存储
|
||
[EPEmotionColorStorage saveColor:pendingColor forDynamicId:model.dynamicId];
|
||
// 清除临时数据
|
||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Color"];
|
||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Timestamp"];
|
||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 从持久化存储中匹配
|
||
NSString *savedColor = [EPEmotionColorStorage colorForDynamicId:model.dynamicId];
|
||
if (savedColor) {
|
||
model.emotionColor = savedColor;
|
||
} else {
|
||
// 无保存颜色,生成随机颜色并立即持久化
|
||
NSString *randomColor = [EPEmotionColorStorage randomEmotionColor];
|
||
model.emotionColor = randomColor;
|
||
[EPEmotionColorStorage saveColor:randomColor forDynamicId:model.dynamicId];
|
||
NSLog(@"[EPMomentListView] 为动态 %@ 分配随机颜色: %@", model.dynamicId, randomColor);
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - UITableView
|
||
|
||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||
return self.mutableRawList.count;
|
||
}
|
||
|
||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||
EPMomentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NewMomentCell" forIndexPath:indexPath];
|
||
if (indexPath.row < self.mutableRawList.count) {
|
||
MomentsInfoModel *model = [self.mutableRawList xpSafeObjectAtIndex:indexPath.row];
|
||
[cell configureWithModel:model];
|
||
}
|
||
return cell;
|
||
}
|
||
|
||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||
return UITableViewAutomaticDimension;
|
||
}
|
||
|
||
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||
return 200;
|
||
}
|
||
|
||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||
if (self.onSelectMoment) self.onSelectMoment(indexPath.row);
|
||
}
|
||
|
||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||
// 本地模式下不触发加载更多
|
||
if (self.isLocalMode) return;
|
||
|
||
CGFloat offsetY = scrollView.contentOffset.y;
|
||
CGFloat contentHeight = scrollView.contentSize.height;
|
||
CGFloat screenHeight = scrollView.frame.size.height;
|
||
if (offsetY > contentHeight - screenHeight - 100 && !self.isLoading) {
|
||
[self requestNextPage];
|
||
}
|
||
}
|
||
|
||
#pragma mark - Lazy
|
||
|
||
- (UITableView *)tableView {
|
||
if (!_tableView) {
|
||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||
_tableView.delegate = self;
|
||
_tableView.dataSource = self;
|
||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||
_tableView.backgroundColor = [UIColor clearColor];
|
||
_tableView.estimatedRowHeight = 200;
|
||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||
_tableView.showsVerticalScrollIndicator = NO;
|
||
// 底部留出更高空间,避免被悬浮 TabBar 遮挡
|
||
_tableView.contentInset = UIEdgeInsetsMake(10, 0, 120, 0);
|
||
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(10, 0, 120, 0);
|
||
[_tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"NewMomentCell"];
|
||
_tableView.refreshControl = self.refreshControl;
|
||
|
||
// MJRefresh Footer - 加载更多
|
||
__weak typeof(self) weakSelf = self;
|
||
MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||
__strong typeof(weakSelf) self = weakSelf;
|
||
if (!self.isLoading && self.nextID.length > 0) {
|
||
[self requestNextPage];
|
||
} else if (self.nextID.length == 0) {
|
||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||
} else {
|
||
[self.tableView.mj_footer endRefreshing];
|
||
}
|
||
}];
|
||
// 设置白色文字和指示器
|
||
footer.stateLabel.textColor = [UIColor whiteColor];
|
||
footer.loadingView.color = [UIColor whiteColor];
|
||
_tableView.mj_footer = footer;
|
||
}
|
||
return _tableView;
|
||
}
|
||
|
||
- (UIRefreshControl *)refreshControl {
|
||
if (!_refreshControl) {
|
||
_refreshControl = [[UIRefreshControl alloc] init];
|
||
_refreshControl.tintColor = [UIColor whiteColor]; // 白色加载指示器
|
||
[_refreshControl addTarget:self action:@selector(reloadFirstPage) forControlEvents:UIControlEventValueChanged];
|
||
}
|
||
return _refreshControl;
|
||
}
|
||
|
||
@end
|
||
|
||
|