Files
real-e-party-iOS/YuMi/E-P/NewMoments/Views/EPMomentCell.m
edwinQQQ a8319c61d8 feat: 添加情绪颜色选择功能及相关存储管理
主要变更:
1. 在 EPMomentPublishViewController 中添加情绪颜色选择按钮,用户可通过色轮选择情绪颜色。
2. 新增 EPEmotionColorStorage 类,提供情绪颜色的保存、获取和删除功能,支持动态 ID 的关联。
3. 新增 EPEmotionColorPicker 视图,提供环形布局的颜色选择器,增强用户体验。
4. 更新 EPMomentCell 和 EPMomentListView,以支持情绪颜色的显示和处理,确保动态展示的情绪效果。

此更新旨在提升用户交互体验,丰富动态发布功能,确保情绪颜色的有效管理和展示。
2025-10-14 18:26:16 +08:00

436 lines
15 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 "Api+Moments.h"
#import "NetImageView.h"
#import "EPEmotionColorStorage.h"
@interface EPMomentCell ()
// MARK: - UI Components
/// 卡片容器
@property (nonatomic, strong) UIView *cardView;
/// 头像(网络)
@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;
@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);
}];
// 头像(圆角矩形,不是圆形!)
[self.cardView 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.cardView 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.cardView 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.cardView 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.cardView 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.cardView 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(UILayoutPriorityDefaultHigh);
}];
// 点赞按钮
[self.actionBar addSubview:self.likeButton];
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.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);
}];
// 右侧占位(去掉分享按钮后,右边保持留白)
}
// 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 renderImages:model.dynamicResList];
// 配置点赞/评论数(安全整型,避免负数和溢出)
NSInteger likeCnt = MAX(0, model.likeCount.integerValue);
NSInteger cmtCnt = MAX(0, model.commentCount.integerValue);
[self.likeButton setTitle:[NSString stringWithFormat:@"👍 %ld", (long)likeCnt] forState:UIControlStateNormal];
[self.commentButton setTitle:[NSString stringWithFormat:@"💬 %ld", (long)cmtCnt] forState:UIControlStateNormal];
self.avatarImageView.imageUrl = model.avatar;
// 配置情绪颜色 border 和 shadow
[self applyEmotionColorEffect:model.emotionColor];
}
/// 应用情绪颜色视觉效果Border + Shadow
- (void)applyEmotionColorEffect:(NSString *)emotionColorHex {
NSString *displayColorHex = emotionColorHex;
// 如果没有保存的颜色,使用随机颜色(不持久化)
if (!displayColorHex) {
displayColorHex = [EPEmotionColorStorage randomEmotionColor];
}
UIColor *color = [self colorFromHex:displayColorHex];
// 设置 border
self.cardView.layer.borderWidth = 3.0;
self.cardView.layer.borderColor = color.CGColor;
// 设置 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);
}];
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;
[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);
}];
}
/// 格式化时间戳为相对时间
- (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; // 圆角
// Shadow 和 Border 将由 applyEmotionColorEffect 动态设置
_cardView.layer.masksToBounds = NO;
}
return _cardView;
}
- (UIImageView *)avatarImageView {
if (!_avatarImageView) {
NetImageConfig *config = [[NetImageConfig alloc] init];
config.imageType = ImageTypeUserIcon;
config.placeHolder = [UIImageConstant defaultAvatarPlaceholder];
_avatarImageView = [[NetImageView alloc] initWithConfig:config];
_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 {
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;
}
@end