From a8319c61d88f38d889d41dae14e5f81886e9577e Mon Sep 17 00:00:00 2001 From: edwinQQQ Date: Tue, 14 Oct 2025 18:26:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=83=85=E7=BB=AA?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=AD=98=E5=82=A8=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要变更: 1. 在 EPMomentPublishViewController 中添加情绪颜色选择按钮,用户可通过色轮选择情绪颜色。 2. 新增 EPEmotionColorStorage 类,提供情绪颜色的保存、获取和删除功能,支持动态 ID 的关联。 3. 新增 EPEmotionColorPicker 视图,提供环形布局的颜色选择器,增强用户体验。 4. 更新 EPMomentCell 和 EPMomentListView,以支持情绪颜色的显示和处理,确保动态展示的情绪效果。 此更新旨在提升用户交互体验,丰富动态发布功能,确保情绪颜色的有效管理和展示。 --- YuMi.xcodeproj/project.pbxproj | 12 + .../EPMomentPublishViewController.m | 97 ++++++- .../Services/EPEmotionColorStorage.h | 39 +++ .../Services/EPEmotionColorStorage.m | 70 ++++++ .../NewMoments/Views/EPEmotionColorPicker.h | 28 +++ .../NewMoments/Views/EPEmotionColorPicker.m | 236 ++++++++++++++++++ YuMi/E-P/NewMoments/Views/EPMomentCell.m | 43 +++- YuMi/E-P/NewMoments/Views/EPMomentListView.m | 39 +++ .../YMMonents/Model/MomentsInfoModel.h | 2 + 9 files changed, 561 insertions(+), 5 deletions(-) create mode 100644 YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h create mode 100644 YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m create mode 100644 YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.h create mode 100644 YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m diff --git a/YuMi.xcodeproj/project.pbxproj b/YuMi.xcodeproj/project.pbxproj index a789d64..06deee6 100644 --- a/YuMi.xcodeproj/project.pbxproj +++ b/YuMi.xcodeproj/project.pbxproj @@ -768,6 +768,8 @@ 4CD47BCC2E61ADE400BCDA46 /* XPSocialAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD47BCB2E61ADE400BCDA46 /* XPSocialAction.m */; }; 4CD6FF662D673A5C00262AB7 /* AgentMessageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD6FF652D673A5C00262AB7 /* AgentMessageModel.m */; }; 4CD6FF692D673F7F00262AB7 /* AgentMessageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD6FF682D673F7F00262AB7 /* AgentMessageTableViewCell.m */; }; + 4CDF7F3F2E9E580E00113F54 /* EPEmotionColorStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDF7F3E2E9E580E00113F54 /* EPEmotionColorStorage.m */; }; + 4CDF7F422E9E581900113F54 /* EPEmotionColorPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDF7F412E9E581900113F54 /* EPEmotionColorPicker.m */; }; 4CE3A9462D22754C003F0796 /* RechargeUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3A9452D22754C003F0796 /* RechargeUserModel.m */; }; 4CE746C32D9290430094E496 /* RoomBoomManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE746C22D9290430094E496 /* RoomBoomManager.m */; }; 4CE746C62D9297C30094E496 /* BravoGiftTipModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE746C52D9297C30094E496 /* BravoGiftTipModel.m */; }; @@ -2991,6 +2993,10 @@ 4CD6FF652D673A5C00262AB7 /* AgentMessageModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AgentMessageModel.m; sourceTree = ""; }; 4CD6FF672D673F7F00262AB7 /* AgentMessageTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AgentMessageTableViewCell.h; sourceTree = ""; }; 4CD6FF682D673F7F00262AB7 /* AgentMessageTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AgentMessageTableViewCell.m; sourceTree = ""; }; + 4CDF7F3D2E9E580E00113F54 /* EPEmotionColorStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPEmotionColorStorage.h; sourceTree = ""; }; + 4CDF7F3E2E9E580E00113F54 /* EPEmotionColorStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPEmotionColorStorage.m; sourceTree = ""; }; + 4CDF7F402E9E581900113F54 /* EPEmotionColorPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPEmotionColorPicker.h; sourceTree = ""; }; + 4CDF7F412E9E581900113F54 /* EPEmotionColorPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPEmotionColorPicker.m; sourceTree = ""; }; 4CE3A9442D22754C003F0796 /* RechargeUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RechargeUserModel.h; sourceTree = ""; }; 4CE3A9452D22754C003F0796 /* RechargeUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RechargeUserModel.m; sourceTree = ""; }; 4CE746C12D9290430094E496 /* RoomBoomManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomBoomManager.h; sourceTree = ""; }; @@ -6415,6 +6421,8 @@ 4C06427B2E97BD6D00BAF413 /* Views */ = { isa = PBXGroup; children = ( + 4CDF7F402E9E581900113F54 /* EPEmotionColorPicker.h */, + 4CDF7F412E9E581900113F54 /* EPEmotionColorPicker.m */, 4C0642972E98F77900BAF413 /* EPMomentListView.h */, 4C0642982E98F77900BAF413 /* EPMomentListView.m */, 4C0642792E97BD6D00BAF413 /* EPMomentCell.h */, @@ -6456,6 +6464,8 @@ 4C0642952E98F76F00BAF413 /* Services */ = { isa = PBXGroup; children = ( + 4CDF7F3D2E9E580E00113F54 /* EPEmotionColorStorage.h */, + 4CDF7F3E2E9E580E00113F54 /* EPEmotionColorStorage.m */, 4C1E98C52E9A45BC0031AE79 /* EPMomentAPISwiftHelper.swift */, ); path = Services; @@ -12967,6 +12977,7 @@ 5456F3C82C6EF962000E1805 /* VIPCenterViewController.m in Sources */, E8EEB90926FC579A007C6EBA /* XPMineUserInfoEditTableViewCell.m in Sources */, E8F1559028125E2D00EE8C06 /* MessageAudioCenter.m in Sources */, + 4CDF7F3F2E9E580E00113F54 /* EPEmotionColorStorage.m in Sources */, E8901CF628B38D89001E9A92 /* XPGraffitiGiftView.m in Sources */, E89DA67527009ACD008483C1 /* XPMineRechargeNavView.m in Sources */, E81A6546283519CA00F55894 /* MomentsTopicModel.m in Sources */, @@ -13201,6 +13212,7 @@ 189DD75026E21D9000AB55B1 /* GCDHelper.m in Sources */, E82D5C76276AEB5100858D6D /* NameplateModel.m in Sources */, 142721AF29A75F6F00C7C423 /* SJXCSMIPHelper.m in Sources */, + 4CDF7F422E9E581900113F54 /* EPEmotionColorPicker.m in Sources */, E87E62742A3F5907002F68C9 /* XPHomeBannerTableViewCell.m in Sources */, E88C72A3282917590047FB2B /* XPRoomMusicVoiceSettingView.m in Sources */, 239D0FA82BFCB88D002977CE /* XPRoomBackContainerView.m in Sources */, diff --git a/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m b/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m index bf8d92b..7dcb63c 100644 --- a/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m +++ b/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m @@ -16,6 +16,8 @@ #import "DJDKMIMOMColor.h" #import "SZTextView.h" #import "YuMi-Swift.h" +#import "EPEmotionColorPicker.h" +#import "EPEmotionColorStorage.h" // 发布成功通知 NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNotification"; @@ -31,9 +33,11 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot @property (nonatomic, strong) SZTextView *textView; @property (nonatomic, strong) UILabel *limitLabel; @property (nonatomic, strong) UIView *lineView; +@property (nonatomic, strong) UIButton *emotionButton; @property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) NSMutableArray *images; @property (nonatomic, strong) NSMutableArray *selectedAssets; // TZImagePicker 已选资源 +@property (nonatomic, copy) NSString *selectedEmotionColor; // 选中的情绪颜色 @end @@ -54,6 +58,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot [self.contentView addSubview:self.textView]; [self.contentView addSubview:self.limitLabel]; [self.contentView addSubview:self.lineView]; + [self.contentView addSubview:self.emotionButton]; [self.contentView addSubview:self.collectionView]; [self.contentView addSubview:self.publishButton]; @@ -90,6 +95,14 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot make.leading.trailing.equalTo(self.textView); make.height.mas_equalTo(1); }]; + + // 情绪按钮 + [self.emotionButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.leading.trailing.equalTo(self.contentView).inset(15); + make.top.equalTo(self.lineView.mas_bottom).offset(10); + make.height.mas_equalTo(44); + }]; + // 计算显示3行图片所需的高度 // itemW = (屏幕宽度 - 左右边距30 - 列间距20) / 3 // 总高度 = 3行itemW + 2个行间距(10*2) @@ -98,7 +111,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.contentView).inset(15); - make.top.equalTo(self.lineView.mas_bottom).offset(10); + make.top.equalTo(self.emotionButton.mas_bottom).offset(10); make.height.mas_equalTo(collectionHeight); }]; @@ -116,6 +129,55 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot [self dismissViewControllerAnimated:YES completion:nil]; } +- (void)onEmotionButtonTapped { + EPEmotionColorPicker *picker = [[EPEmotionColorPicker alloc] init]; + __weak typeof(self) weakSelf = self; + picker.onColorSelected = ^(NSString *hexColor) { + __strong typeof(weakSelf) self = weakSelf; + self.selectedEmotionColor = hexColor; + [self updateEmotionButtonAppearance]; + }; + [picker showInView:self.view]; +} + +- (void)updateEmotionButtonAppearance { + if (self.selectedEmotionColor) { + // 显示选中的颜色 + UIColor *color = [self colorFromHex:self.selectedEmotionColor]; + + // 创建色块视图 + UIView *colorDot = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; + colorDot.backgroundColor = color; + colorDot.layer.cornerRadius = 10; + colorDot.layer.masksToBounds = YES; + colorDot.layer.borderWidth = 2; + colorDot.layer.borderColor = [UIColor whiteColor].CGColor; + + // 转换为 UIImage + UIGraphicsBeginImageContextWithOptions(colorDot.bounds.size, NO, 0); + [colorDot.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *colorDotImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [self.emotionButton setImage:colorDotImage forState:UIControlStateNormal]; + [self.emotionButton setTitle:@" Emotion Selected" forState:UIControlStateNormal]; + } else { + [self.emotionButton setImage:nil forState:UIControlStateNormal]; + [self.emotionButton setTitle:@"🎨 Add Emotion" forState:UIControlStateNormal]; + } +} + +- (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]; +} + - (void)onPublish { [self.view endEditing:YES]; @@ -129,6 +191,9 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot // 创建 Swift API Helper EPMomentAPISwiftHelper *apiHelper = [[EPMomentAPISwiftHelper alloc] init]; + // 保存情绪颜色用于发布后关联 + NSString *emotionColorToSave = self.selectedEmotionColor; + if (self.images.count > 0) { // 有图片:上传后发布(统一入口) [[EPSDKManager shared] uploadImages:self.images @@ -141,6 +206,10 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot content:self.textView.text ?: @"" resList:resList completion:^{ + // 保存临时情绪颜色(等待列表刷新后匹配) + if (emotionColorToSave) { + [self savePendingEmotionColor:emotionColorToSave]; + } // 发送发布成功通知 [[NSNotificationCenter defaultCenter] postNotificationName:EPMomentPublishSuccessNotification object:nil]; [self dismissViewControllerAnimated:YES completion:nil]; @@ -160,6 +229,10 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot content:self.textView.text resList:@[] completion:^{ + // 保存临时情绪颜色(等待列表刷新后匹配) + if (emotionColorToSave) { + [self savePendingEmotionColor:emotionColorToSave]; + } // 发送发布成功通知 [[NSNotificationCenter defaultCenter] postNotificationName:EPMomentPublishSuccessNotification object:nil]; [self dismissViewControllerAnimated:YES completion:nil]; @@ -170,6 +243,13 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot } } +/// 保存待处理的情绪颜色(临时存储,供列表刷新后匹配) +- (void)savePendingEmotionColor:(NSString *)color { + [[NSUserDefaults standardUserDefaults] setObject:color forKey:@"EP_Pending_Emotion_Color"]; + [[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:@"EP_Pending_Emotion_Timestamp"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + #pragma mark - UICollectionView - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { @@ -261,6 +341,21 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot - (SZTextView *)textView { if (!_textView) { _textView = [SZTextView new]; _textView.placeholder = @"Enter Content"; _textView.textColor = [DJDKMIMOMColor mainTextColor]; _textView.placeholderTextColor = [DJDKMIMOMColor secondTextColor]; _textView.font = [UIFont systemFontOfSize:15]; _textView.delegate = self; } return _textView; } - (UILabel *)limitLabel { if (!_limitLabel) { _limitLabel = [UILabel new]; _limitLabel.text = @"0/500"; _limitLabel.textColor = [DJDKMIMOMColor mainTextColor]; _limitLabel.font = [UIFont systemFontOfSize:12]; } return _limitLabel; } - (UIView *)lineView { if (!_lineView) { _lineView = [UIView new]; _lineView.backgroundColor = [DJDKMIMOMColor dividerColor]; } return _lineView; } +- (UIButton *)emotionButton { + if (!_emotionButton) { + _emotionButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_emotionButton setTitle:@"🎨 Add Emotion" forState:UIControlStateNormal]; + [_emotionButton setTitleColor:[DJDKMIMOMColor mainTextColor] forState:UIControlStateNormal]; + _emotionButton.titleLabel.font = [UIFont systemFontOfSize:15]; + _emotionButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; + _emotionButton.contentEdgeInsets = UIEdgeInsetsMake(0, 15, 0, 0); + _emotionButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.06]; + _emotionButton.layer.cornerRadius = 8; + _emotionButton.layer.masksToBounds = YES; + [_emotionButton addTarget:self action:@selector(onEmotionButtonTapped) forControlEvents:UIControlEventTouchUpInside]; + } + return _emotionButton; +} - (UICollectionView *)collectionView { if (!_collectionView) { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.minimumLineSpacing = 10; layout.minimumInteritemSpacing = 10; CGFloat itemW = (KScreenWidth - 15*2 - 10*2)/3.0; layout.itemSize = CGSizeMake(itemW, itemW); _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; _collectionView.delegate = self; _collectionView.dataSource = self; _collectionView.backgroundColor = [UIColor clearColor]; [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"ep.publish.cell"]; } return _collectionView; } - (NSMutableArray *)images { if (!_images) { _images = [NSMutableArray array]; } return _images; } - (NSMutableArray *)selectedAssets { if (!_selectedAssets) { _selectedAssets = [NSMutableArray array]; } return _selectedAssets; } diff --git a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h new file mode 100644 index 0000000..5b0f367 --- /dev/null +++ b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h @@ -0,0 +1,39 @@ +// +// EPEmotionColorStorage.h +// YuMi +// +// Created by AI on 2025-10-14. +// 本地情绪颜色存储管理器(基于 UserDefaults) +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface EPEmotionColorStorage : NSObject + +/// 保存动态的情绪颜色 +/// @param hexColor Hex 格式颜色值,如 #FF0000 +/// @param dynamicId 动态 ID ++ (void)saveColor:(NSString *)hexColor forDynamicId:(NSString *)dynamicId; + +/// 获取动态关联的情绪颜色 +/// @param dynamicId 动态 ID +/// @return Hex 格式颜色值,若未设置则返回 nil ++ (nullable NSString *)colorForDynamicId:(NSString *)dynamicId; + +/// 删除动态的情绪颜色 +/// @param dynamicId 动态 ID ++ (void)removeColorForDynamicId:(NSString *)dynamicId; + +/// 获取所有预设情绪颜色(6种基础情绪) +/// @return Hex 颜色数组 ++ (NSArray *)allEmotionColors; + +/// 获取随机情绪颜色(不持久化) ++ (NSString *)randomEmotionColor; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m new file mode 100644 index 0000000..2a122d4 --- /dev/null +++ b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m @@ -0,0 +1,70 @@ +// +// EPEmotionColorStorage.m +// YuMi +// +// Created by AI on 2025-10-14. +// + +#import "EPEmotionColorStorage.h" + +static NSString *const kEmotionColorStorageKey = @"EP_Emotion_Colors"; + +@implementation EPEmotionColorStorage + +#pragma mark - Public Methods + ++ (void)saveColor:(NSString *)hexColor forDynamicId:(NSString *)dynamicId { + if (!hexColor || !dynamicId) return; + + NSMutableDictionary *colorDict = [[self loadColorDictionary] mutableCopy]; + colorDict[dynamicId] = hexColor; + + [[NSUserDefaults standardUserDefaults] setObject:colorDict forKey:kEmotionColorStorageKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + ++ (NSString *)colorForDynamicId:(NSString *)dynamicId { + if (!dynamicId) return nil; + + NSDictionary *colorDict = [self loadColorDictionary]; + return colorDict[dynamicId]; +} + ++ (void)removeColorForDynamicId:(NSString *)dynamicId { + if (!dynamicId) return; + + NSMutableDictionary *colorDict = [[self loadColorDictionary] mutableCopy]; + [colorDict removeObjectForKey:dynamicId]; + + [[NSUserDefaults standardUserDefaults] setObject:colorDict forKey:kEmotionColorStorageKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + ++ (NSArray *)allEmotionColors { + return @[ + @"#FFD700", // 喜悦 Joy(金黄)- 降低亮度,更温暖 + @"#4A90E2", // 悲伤 Sadness(天蓝)- 提高亮度,更柔和 + @"#E74C3C", // 愤怒 Anger(珊瑚红)- 降低饱和度 + @"#9B59B6", // 恐惧 Fear(紫罗兰)- 稍微提亮 + @"#FF9A3D", // 惊讶 Surprise(柔和橙)- 略微调暗 + @"#2ECC71", // 厌恶 Disgust(翡翠绿)- 大幅降低亮度 + @"#3498DB", // 信任 Trust(亮蓝)- 清新明亮 + @"#F39C12" // 期待 Anticipation(琥珀色)- 温暖期待 + ]; +} + ++ (NSString *)randomEmotionColor { + NSArray *colors = [self allEmotionColors]; + uint32_t randomIndex = arc4random_uniform((uint32_t)colors.count); + return colors[randomIndex]; +} + +#pragma mark - Private Methods + ++ (NSDictionary *)loadColorDictionary { + NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryForKey:kEmotionColorStorageKey]; + return dict ?: @{}; +} + +@end + diff --git a/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.h b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.h new file mode 100644 index 0000000..7ad6653 --- /dev/null +++ b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.h @@ -0,0 +1,28 @@ +// +// EPEmotionColorPicker.h +// YuMi +// +// Created by AI on 2025-10-14. +// 情绪色轮选择器 - 环形布局 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface EPEmotionColorPicker : UIView + +/// 颜色选择回调 +@property (nonatomic, copy) void(^onColorSelected)(NSString *hexColor); + +/// 在指定视图中显示选择器 +/// @param parentView 父视图(通常是 ViewController 的 view) +- (void)showInView:(UIView *)parentView; + +/// 关闭选择器 +- (void)dismiss; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m new file mode 100644 index 0000000..0466291 --- /dev/null +++ b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m @@ -0,0 +1,236 @@ +// +// EPEmotionColorPicker.m +// YuMi +// +// Created by AI on 2025-10-14. +// + +#import "EPEmotionColorPicker.h" +#import "EPEmotionColorStorage.h" +#import + +@interface EPEmotionColorPicker () + +@property (nonatomic, strong) UIView *backgroundMask; +@property (nonatomic, strong) UIView *containerView; +@property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UIView *colorWheelContainer; +@property (nonatomic, strong) NSMutableArray *colorButtons; + +@end + +@implementation EPEmotionColorPicker + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + [self setupUI]; + } + return self; +} + +- (void)setupUI { + self.backgroundColor = [UIColor clearColor]; + + // 背景遮罩 + [self addSubview:self.backgroundMask]; + [self.backgroundMask mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self); + }]; + + // 底部卡片容器 + [self addSubview:self.containerView]; + [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) { + make.leading.trailing.bottom.equalTo(self); + make.height.mas_equalTo(320); + }]; + + // 标题 + [self.containerView addSubview:self.titleLabel]; + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.containerView).offset(20); + make.centerX.equalTo(self.containerView); + }]; + + // 色轮容器 + [self.containerView addSubview:self.colorWheelContainer]; + [self.colorWheelContainer mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.containerView); + make.top.equalTo(self.titleLabel.mas_bottom).offset(30); + make.size.mas_equalTo(CGSizeMake(240, 240)); + }]; + + // 创建环形排列的颜色按钮 + [self createColorButtons]; +} + +- (void)createColorButtons { + NSArray *colors = [EPEmotionColorStorage allEmotionColors]; + NSArray *emotions = @[@"Joy", @"Sadness", @"Anger", @"Fear", @"Surprise", @"Disgust", @"Trust", @"Anticipation"]; + + CGFloat radius = 80.0; // 圆周半径 + CGFloat buttonSize = 50.0; // 按钮直径 + CGFloat angleStep = M_PI * 2.0 / colors.count; + CGFloat centerX = 120.0; // 容器中心 X + CGFloat centerY = 120.0; // 容器中心 Y + + for (NSInteger i = 0; i < colors.count; i++) { + // 从顶部开始,顺时针排列 + CGFloat angle = angleStep * i - M_PI_2; + CGFloat x = centerX + radius * cos(angle) - buttonSize / 2.0; + CGFloat y = centerY + radius * sin(angle) - buttonSize / 2.0; + + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.frame = CGRectMake(x, y, buttonSize, buttonSize); + button.backgroundColor = [self colorFromHex:colors[i]]; + button.layer.cornerRadius = buttonSize / 2.0; + button.layer.masksToBounds = YES; + button.layer.borderWidth = 3.0; + button.layer.borderColor = [UIColor whiteColor].CGColor; + button.tag = i; + [button addTarget:self action:@selector(onColorButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + + // 添加阴影效果 + button.layer.shadowColor = [self colorFromHex:colors[i]].CGColor; + button.layer.shadowOffset = CGSizeMake(0, 2); + button.layer.shadowOpacity = 0.6; + button.layer.shadowRadius = 8; + button.layer.masksToBounds = NO; + + // 添加情绪标签(可选) + UILabel *emotionLabel = [[UILabel alloc] init]; + emotionLabel.text = emotions[i]; + emotionLabel.textColor = [UIColor whiteColor]; + emotionLabel.font = [UIFont systemFontOfSize:10 weight:UIFontWeightMedium]; + emotionLabel.textAlignment = NSTextAlignmentCenter; + emotionLabel.frame = CGRectMake(0, buttonSize - 2, buttonSize, 12); + emotionLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + emotionLabel.layer.cornerRadius = 6; + emotionLabel.layer.masksToBounds = YES; + [button addSubview:emotionLabel]; + + [self.colorWheelContainer addSubview:button]; + [self.colorButtons addObject:button]; + } +} + +#pragma mark - Actions + +- (void)onColorButtonTapped:(UIButton *)sender { + NSInteger index = sender.tag; + NSArray *colors = [EPEmotionColorStorage allEmotionColors]; + NSString *selectedColor = colors[index]; + + // 点击动画 + [UIView animateWithDuration:0.1 animations:^{ + sender.transform = CGAffineTransformMakeScale(0.9, 0.9); + } completion:^(BOOL finished) { + [UIView animateWithDuration:0.1 animations:^{ + sender.transform = CGAffineTransformIdentity; + } completion:^(BOOL finished) { + // 执行回调 + if (self.onColorSelected) { + self.onColorSelected(selectedColor); + } + [self dismiss]; + }]; + }]; +} + +- (void)onBackgroundTapped { + [self dismiss]; +} + +#pragma mark - Public Methods + +- (void)showInView:(UIView *)parentView { + [parentView addSubview:self]; + [self mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(parentView); + }]; + + // 初始状态 + self.backgroundMask.alpha = 0; + self.containerView.transform = CGAffineTransformMakeTranslation(0, 320); + + // 弹出动画 + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.backgroundMask.alpha = 1; + self.containerView.transform = CGAffineTransformIdentity; + } completion:nil]; +} + +- (void)dismiss { + [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ + self.backgroundMask.alpha = 0; + self.containerView.transform = CGAffineTransformMakeTranslation(0, 320); + } completion:^(BOOL finished) { + [self removeFromSuperview]; + }]; +} + +#pragma mark - Utilities + +- (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]; +} + +#pragma mark - Lazy Loading + +- (UIView *)backgroundMask { + if (!_backgroundMask) { + _backgroundMask = [[UIView alloc] init]; + _backgroundMask.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onBackgroundTapped)]; + [_backgroundMask addGestureRecognizer:tap]; + } + return _backgroundMask; +} + +- (UIView *)containerView { + if (!_containerView) { + _containerView = [[UIView alloc] init]; + _containerView.backgroundColor = [UIColor colorWithRed:0x0C/255.0 green:0x05/255.0 blue:0x27/255.0 alpha:1.0]; + _containerView.layer.cornerRadius = 20; + _containerView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; + _containerView.layer.masksToBounds = YES; + } + return _containerView; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [[UILabel alloc] init]; + _titleLabel.text = @"Choose your emotion"; + _titleLabel.textColor = [UIColor whiteColor]; + _titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +- (UIView *)colorWheelContainer { + if (!_colorWheelContainer) { + _colorWheelContainer = [[UIView alloc] init]; + _colorWheelContainer.backgroundColor = [UIColor clearColor]; + } + return _colorWheelContainer; +} + +- (NSMutableArray *)colorButtons { + if (!_colorButtons) { + _colorButtons = [NSMutableArray array]; + } + return _colorButtons; +} + +@end + diff --git a/YuMi/E-P/NewMoments/Views/EPMomentCell.m b/YuMi/E-P/NewMoments/Views/EPMomentCell.m index f680169..eb836b4 100644 --- a/YuMi/E-P/NewMoments/Views/EPMomentCell.m +++ b/YuMi/E-P/NewMoments/Views/EPMomentCell.m @@ -11,6 +11,7 @@ #import "AccountInfoStorage.h" #import "Api+Moments.h" #import "NetImageView.h" +#import "EPEmotionColorStorage.h" @interface EPMomentCell () @@ -167,6 +168,43 @@ [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 @@ -294,10 +332,7 @@ _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; + // Shadow 和 Border 将由 applyEmotionColorEffect 动态设置 _cardView.layer.masksToBounds = NO; } return _cardView; diff --git a/YuMi/E-P/NewMoments/Views/EPMomentListView.m b/YuMi/E-P/NewMoments/Views/EPMomentListView.m index c61d450..af72d53 100644 --- a/YuMi/E-P/NewMoments/Views/EPMomentListView.m +++ b/YuMi/E-P/NewMoments/Views/EPMomentListView.m @@ -10,6 +10,7 @@ #import "EPMomentCell.h" #import #import "YuMi-Swift.h" +#import "EPEmotionColorStorage.h" @interface EPMomentListView () @@ -91,6 +92,9 @@ @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]; @@ -120,6 +124,41 @@ [self.refreshControl endRefreshing]; } +/// 处理动态的情绪颜色(从 UserDefaults 匹配 + 处理临时颜色) +- (void)processEmotionColors:(NSArray *)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; + } + // 不设置则保持 nil,Cell 渲染时会随机生成 + } +} + #pragma mark - UITableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { diff --git a/YuMi/Modules/YMMonents/Model/MomentsInfoModel.h b/YuMi/Modules/YMMonents/Model/MomentsInfoModel.h index d34d3bf..3ee8f9b 100644 --- a/YuMi/Modules/YMMonents/Model/MomentsInfoModel.h +++ b/YuMi/Modules/YMMonents/Model/MomentsInfoModel.h @@ -77,6 +77,8 @@ typedef NS_ENUM(NSInteger, MonentsContentType) { @property (nonatomic, copy) NSString *worldName; ///动态的id @property (nonatomic,copy) NSString *dynamicId; +///情绪颜色(本地标注,Hex格式如 #FF0000) +@property (nonatomic, copy) NSString *emotionColor; ///是否是折叠起来的 @property (nonatomic,assign) BOOL isFold; ///cell的高度