feat: 添加 EPMomentPublishViewController 以支持图文发布功能
主要变更: 1. 新增 EPMomentPublishViewController.h 和 EPMomentPublishViewController.m 文件,提供图文发布页面的 UI 和逻辑。 2. 实现了发布按钮、文本输入框、图片选择功能,支持最多选择 9 张图片。 3. 集成了 TZImagePickerController 以便于用户选择图片。 4. 更新了 EPMomentViewController,添加了跳转到发布页面的逻辑。 此功能旨在提升用户体验,简化图文发布流程。
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// EPMomentPublishViewController.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-10.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// EP 版:图文发布页面
|
||||
@interface EPMomentPublishViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
206
YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m
Normal file
206
YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m
Normal file
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// EPMomentPublishViewController.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-10.
|
||||
//
|
||||
|
||||
#import "EPMomentPublishViewController.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TZImagePickerController/TZImagePickerController.h>
|
||||
#import "DJDKMIMOMColor.h"
|
||||
#import "SZTextView.h"
|
||||
|
||||
@interface EPMomentPublishViewController () <UICollectionViewDataSource, UICollectionViewDelegate, TZImagePickerControllerDelegate, UITextViewDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIView *navView;
|
||||
@property (nonatomic, strong) UIButton *backButton;
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
@property (nonatomic, strong) UIButton *publishButton;
|
||||
|
||||
@property (nonatomic, strong) UIView *contentView;
|
||||
@property (nonatomic, strong) SZTextView *textView;
|
||||
@property (nonatomic, strong) UILabel *limitLabel;
|
||||
@property (nonatomic, strong) UIView *lineView;
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
@property (nonatomic, strong) NSMutableArray<UIImage *> *images;
|
||||
@property (nonatomic, strong) NSMutableArray *selectedAssets; // TZImagePicker 已选资源
|
||||
|
||||
@end
|
||||
|
||||
@implementation EPMomentPublishViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor colorWithRed:0x0C/255.0 green:0x05/255.0 blue:0x27/255.0 alpha:1.0];
|
||||
[self setupUI];
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
[self.view addSubview:self.navView];
|
||||
[self.view addSubview:self.contentView];
|
||||
[self.navView addSubview:self.backButton];
|
||||
[self.navView addSubview:self.titleLabel];
|
||||
// 发布按钮移到底部
|
||||
[self.contentView addSubview:self.textView];
|
||||
[self.contentView addSubview:self.limitLabel];
|
||||
[self.contentView addSubview:self.lineView];
|
||||
[self.contentView addSubview:self.collectionView];
|
||||
[self.contentView addSubview:self.publishButton];
|
||||
|
||||
[self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.trailing.top.equalTo(self.view);
|
||||
make.height.mas_equalTo(kNavigationHeight);
|
||||
}];
|
||||
[self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.equalTo(self.view).offset(10);
|
||||
make.top.mas_equalTo(statusbarHeight);
|
||||
make.size.mas_equalTo(CGSizeMake(44, 44));
|
||||
}];
|
||||
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self.navView);
|
||||
make.centerY.equalTo(self.backButton);
|
||||
}];
|
||||
// 发布按钮约束移到底部
|
||||
[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.trailing.equalTo(self.view);
|
||||
make.top.equalTo(self.navView.mas_bottom);
|
||||
make.bottom.equalTo(self.view);
|
||||
}];
|
||||
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.trailing.equalTo(self.contentView).inset(15);
|
||||
make.top.equalTo(self.contentView).offset(10);
|
||||
make.height.mas_equalTo(150);
|
||||
}];
|
||||
[self.limitLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.textView.mas_bottom).offset(5);
|
||||
make.trailing.equalTo(self.textView);
|
||||
}];
|
||||
[self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.limitLabel.mas_bottom).offset(10);
|
||||
make.leading.trailing.equalTo(self.textView);
|
||||
make.height.mas_equalTo(1);
|
||||
}];
|
||||
[self.collectionView 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(110);
|
||||
}];
|
||||
|
||||
// 底部发布按钮
|
||||
[self.publishButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.trailing.equalTo(self.view).inset(20);
|
||||
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-20);
|
||||
make.height.mas_equalTo(50);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onBack {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)onPublish {
|
||||
// TODO: 挂接实际发布逻辑
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionView
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.images.count + 1; // 最后一个是添加按钮
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ep.publish.cell" forIndexPath:indexPath];
|
||||
cell.contentView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.06];
|
||||
cell.contentView.layer.cornerRadius = 12;
|
||||
// 清空复用子视图,避免加号被覆盖
|
||||
for (UIView *sub in cell.contentView.subviews) { [sub removeFromSuperview]; }
|
||||
BOOL showAdd = (self.images.count < 9) && (indexPath.item == self.images.count);
|
||||
if (showAdd) {
|
||||
UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mine_user_info_album_add"]];
|
||||
[cell.contentView addSubview:iv];
|
||||
[iv mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(cell.contentView); make.size.mas_equalTo(CGSizeMake(24, 24)); }];
|
||||
} else {
|
||||
UIImageView *iv = [[UIImageView alloc] init];
|
||||
iv.contentMode = UIViewContentModeScaleAspectFill;
|
||||
iv.layer.masksToBounds = YES;
|
||||
[cell.contentView addSubview:iv];
|
||||
[iv mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(cell.contentView); }];
|
||||
NSInteger idx = MIN(indexPath.item, (NSInteger)self.images.count - 1);
|
||||
if (idx >= 0 && idx < self.images.count) iv.image = self.images[idx];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.item == self.images.count) {
|
||||
TZImagePickerController *picker = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self];
|
||||
picker.allowPickingVideo = NO;
|
||||
picker.allowTakeVideo = NO;
|
||||
picker.selectedAssets = self.selectedAssets; // 预选
|
||||
picker.maxImagesCount = 9; // 总上限
|
||||
[self presentViewController:picker animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - TZImagePickerControllerDelegate
|
||||
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos {
|
||||
// 合并选择:在已有基础上追加,最多 9 张
|
||||
for (NSInteger i = 0; i < assets.count; i++) {
|
||||
id asset = assets[i];
|
||||
UIImage *img = [photos xpSafeObjectAtIndex:i] ?: photos[i];
|
||||
if (![self.selectedAssets containsObject:asset] && self.images.count < 9) {
|
||||
[self.selectedAssets addObject:asset];
|
||||
[self.images addObject:img];
|
||||
}
|
||||
}
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - UITextViewDelegate
|
||||
- (void)textViewDidChange:(UITextView *)textView {
|
||||
if (textView.text.length > 500) {
|
||||
textView.text = [textView.text substringToIndex:500];
|
||||
}
|
||||
self.limitLabel.text = [NSString stringWithFormat:@"%lu/500", (unsigned long)textView.text.length];
|
||||
}
|
||||
|
||||
#pragma mark - Lazy
|
||||
|
||||
- (UIView *)navView { if (!_navView) { _navView = [UIView new]; _navView.backgroundColor = [UIColor clearColor]; } return _navView; }
|
||||
- (UIButton *)backButton { if (!_backButton) { _backButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_backButton setImage:[UIImage imageNamed:@"common_nav_back"] forState:UIControlStateNormal]; [_backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside]; } return _backButton; }
|
||||
- (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [UILabel new]; _titleLabel.text = @"图文发布"; _titleLabel.textColor = [DJDKMIMOMColor mainTextColor]; _titleLabel.font = [UIFont systemFontOfSize:17]; } return _titleLabel; }
|
||||
- (UIButton *)publishButton {
|
||||
if (!_publishButton) {
|
||||
_publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_publishButton setTitle:@"发布" forState:UIControlStateNormal];
|
||||
[_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
_publishButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium];
|
||||
_publishButton.layer.cornerRadius = 25;
|
||||
_publishButton.layer.masksToBounds = YES;
|
||||
// 渐变背景:从浅紫到深紫
|
||||
CAGradientLayer *gradient = [CAGradientLayer layer];
|
||||
gradient.colors = @[(__bridge id)[UIColor colorWithRed:0.6 green:0.3 blue:0.8 alpha:1.0].CGColor,
|
||||
(__bridge id)[UIColor colorWithRed:0.3 green:0.1 blue:0.5 alpha:1.0].CGColor];
|
||||
gradient.startPoint = CGPointMake(0, 0);
|
||||
gradient.endPoint = CGPointMake(1, 0);
|
||||
gradient.frame = CGRectMake(0, 0, 1, 1);
|
||||
[_publishButton.layer insertSublayer:gradient atIndex:0];
|
||||
[_publishButton addTarget:self action:@selector(onPublish) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _publishButton;
|
||||
}
|
||||
- (UIView *)contentView { if (!_contentView) { _contentView = [UIView new]; _contentView.backgroundColor = [UIColor clearColor]; } return _contentView; }
|
||||
- (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; }
|
||||
- (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; }
|
||||
|
||||
@end
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "EPMomentCell.h"
|
||||
#import "EPMomentListView.h"
|
||||
#import "EPMomentPublishViewController.h"
|
||||
|
||||
@interface EPMomentViewController ()
|
||||
|
||||
@@ -88,8 +89,9 @@
|
||||
|
||||
- (void)onPublishButtonTapped {
|
||||
NSLog(@"[EPMomentViewController] 发布按钮点击");
|
||||
// TODO: 跳转到发布页面
|
||||
[self showAlertWithMessage:@"发布功能开发中"];
|
||||
EPMomentPublishViewController *vc = [[EPMomentPublishViewController alloc] init];
|
||||
vc.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[self.navigationController presentViewController:vc animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString *)message {
|
||||
|
@@ -21,13 +21,10 @@ typedef NS_ENUM(NSInteger, EPMomentListSourceType) {
|
||||
/// 统一封装 Moments 列表 API
|
||||
@interface EPMomentAPIHelper : BaseMvpPresenter
|
||||
|
||||
/// 拉取动态列表(默认 types:"0,2" 图片+文字)
|
||||
/// page 从 0 开始,pageSize > 0
|
||||
/// completion 返回 data 数组(字典数组) 或错误
|
||||
- (void)fetchMomentsWithType:(EPMomentListSourceType)sourceType
|
||||
page:(NSInteger)page
|
||||
pageSize:(NSInteger)pageSize
|
||||
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSInteger code, NSString * _Nullable msg))completion;
|
||||
/// 拉取最新动态列表(默认 types:"0,2" 图片+文字)
|
||||
- (void)fetchLatestMomentsWithNextID:(NSString *)nextID
|
||||
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSString *nextMomentID))completion
|
||||
failure:(void(^)(NSInteger code, NSString * _Nullable msg))failure;
|
||||
|
||||
@end
|
||||
|
||||
|
@@ -13,18 +13,6 @@
|
||||
|
||||
|
||||
@implementation EPMomentAPIHelper
|
||||
|
||||
- (void)fetchMomentsWithType:(EPMomentListSourceType)sourceType
|
||||
page:(NSInteger)page
|
||||
pageSize:(NSInteger)pageSize
|
||||
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSInteger code, NSString * _Nullable msg))completion {
|
||||
// 兼容后端从 1 开始分页:若收到 0 则转成 1
|
||||
NSInteger requestPage = page <= 0 ? 1 : page;
|
||||
NSString *pageStr = [NSString stringWithFormat:@"%ld", (long)requestPage];
|
||||
NSString *pageSizeStr = [NSString stringWithFormat:@"%ld", (long)pageSize];
|
||||
NSString *types = @"0,2"; // 图片+文字
|
||||
|
||||
if (sourceType == EPMomentListSourceTypeRecommend) {
|
||||
// [Api momentsRecommendList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
// if (code == 200 && data.data) {
|
||||
// NSArray *array = [MomentsInfoModel modelsWithArray:data.data];
|
||||
@@ -33,24 +21,20 @@
|
||||
// if (completion) completion(@[], code, msg);
|
||||
// }
|
||||
// } page:pageStr pageSize:pageSizeStr types:types];
|
||||
[Api momentsLatestList:[self createHttpCompletion:^(BaseModel * _Nonnull data) {
|
||||
MomentsListInfoModel *listInfo = [MomentsListInfoModel modelWithDictionary:data.data];
|
||||
if (completion) completion(listInfo.dynamicList ?: @[], 200, @"success");
|
||||
} fail:^(NSInteger code, NSString * _Nullable msg) {
|
||||
if (completion) completion(@[], code, msg);
|
||||
}] dynamicId:@"" pageSize:pageSizeStr types:types];
|
||||
|
||||
} else {
|
||||
// 预留:我的动态列表(暂时复用推荐接口,后续替换为真正的“我的动态”API)
|
||||
[Api momentsRecommendList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||||
if (code == 200 && data.data) {
|
||||
NSArray *array = [MomentsInfoModel modelsWithArray:data.data];
|
||||
if (completion) completion(array ?: @[], 200, @"success");
|
||||
} else {
|
||||
if (completion) completion(@[], code, msg);
|
||||
}
|
||||
} page:pageStr pageSize:pageSizeStr types:types];
|
||||
}
|
||||
|
||||
- (void)fetchLatestMomentsWithNextID:(NSString *)nextID
|
||||
completion:(void (^)(NSArray <MomentsInfoModel *>* _Nullable list, NSString *nextMomentID))completion
|
||||
failure:(void(^)(NSInteger code, NSString * _Nullable msg))failure {
|
||||
NSString *pageSizeStr = @"20";
|
||||
NSString *types = @"0,2"; // 图片+文字
|
||||
|
||||
[Api momentsLatestList:[self createHttpCompletion:^(BaseModel * _Nonnull data) {
|
||||
MomentsListInfoModel *listInfo = [MomentsListInfoModel modelWithDictionary:data.data];
|
||||
if (completion) completion(listInfo.dynamicList ?: @[],
|
||||
listInfo.nextDynamicId);
|
||||
} fail:^(NSInteger code, NSString * _Nullable msg) {
|
||||
if (failure) failure(code, msg);
|
||||
}] dynamicId:nextID pageSize:pageSizeStr types:types];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -10,7 +10,7 @@
|
||||
#import "MomentsInfoModel.h"
|
||||
#import "AccountInfoStorage.h"
|
||||
#import "Api+Moments.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "NetImageView.h"
|
||||
|
||||
@interface EPMomentCell ()
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
/// 卡片容器
|
||||
@property (nonatomic, strong) UIView *cardView;
|
||||
|
||||
/// 头像
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
/// 头像(网络)
|
||||
@property (nonatomic, strong) NetImageView *avatarImageView;
|
||||
|
||||
/// 用户名
|
||||
@property (nonatomic, strong) UILabel *nameLabel;
|
||||
@@ -31,8 +31,9 @@
|
||||
/// 内容标签
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
|
||||
/// 图片容器(可选)
|
||||
/// 图片容器(九宫格)
|
||||
@property (nonatomic, strong) UIView *imagesContainer;
|
||||
@property (nonatomic, strong) NSMutableArray<NetImageView *> *imageViews;
|
||||
|
||||
/// 底部操作栏
|
||||
@property (nonatomic, strong) UIView *actionBar;
|
||||
@@ -43,8 +44,7 @@
|
||||
/// 评论按钮
|
||||
@property (nonatomic, strong) UIButton *commentButton;
|
||||
|
||||
/// 分享按钮
|
||||
@property (nonatomic, strong) UIButton *shareButton;
|
||||
// 分享按钮已移除
|
||||
|
||||
/// 当前数据模型
|
||||
@property (nonatomic, strong) MomentsInfoModel *currentModel;
|
||||
@@ -108,11 +108,19 @@
|
||||
make.top.equalTo(self.avatarImageView.mas_bottom).offset(12);
|
||||
}];
|
||||
|
||||
// 图片九宫格
|
||||
[self.cardView addSubview:self.imagesContainer];
|
||||
[self.imagesContainer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.cardView).offset(15);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
make.top.equalTo(self.contentLabel.mas_bottom).offset(12);
|
||||
}];
|
||||
|
||||
// 底部操作栏
|
||||
[self.cardView addSubview:self.actionBar];
|
||||
[self.actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.cardView);
|
||||
make.top.equalTo(self.contentLabel.mas_bottom).offset(15);
|
||||
make.top.equalTo(self.imagesContainer.mas_bottom).offset(12);
|
||||
make.height.mas_equalTo(50);
|
||||
make.bottom.equalTo(self.cardView).offset(-8);
|
||||
}];
|
||||
@@ -133,13 +141,7 @@
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
|
||||
// 分享按钮
|
||||
[self.actionBar addSubview:self.shareButton];
|
||||
[self.shareButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.actionBar).offset(-15);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
// 右侧占位(去掉分享按钮后,右边保持留白)
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
@@ -156,17 +158,74 @@
|
||||
// 配置内容
|
||||
self.contentLabel.text = model.content ?: @"";
|
||||
|
||||
// 配置点赞数
|
||||
[self.likeButton setTitle:[NSString stringWithFormat:@"👍 %ld", (long)model.likeCount] forState:UIControlStateNormal];
|
||||
// 配置图片九宫格
|
||||
[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.commentButton setTitle:[NSString stringWithFormat:@"💬 %ld", (long)model.commentCount] forState:UIControlStateNormal];
|
||||
|
||||
// 配置分享
|
||||
[self.shareButton setTitle:@"🔗 分享" forState:UIControlStateNormal];
|
||||
|
||||
// TODO: 加载头像图片
|
||||
// [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:model.avatar]];
|
||||
self.avatarImageView.imageUrl = model.avatar;
|
||||
}
|
||||
|
||||
// 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.left.equalTo(self.cardView).offset(15);
|
||||
make.right.equalTo(self.cardView).offset(-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.left.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.left.equalTo(self.cardView).offset(15);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
make.top.equalTo(self.contentLabel.mas_bottom).offset(12);
|
||||
make.height.mas_equalTo(height);
|
||||
}];
|
||||
}
|
||||
|
||||
/// 格式化时间戳为相对时间
|
||||
@@ -249,7 +308,10 @@
|
||||
|
||||
- (UIImageView *)avatarImageView {
|
||||
if (!_avatarImageView) {
|
||||
_avatarImageView = [[UIImageView alloc] init];
|
||||
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;
|
||||
@@ -312,11 +374,7 @@
|
||||
}
|
||||
|
||||
- (UIButton *)shareButton {
|
||||
if (!_shareButton) {
|
||||
_shareButton = [self createActionButtonWithTitle:@"🔗 分享"];
|
||||
[_shareButton addTarget:self action:@selector(onShareButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _shareButton;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIButton *)createActionButtonWithTitle:(NSString *)title {
|
||||
@@ -327,4 +385,19 @@
|
||||
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
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "EPMomentListView.h"
|
||||
#import "EPMomentCell.h"
|
||||
#import <MJRefresh/MJRefresh.h>
|
||||
|
||||
|
||||
@interface EPMomentListView () <UITableViewDelegate, UITableViewDataSource>
|
||||
@@ -16,9 +17,8 @@
|
||||
@property (nonatomic, strong) UIRefreshControl *refreshControl;
|
||||
@property (nonatomic, strong) NSMutableArray *mutableRawList;
|
||||
@property (nonatomic, strong) EPMomentAPIHelper *api;
|
||||
@property (nonatomic, assign) NSInteger currentPage;
|
||||
@property (nonatomic, assign) BOOL isLoading;
|
||||
|
||||
@property (nonatomic, copy) NSString *nextID;
|
||||
@end
|
||||
|
||||
@implementation EPMomentListView
|
||||
@@ -29,7 +29,6 @@
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
_api = [[EPMomentAPIHelper alloc] init];
|
||||
_mutableRawList = [NSMutableArray array];
|
||||
_currentPage = 0; // 若后端从1开始,这里会在首次请求时自增到1
|
||||
_sourceType = EPMomentListSourceTypeRecommend;
|
||||
|
||||
[self addSubview:self.tableView];
|
||||
@@ -45,9 +44,10 @@
|
||||
}
|
||||
|
||||
- (void)reloadFirstPage {
|
||||
self.currentPage = 0;
|
||||
self.nextID = @"";
|
||||
[self.mutableRawList removeAllObjects];
|
||||
[self.tableView reloadData];
|
||||
[self.tableView.mj_footer resetNoMoreData];
|
||||
[self requestNextPage];
|
||||
}
|
||||
|
||||
@@ -55,25 +55,41 @@
|
||||
if (self.isLoading) return;
|
||||
self.isLoading = YES;
|
||||
|
||||
NSLog(@"[EPMomentListView] 请求页码: %ld", (long)self.currentPage);
|
||||
@kWeakify(self);
|
||||
[self.api fetchMomentsWithType:self.sourceType page:self.currentPage pageSize:20 completion:^(NSArray <MomentsInfoModel *>* _Nullable list, NSInteger code, NSString * _Nullable msg) {
|
||||
[self.api fetchLatestMomentsWithNextID:self.nextID
|
||||
completion:^(NSArray<MomentsInfoModel *> * _Nullable list, NSString * _Nonnull nextMomentID) {
|
||||
@kStrongify(self);
|
||||
self.isLoading = NO;
|
||||
[self.refreshControl endRefreshing];
|
||||
NSLog(@"[EPMomentListView] 返回 code=%ld, count=%lu", (long)code, (unsigned long)list.count);
|
||||
if (code == 200 && list.count > 0) {
|
||||
[self endLoading];
|
||||
if (list.count > 0) {
|
||||
self.nextID = nextMomentID;
|
||||
[self.mutableRawList addObjectsFromArray:list];
|
||||
self.currentPage++;
|
||||
[self.tableView reloadData];
|
||||
} else if (code == 200 && list.count == 0 && self.currentPage == 0) {
|
||||
// 如果第一页就为空,尝试从1开始(兼容某些后端从1计数)
|
||||
self.currentPage = 1;
|
||||
[self requestNextPage];
|
||||
if (nextMomentID.length > 0) {
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
} else {
|
||||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||
}
|
||||
} else {
|
||||
// TODO: 后续补充空数据页面
|
||||
if (self.nextID.length == 0) {
|
||||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||
} else {
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
}
|
||||
}
|
||||
} failure:^(NSInteger code, NSString * _Nullable msg) {
|
||||
@kStrongify(self);
|
||||
[self endLoading];
|
||||
// TODO: 完全没有数据情况下,后续补充数据异常页面
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)endLoading {
|
||||
self.isLoading = NO;
|
||||
[self.refreshControl endRefreshing];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
@@ -123,9 +139,24 @@
|
||||
_tableView.estimatedRowHeight = 200;
|
||||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_tableView.showsVerticalScrollIndicator = NO;
|
||||
_tableView.contentInset = UIEdgeInsetsMake(10, 0, 10, 0);
|
||||
// 底部留出更高空间,避免被悬浮 TabBar 遮挡
|
||||
_tableView.contentInset = UIEdgeInsetsMake(10, 0, 120, 0);
|
||||
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(10, 0, 120, 0);
|
||||
[_tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"NewMomentCell"];
|
||||
_tableView.refreshControl = self.refreshControl;
|
||||
|
||||
// MJRefresh Footer - 加载更多
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self.isLoading && self.nextID.length > 0) {
|
||||
[self requestNextPage];
|
||||
} else if (self.nextID.length == 0) {
|
||||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||
} else {
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
}
|
||||
}];
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
Reference in New Issue
Block a user