Files
real-e-party-iOS/YuMi/E-P/NewMoments/Views/EPMomentCell.m
edwinQQQ f60a0eef14 feat: 更新 EPMomentCell 以支持图片点击查看和点赞功能
主要变更:
1. 引入 SDPhotoBrowser 类,支持点击图片查看大图功能。
2. 更新点赞逻辑,优化点赞状态和数量的显示,移除评论功能。
3. 调整 UI 组件约束,确保点赞按钮的显示效果。
4. 增加图片点击手势识别,提升用户交互体验。

此更新旨在增强动态展示的互动性,简化用户操作流程。
2025-10-14 19:01:49 +08:00

485 lines
17 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"
#import "SDPhotoBrowser.h"
@interface EPMomentCell () <SDPhotoBrowserDelegate>
// 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(80);
}];
}
// 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);
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];
}
/// 应用情绪颜色视觉效果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;
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);
}];
}
/// 格式化时间戳为相对时间
- (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;
// 如果已点赞,执行取消点赞
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 *uid = [[AccountInfoStorage instance] getUid];
NSString *dynamicId = self.currentModel.dynamicId;
NSString *status = isLike ? @"1" : @"0"; // 0=取消1=点赞
NSString *likedUid = self.currentModel.uid;
NSString *worldId = [NSString stringWithFormat:@"%ld", self.currentModel.worldId];
[Api momentsLike:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
// 更新点赞状态
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 ? @"点赞" : @"取消点赞");
} else {
NSLog(@"[EPMomentCell] %@ 失败: %@", isLike ? @"点赞" : @"取消点赞", msg);
}
} dynamicId:dynamicId uid:uid status:status likedUid:likedUid worldId:worldId];
}
// 评论功能已隐藏
// - (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 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 = [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:0.5 alpha:1.0] forState:UIControlStateNormal];
[_likeButton setTitleColor:[UIColor colorWithWhite:0.5 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;
}
@end