Files
real-e-party-iOS/YuMi/E-P/NewMoments/Views/EPMomentCell.m
edwinQQQ a0e83658c6 chore: 更新 .gitignore 文件并删除过时的文档
主要变更:
1. 在 .gitignore 中添加了 Docs/ 文件夹,以忽略文档相关文件。
2. 删除了多个过时的文档,包括构建指南、编译修复指南和当前状态报告等。

此更新旨在清理项目文件,确保版本控制的整洁性。
2025-10-16 16:04:15 +08:00

565 lines
20 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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 "NetImageView.h"
#import "EPEmotionColorStorage.h"
#import "SDPhotoBrowser.h"
#import "YuMi-Swift.h" // Swift 互操作
@interface EPMomentCell () <SDPhotoBrowserDelegate>
// MARK: - UI Components
/// 卡片容器
@property (nonatomic, strong) UIView *cardView;
/// 彩色背景层(毛玻璃下方)
@property (nonatomic, strong) UIView *colorBackgroundView;
/// 毛玻璃效果视图
@property (nonatomic, strong) UIVisualEffectView *blurEffectView;
/// 头像(网络)
@property (nonatomic, strong) NetImageView *avatarImageView;
/// 用户名
@property (nonatomic, strong) UILabel *nameLabel;
/// 时间标签
@property (nonatomic, strong) UILabel *timeLabel;
/// 内容标签
@property (nonatomic, strong) UILabel *contentLabel;
/// 图片容器(九宫格)
@property (nonatomic, strong) UIView *imagesContainer;
@property (nonatomic, strong) NSMutableArray<NetImageView *> *imageViews;
/// 底部操作栏
@property (nonatomic, strong) UIView *actionBar;
/// 点赞按钮
@property (nonatomic, strong) UIButton *likeButton;
/// 评论按钮
@property (nonatomic, strong) UIButton *commentButton;
// 分享按钮已移除
/// 当前数据模型
@property (nonatomic, strong) MomentsInfoModel *currentModel;
/// API Helper (Swift 版本)
@property (nonatomic, strong) EPMomentAPISwiftHelper *apiHelper;
@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.leading.trailing.equalTo(self.contentView).inset(15);
make.top.equalTo(self.contentView).offset(8);
make.bottom.equalTo(self.contentView).offset(-8).priority(UILayoutPriorityRequired - 1);
}];
// 彩色背景层(最底层)
[self.cardView addSubview:self.colorBackgroundView];
[self.colorBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.cardView);
}];
// 毛玻璃效果视图(在彩色背景层之上)
[self.cardView addSubview:self.blurEffectView];
[self.blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.cardView);
}];
// 头像(
[self.blurEffectView.contentView addSubview:self.avatarImageView];
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.cardView).offset(15);
make.top.equalTo(self.cardView).offset(15);
make.size.mas_equalTo(CGSizeMake(40, 40));
}];
// 用户名
[self.blurEffectView.contentView addSubview:self.nameLabel];
[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.avatarImageView.mas_trailing).offset(10);
make.top.equalTo(self.avatarImageView);
make.trailing.equalTo(self.cardView).offset(-15);
}];
// 时间
[self.blurEffectView.contentView addSubview:self.timeLabel];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.nameLabel);
make.bottom.equalTo(self.avatarImageView);
make.trailing.equalTo(self.cardView).offset(-15);
}];
// 内容
[self.blurEffectView.contentView addSubview:self.contentLabel];
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.cardView).inset(15);
make.top.equalTo(self.avatarImageView.mas_bottom).offset(12);
}];
// 图片九宫格
[self.blurEffectView.contentView addSubview:self.imagesContainer];
[self.imagesContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.cardView).inset(15);
make.top.equalTo(self.contentLabel.mas_bottom).offset(12);
make.height.mas_equalTo(0); // 初始高度为0renderImages 时会 remakeConstraints
}];
// 底部操作栏
[self.blurEffectView.contentView addSubview:self.actionBar];
[self.actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.cardView);
make.top.equalTo(self.imagesContainer.mas_bottom).offset(12);
make.height.mas_equalTo(50);
// 设置较高优先级,确保底部约束生效
make.bottom.equalTo(self.cardView).offset(-8).priority(UILayoutPriorityRequired - 2);
}];
// 点赞按钮(居左显示,评论功能已隐藏)
[self.actionBar addSubview:self.likeButton];
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.actionBar);
make.centerY.equalTo(self.actionBar);
make.width.mas_greaterThanOrEqualTo(80);
}];
}
// MARK: - Public Methods
- (void)configureWithModel:(MomentsInfoModel *)model {
self.currentModel = model;
// 配置用户名
self.nameLabel.text = model.nick ?: YMLocalizedString(@"user.anonymous");
// 配置时间(将时间戳转换为 MM/dd 格式)
self.timeLabel.text = [self formatTimestampToDate:model.publishTime];
// 配置内容
self.contentLabel.text = model.content ?: @"";
// 配置图片九宫格
[self renderImages:model.dynamicResList];
// 配置点赞按钮状态和数字
NSInteger likeCnt = MAX(0, model.likeCount.integerValue);
self.likeButton.selected = model.isLike;
[self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCnt] forState:UIControlStateNormal];
[self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCnt] forState:UIControlStateSelected];
self.avatarImageView.imageUrl = model.avatar;
// 配置情绪颜色 border 和 shadow
[self applyEmotionColorEffect:model.emotionColor];
// 确保布局完成后 cell 高度正确
[self setNeedsLayout];
}
/// 应用情绪颜色视觉效果Background + Shadow
- (void)applyEmotionColorEffect:(NSString *)emotionColorHex {
// 获取颜色(已在列表加载时处理,这里直接使用)
if (!emotionColorHex) {
NSLog(@"[EPMomentCell] 警告emotionColorHex 为 nil");
return;
}
UIColor *color = [self colorFromHex:emotionColorHex];
// 移除边框
self.cardView.layer.borderWidth = 0;
// 设置彩色背景50% 透明度,在毛玻璃下方)
self.colorBackgroundView.backgroundColor = [color colorWithAlphaComponent:0.5];
// 设置 shadow使用情绪颜色
self.cardView.layer.shadowColor = color.CGColor;
self.cardView.layer.shadowOffset = CGSizeMake(0, 2);
self.cardView.layer.shadowOpacity = 0.5;
self.cardView.layer.shadowRadius = 16.0;
}
/// Hex 转 UIColor
- (UIColor *)colorFromHex:(NSString *)hexString {
unsigned rgbValue = 0;
NSScanner *scanner = [NSScanner scannerWithString:hexString];
[scanner setScanLocation:1]; // 跳过 #
[scanner scanHexInt:&rgbValue];
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0
green:((rgbValue & 0xFF00) >> 8)/255.0
blue:(rgbValue & 0xFF)/255.0
alpha:1.0];
}
// MARK: - Images Grid
- (void)renderImages:(NSArray *)resList {
// 清理旧视图
for (UIView *iv in self.imageViews) { [iv removeFromSuperview]; }
[self.imageViews removeAllObjects];
if (resList.count == 0) {
[self.imagesContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.cardView).inset(15);
make.top.equalTo(self.contentLabel.mas_bottom).offset(0);
make.height.mas_equalTo(0);
}];
// 强制触发布局更新
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
return;
}
NSInteger columns = 3;
CGFloat spacing = 6.0;
CGFloat totalWidth = [UIScreen mainScreen].bounds.size.width - 30 - 30; // 左右各 15 内边距,再减卡片左右 15
CGFloat itemW = floor((totalWidth - spacing * (columns - 1)) / columns);
for (NSInteger i = 0; i < resList.count && i < 9; i++) {
NetImageConfig *config = [[NetImageConfig alloc] init];
config.placeHolder = [UIImageConstant defaultBannerPlaceholder];
NetImageView *iv = [[NetImageView alloc] initWithConfig:config];
iv.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
iv.layer.cornerRadius = 6;
iv.layer.masksToBounds = YES;
iv.contentMode = UIViewContentModeScaleAspectFill;
iv.userInteractionEnabled = YES;
iv.tag = i; // 用于识别点击的图片索引
// 添加点击手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onImageTapped:)];
[iv addGestureRecognizer:tap];
[self.imagesContainer addSubview:iv];
[self.imageViews addObject:iv];
NSInteger row = i / columns;
NSInteger col = i % columns;
[iv mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.imagesContainer).offset((itemW + spacing) * col);
make.top.equalTo(self.imagesContainer).offset((itemW + spacing) * row);
make.size.mas_equalTo(CGSizeMake(itemW, itemW));
}];
// 绑定网络图片
NSString *url = nil;
id item = resList[i];
if ([item isKindOfClass:[NSDictionary class]]) {
url = [item valueForKey:@"resUrl"] ?: [item valueForKey:@"url"];
} else if ([item respondsToSelector:@selector(resUrl)]) {
url = [item valueForKey:@"resUrl"];
}
iv.imageUrl = url;
}
NSInteger rows = ((MIN(resList.count, 9) - 1) / columns) + 1;
CGFloat height = rows * itemW + (rows - 1) * spacing;
[self.imagesContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self.cardView).inset(15);
make.top.equalTo(self.contentLabel.mas_bottom).offset(12);
make.height.mas_equalTo(height);
}];
// 强制触发布局更新,确保 cell 高度正确计算
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
}
/// 格式化时间戳为 MM/dd 格式
- (NSString *)formatTimestampToDate:(NSString *)timestampString {
if (!timestampString || timestampString.length == 0) {
return @"";
}
// 将字符串转换为时间戳(毫秒)
NSTimeInterval timestamp = [timestampString doubleValue] / 1000.0;
if (timestamp <= 0) {
return @"";
}
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"MM/dd";
return [formatter stringFromDate:date];
}
/// 格式化时间戳为相对时间
- (NSString *)formatTimeInterval:(NSInteger)timestamp {
if (timestamp <= 0) return YMLocalizedString(@"time.just_now");
NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] - timestamp / 1000.0;
if (interval < 60) {
return YMLocalizedString(@"time.just_now");
} else if (interval < 3600) {
return [NSString stringWithFormat:YMLocalizedString(@"time.minutes_ago"), interval / 60];
} else if (interval < 86400) {
return [NSString stringWithFormat:YMLocalizedString(@"time.hours_ago"), interval / 3600];
} else if (interval < 604800) {
return [NSString stringWithFormat:YMLocalizedString(@"time.days_ago"), 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;
// 如果已点赞,执行取消点赞
if (self.currentModel.isLike) {
[self performLikeAction:NO];
return;
}
// 审核中的动态不可点赞
if (self.currentModel.status == 0) {
NSLog(@"[EPMomentCell] 动态审核中,无法点赞");
// TODO: 可选择显示提示 Toast
return;
}
// 执行点赞
[self performLikeAction:YES];
}
- (void)performLikeAction:(BOOL)isLike {
NSLog(@"[EPMomentCell] %@ 动态: %@", isLike ? @"点赞" : @"取消点赞", self.currentModel.dynamicId);
NSString *dynamicId = self.currentModel.dynamicId;
NSString *likedUid = self.currentModel.uid;
long worldId = self.currentModel.worldId;
// 使用 Swift API Helper
@kWeakify(self);
[self.apiHelper likeMomentWithDynamicId:dynamicId
isLike:isLike
likedUid:likedUid
worldId:worldId
completion:^{
@kStrongify(self);
// 更新点赞状态
self.currentModel.isLike = isLike;
NSInteger likeCount = [self.currentModel.likeCount integerValue];
likeCount += isLike ? 1 : -1;
likeCount = MAX(0, likeCount); // 防止负数
self.currentModel.likeCount = @(likeCount).stringValue;
// 更新 UI
self.likeButton.selected = self.currentModel.isLike;
[self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCount] forState:UIControlStateNormal];
[self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCount] forState:UIControlStateSelected];
NSLog(@"[EPMomentCell] %@ 成功", isLike ? @"点赞" : @"取消点赞");
} failure:^(NSInteger code, NSString * _Nonnull msg) {
NSLog(@"[EPMomentCell] %@ 失败 (code: %ld): %@", isLike ? @"点赞" : @"取消点赞", (long)code, msg);
}];
}
// 评论功能已隐藏
// - (void)onCommentButtonTapped {
// NSLog(@"[EPMomentCell] 评论");
// }
- (void)onImageTapped:(UITapGestureRecognizer *)gesture {
if (!self.currentModel || !self.currentModel.dynamicResList.count) return;
NSInteger index = gesture.view.tag;
NSLog(@"[EPMomentCell] 点击图片索引: %ld", (long)index);
SDPhotoBrowser *browser = [[SDPhotoBrowser alloc] init];
browser.sourceImagesContainerView = self.imagesContainer;
browser.delegate = self;
browser.imageCount = self.currentModel.dynamicResList.count;
browser.currentImageIndex = index;
[browser show];
}
#pragma mark - SDPhotoBrowserDelegate
- (NSURL *)photoBrowser:(SDPhotoBrowser *)browser highQualityImageURLForIndex:(NSInteger)index {
if (index >= 0 && index < self.currentModel.dynamicResList.count) {
id item = self.currentModel.dynamicResList[index];
NSString *url = nil;
if ([item isKindOfClass:[NSDictionary class]]) {
url = [item valueForKey:@"resUrl"] ?: [item valueForKey:@"url"];
} else if ([item respondsToSelector:@selector(resUrl)]) {
url = [item valueForKey:@"resUrl"];
}
if (url) {
return [NSURL URLWithString:url];
}
}
return nil;
}
- (UIImage *)photoBrowser:(SDPhotoBrowser *)browser placeholderImageForIndex:(NSInteger)index {
return [UIImageConstant defaultBannerPlaceholder];
}
// MARK: - Lazy Loading
- (UIView *)cardView {
if (!_cardView) {
_cardView = [[UIView alloc] init];
_cardView.backgroundColor = [UIColor clearColor]; // 透明背景,颜色由 colorBackgroundView 提供
_cardView.layer.cornerRadius = 12; // 圆角
// Shadow 将由 applyEmotionColorEffect 动态设置
_cardView.layer.masksToBounds = NO;
}
return _cardView;
}
- (UIView *)colorBackgroundView {
if (!_colorBackgroundView) {
_colorBackgroundView = [[UIView alloc] init];
_colorBackgroundView.layer.cornerRadius = 12;
_colorBackgroundView.layer.masksToBounds = YES;
}
return _colorBackgroundView;
}
- (UIVisualEffectView *)blurEffectView {
if (!_blurEffectView) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
_blurEffectView.layer.cornerRadius = 12;
_blurEffectView.layer.masksToBounds = YES;
}
return _blurEffectView;
}
- (UIImageView *)avatarImageView {
if (!_avatarImageView) {
NetImageConfig *config = [[NetImageConfig alloc] init];
_avatarImageView = [[NetImageView alloc] initWithConfig:config];
_avatarImageView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
_avatarImageView.layer.cornerRadius = 20;
_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 whiteColor];
}
return _nameLabel;
}
- (UILabel *)timeLabel {
if (!_timeLabel) {
_timeLabel = [[UILabel alloc] init];
_timeLabel.font = [UIFont systemFontOfSize:12];
_timeLabel.textColor = [UIColor colorWithWhite:1 alpha:0.6];
}
return _timeLabel;
}
- (UILabel *)contentLabel {
if (!_contentLabel) {
_contentLabel = [[UILabel alloc] init];
_contentLabel.font = [UIFont systemFontOfSize:15];
_contentLabel.textColor = [UIColor whiteColor];
_contentLabel.numberOfLines = 0;
_contentLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
return _contentLabel;
}
- (UIView *)actionBar {
if (!_actionBar) {
_actionBar = [[UIView alloc] init];
_actionBar.backgroundColor = [UIColor clearColor]; // 半透明白色,与毛玻璃效果搭配
}
return _actionBar;
}
- (UIButton *)likeButton {
if (!_likeButton) {
_likeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_likeButton setImage:[UIImage imageNamed:@"monents_info_like_count_normal"] forState:UIControlStateNormal];
[_likeButton setImage:[UIImage imageNamed:@"monents_info_like_count_select"] forState:UIControlStateSelected];
[_likeButton setTitle:@" 0" forState:UIControlStateNormal];
_likeButton.titleLabel.font = [UIFont systemFontOfSize:13];
[_likeButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.6] forState:UIControlStateNormal];
[_likeButton setTitleColor:[UIColor colorWithWhite:1 alpha:1.0] forState:UIControlStateSelected];
[_likeButton addTarget:self action:@selector(onLikeButtonTapped) forControlEvents:UIControlEventTouchUpInside];
}
return _likeButton;
}
// 评论按钮已移除
- (UIButton *)commentButton {
return nil;
}
- (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;
}
- (UIView *)imagesContainer {
if (!_imagesContainer) {
_imagesContainer = [[UIView alloc] init];
_imagesContainer.backgroundColor = [UIColor clearColor];
}
return _imagesContainer;
}
- (NSMutableArray<NetImageView *> *)imageViews {
if (!_imageViews) {
_imageViews = [NSMutableArray array];
}
return _imageViews;
}
- (EPMomentAPISwiftHelper *)apiHelper {
if (!_apiHelper) {
_apiHelper = [[EPMomentAPISwiftHelper alloc] init];
}
return _apiHelper;
}
@end