3 Commits

Author SHA1 Message Date
edwinQQQ
3a12a18687 feat: 添加点赞功能支持及 Swift API Helper 集成
主要变更:
1. 在 EPMomentAPISwiftHelper 中新增点赞/取消点赞功能,支持动态 ID 和用户 UID。
2. 更新 EPMomentCell 以使用新的 Swift API Helper 进行点赞操作,简化点赞逻辑。
3. 优化点赞状态和数量的更新逻辑,确保用户界面及时反映点赞结果。

此更新旨在提升用户互动体验,简化点赞操作流程。
2025-10-14 19:06:44 +08:00
edwinQQQ
f60a0eef14 feat: 更新 EPMomentCell 以支持图片点击查看和点赞功能
主要变更:
1. 引入 SDPhotoBrowser 类,支持点击图片查看大图功能。
2. 更新点赞逻辑,优化点赞状态和数量的显示,移除评论功能。
3. 调整 UI 组件约束,确保点赞按钮的显示效果。
4. 增加图片点击手势识别,提升用户交互体验。

此更新旨在增强动态展示的互动性,简化用户操作流程。
2025-10-14 19:01:49 +08:00
edwinQQQ
a8319c61d8 feat: 添加情绪颜色选择功能及相关存储管理
主要变更:
1. 在 EPMomentPublishViewController 中添加情绪颜色选择按钮,用户可通过色轮选择情绪颜色。
2. 新增 EPEmotionColorStorage 类,提供情绪颜色的保存、获取和删除功能,支持动态 ID 的关联。
3. 新增 EPEmotionColorPicker 视图,提供环形布局的颜色选择器,增强用户体验。
4. 更新 EPMomentCell 和 EPMomentListView,以支持情绪颜色的显示和处理,确保动态展示的情绪效果。

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

View File

@@ -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 = "<group>"; };
4CD6FF672D673F7F00262AB7 /* AgentMessageTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AgentMessageTableViewCell.h; sourceTree = "<group>"; };
4CD6FF682D673F7F00262AB7 /* AgentMessageTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AgentMessageTableViewCell.m; sourceTree = "<group>"; };
4CDF7F3D2E9E580E00113F54 /* EPEmotionColorStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPEmotionColorStorage.h; sourceTree = "<group>"; };
4CDF7F3E2E9E580E00113F54 /* EPEmotionColorStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPEmotionColorStorage.m; sourceTree = "<group>"; };
4CDF7F402E9E581900113F54 /* EPEmotionColorPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPEmotionColorPicker.h; sourceTree = "<group>"; };
4CDF7F412E9E581900113F54 /* EPEmotionColorPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPEmotionColorPicker.m; sourceTree = "<group>"; };
4CE3A9442D22754C003F0796 /* RechargeUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RechargeUserModel.h; sourceTree = "<group>"; };
4CE3A9452D22754C003F0796 /* RechargeUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RechargeUserModel.m; sourceTree = "<group>"; };
4CE746C12D9290430094E496 /* RoomBoomManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomBoomManager.h; sourceTree = "<group>"; };
@@ -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 */,

View File

@@ -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<UIImage *> *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
// = 3itemW + 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<UIImage *> *)images { if (!_images) { _images = [NSMutableArray array]; } return _images; }
- (NSMutableArray *)selectedAssets { if (!_selectedAssets) { _selectedAssets = [NSMutableArray array]; } return _selectedAssets; }

View File

@@ -0,0 +1,39 @@
//
// EPEmotionColorStorage.h
// YuMi
//
// Created by AI on 2025-10-14.
// 本地情绪颜色存储管理器(基于 UserDefaults
//
#import <Foundation/Foundation.h>
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<NSString *> *)allEmotionColors;
/// 获取随机情绪颜色(不持久化)
+ (NSString *)randomEmotionColor;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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<NSString *> *)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

View File

@@ -74,5 +74,38 @@ import Foundation
}
}, uid: uid, type: type, worldId: "", content: content, resList: resList)
}
/// /
/// - Parameters:
/// - dynamicId: ID
/// - isLike: true=false=
/// - likedUid: UID
/// - worldId: ID
/// - completion:
/// - failure: (, )
@objc func likeMoment(
dynamicId: String,
isLike: Bool,
likedUid: String,
worldId: Int,
completion: @escaping () -> Void,
failure: @escaping (Int, String) -> Void
) {
guard let uid = AccountInfoStorage.instance().getUid() else {
failure(-1, "用户未登录")
return
}
let status = isLike ? "1" : "0"
let worldIdStr = String(format: "%ld", worldId)
Api.momentsLike({ (data, code, msg) in
if code == 200 {
completion()
} else {
failure(Int(code), msg ?? "点赞操作失败")
}
}, dynamicId: dynamicId, uid: uid, status: status, likedUid: likedUid, worldId: worldIdStr)
}
}

View File

@@ -0,0 +1,28 @@
//
// EPEmotionColorPicker.h
// YuMi
//
// Created by AI on 2025-10-14.
// 情绪色轮选择器 - 环形布局
//
#import <UIKit/UIKit.h>
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

View File

@@ -0,0 +1,236 @@
//
// EPEmotionColorPicker.m
// YuMi
//
// Created by AI on 2025-10-14.
//
#import "EPEmotionColorPicker.h"
#import "EPEmotionColorStorage.h"
#import <Masonry/Masonry.h>
@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<UIButton *> *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<NSString *> *colors = [EPEmotionColorStorage allEmotionColors];
NSArray<NSString *> *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<NSString *> *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<UIButton *> *)colorButtons {
if (!_colorButtons) {
_colorButtons = [NSMutableArray array];
}
return _colorButtons;
}
@end

View File

@@ -9,6 +9,7 @@
#import <UIKit/UIKit.h>
@class MomentsInfoModel;
@class SDPhotoBrowser;
NS_ASSUME_NONNULL_BEGIN

View File

@@ -9,10 +9,12 @@
#import "EPMomentCell.h"
#import "MomentsInfoModel.h"
#import "AccountInfoStorage.h"
#import "Api+Moments.h"
#import "NetImageView.h"
#import "EPEmotionColorStorage.h"
#import "SDPhotoBrowser.h"
#import "YuMi-Swift.h" // Swift
@interface EPMomentCell ()
@interface EPMomentCell () <SDPhotoBrowserDelegate>
// MARK: - UI Components
@@ -49,6 +51,9 @@
///
@property (nonatomic, strong) MomentsInfoModel *currentModel;
/// API Helper (Swift )
@property (nonatomic, strong) EPMomentAPISwiftHelper *apiHelper;
@end
@implementation EPMomentCell
@@ -124,23 +129,13 @@
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);
make.width.mas_greaterThanOrEqualTo(80);
}];
//
[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
@@ -160,13 +155,50 @@
//
[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.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
@@ -196,6 +228,13 @@
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;
@@ -251,40 +290,93 @@
- (void)onLikeButtonTapped {
if (!self.currentModel) return;
NSLog(@"[NewMomentCell] 点赞动态: %@", self.currentModel.dynamicId);
//
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 = self.currentModel.isLike ? @"0" : @"1"; // 0=1=
NSString *likedUid = self.currentModel.uid;
NSString *worldId = self.currentModel.worldId > 0 ? @(self.currentModel.worldId).stringValue : @"";
long worldId = self.currentModel.worldId;
[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);
// 使 Swift API Helper
[self.apiHelper likeMomentWithDynamicId:dynamicId
isLike:isLike
likedUid:likedUid
worldId:worldId
completion:^{
//
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"];
}
} dynamicId:dynamicId uid:uid status:status likedUid:likedUid worldId:worldId];
if (url) {
return [NSURL URLWithString:url];
}
}
return nil;
}
- (void)onCommentButtonTapped {
NSLog(@"[NewMomentCell] 评论");
// TODO:
}
- (void)onShareButtonTapped {
NSLog(@"[NewMomentCell] 分享");
// TODO:
- (UIImage *)photoBrowser:(SDPhotoBrowser *)browser placeholderImageForIndex:(NSInteger)index {
return [UIImageConstant defaultBannerPlaceholder];
}
// MARK: - Lazy Loading
@@ -294,10 +386,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;
@@ -356,21 +445,20 @@
- (UIButton *)likeButton {
if (!_likeButton) {
_likeButton = [self createActionButtonWithTitle:@"👍 0"];
_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 {
if (!_commentButton) {
_commentButton = [self createActionButtonWithTitle:@"💬 0"];
[_commentButton addTarget:self action:@selector(onCommentButtonTapped) forControlEvents:UIControlEventTouchUpInside];
}
return _commentButton;
}
- (UIButton *)shareButton {
return nil;
}
@@ -397,4 +485,11 @@
return _imageViews;
}
- (EPMomentAPISwiftHelper *)apiHelper {
if (!_apiHelper) {
_apiHelper = [[EPMomentAPISwiftHelper alloc] init];
}
return _apiHelper;
}
@end

View File

@@ -10,6 +10,7 @@
#import "EPMomentCell.h"
#import <MJRefresh/MJRefresh.h>
#import "YuMi-Swift.h"
#import "EPEmotionColorStorage.h"
@interface EPMomentListView () <UITableViewDelegate, UITableViewDataSource>
@@ -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<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;
}
// nilCell
}
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

View File

@@ -91,8 +91,7 @@
/// @param likedUid uid
/// @param worldId id
+ (void)momentsLike:(HttpRequestHelperCompletion)completion dynamicId:(NSString *)dynamicId uid:(NSString *)uid status:(NSString *)status likedUid:(NSString *)likedUid worldId:(NSString *)worldId {
NSString * fang = [NSString stringFromBase64String:@"ZHluYW1pYy9saWtl"];///dynamic/like
[self makeRequest:fang method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, dynamicId, uid, status, likedUid, worldId, nil];
[self makeRequest:@"dynamic/like" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, dynamicId, uid, status, likedUid, worldId, nil];
}
///

View File

@@ -77,6 +77,10 @@ typedef NS_ENUM(NSInteger, MonentsContentType) {
@property (nonatomic, copy) NSString *worldName;
///动态的id
@property (nonatomic,copy) NSString *dynamicId;
///审核状态0=审核中1=通过2=拒绝)
@property (nonatomic, assign) NSInteger status;
///情绪颜色本地标注Hex格式如 #FF0000
@property (nonatomic, copy) NSString *emotionColor;
///是否是折叠起来的
@property (nonatomic,assign) BOOL isFold;
///cell的高度