处理好评论了
This commit is contained in:
@@ -19,15 +19,12 @@ static NSString *const kCommentHeaderIdentifier = @"CommentHeader";
|
||||
static NSString *const kReplyCellIdentifier = @"ReplyCell";
|
||||
static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
/// 每次展开的回复数量
|
||||
static NSInteger const kRepliesLoadCount = 3;
|
||||
|
||||
@interface KBAICommentView () <UITableViewDataSource, UITableViewDelegate>
|
||||
|
||||
@property(nonatomic, strong) UIView *headerView;
|
||||
@property(nonatomic, strong) UILabel *titleLabel;
|
||||
@property(nonatomic, strong) UIButton *closeButton;
|
||||
@property(nonatomic, strong) UITableView *tableView;
|
||||
@property(nonatomic, strong) BaseTableView *tableView;
|
||||
@property(nonatomic, strong) KBAICommentInputView *inputView;
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<KBAICommentModel *> *comments;
|
||||
@@ -184,8 +181,21 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
NSArray *commentsArray = json[@"comments"];
|
||||
|
||||
[self.comments removeAllObjects];
|
||||
|
||||
// 获取 tableView 宽度用于计算高度
|
||||
CGFloat tableWidth = self.tableView.bounds.size.width;
|
||||
if (tableWidth <= 0) {
|
||||
tableWidth = [UIScreen mainScreen].bounds.size.width;
|
||||
}
|
||||
|
||||
for (NSDictionary *dict in commentsArray) {
|
||||
KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:dict];
|
||||
// 预先计算并缓存 Header 高度
|
||||
comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth];
|
||||
// 预先计算并缓存所有 Reply 高度
|
||||
for (KBAIReplyModel *reply in comment.replies) {
|
||||
reply.cachedCellHeight = [reply calculateCellHeightWithMaxWidth:tableWidth];
|
||||
}
|
||||
[self.comments addObject:comment];
|
||||
}
|
||||
|
||||
@@ -268,7 +278,6 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
|
||||
// 无二级评论时返回空视图
|
||||
if (state == KBAIReplyFooterStateHidden) {
|
||||
NSLog(@"[KBAICommentView] footer hidden section=%ld", (long)section);
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -286,7 +295,15 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView
|
||||
heightForHeaderInSection:(NSInteger)section {
|
||||
return UITableViewAutomaticDimension;
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
return comment.cachedHeaderHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView
|
||||
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
KBAICommentModel *comment = self.comments[indexPath.section];
|
||||
KBAIReplyModel *reply = comment.displayedReplies[indexPath.row];
|
||||
return reply.cachedCellHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView
|
||||
@@ -300,16 +317,6 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
return 30;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView
|
||||
estimatedHeightForHeaderInSection:(NSInteger)section {
|
||||
return 100;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView
|
||||
estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 60;
|
||||
}
|
||||
|
||||
#pragma mark - Footer Actions
|
||||
|
||||
- (void)handleFooterActionForSection:(NSInteger)section {
|
||||
@@ -317,9 +324,8 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
KBAIReplyFooterState state = [comment footerState];
|
||||
|
||||
switch (state) {
|
||||
case KBAIReplyFooterStateExpand:
|
||||
case KBAIReplyFooterStateLoadMore: {
|
||||
[self loadMoreRepliesForSection:section];
|
||||
case KBAIReplyFooterStateExpand: {
|
||||
[self expandRepliesForSection:section];
|
||||
break;
|
||||
}
|
||||
case KBAIReplyFooterStateCollapse: {
|
||||
@@ -331,115 +337,30 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadMoreRepliesForSection:(NSInteger)section {
|
||||
- (void)expandRepliesForSection:(NSInteger)section {
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
NSInteger currentCount = comment.displayedReplies.count;
|
||||
NSDictionary *anchor = [self captureHeaderAnchorForSection:section];
|
||||
|
||||
NSLog(@"[KBAICommentView] loadMore(before) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f rows=%ld",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
(long)currentCount);
|
||||
|
||||
// 加载更多
|
||||
[comment loadMoreReplies:kRepliesLoadCount];
|
||||
|
||||
// 计算新增的行
|
||||
NSInteger newCount = comment.displayedReplies.count;
|
||||
NSMutableArray *insertIndexPaths = [NSMutableArray array];
|
||||
for (NSInteger i = currentCount; i < newCount; i++) {
|
||||
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:i
|
||||
inSection:section]];
|
||||
}
|
||||
|
||||
// 更新(带轻微动画)
|
||||
[self.tableView beginUpdates];
|
||||
if (insertIndexPaths.count > 0) {
|
||||
[self.tableView insertRowsAtIndexPaths:insertIndexPaths
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
}
|
||||
[self.tableView endUpdates];
|
||||
[self.tableView layoutIfNeeded];
|
||||
[self restoreHeaderAnchor:anchor];
|
||||
|
||||
NSLog(@"[KBAICommentView] loadMore(after) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f rows=%ld",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
(long)comment.displayedReplies.count);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"[KBAICommentView] loadMore(next) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f viewBoundsH=%.2f",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
self.bounds.size.height);
|
||||
});
|
||||
|
||||
// 手动刷新 Footer(避免使用 reloadSections 导致滚动)
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
[footerView configureWithComment:comment];
|
||||
}
|
||||
|
||||
// 一次性展开全部回复
|
||||
[comment expandAllReplies];
|
||||
|
||||
// 直接刷新该 section
|
||||
// [UIView performWithoutAnimation:^{
|
||||
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
// }];
|
||||
}
|
||||
|
||||
- (void)collapseRepliesForSection:(NSInteger)section {
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
NSInteger rowCount = comment.displayedReplies.count;
|
||||
NSDictionary *anchor = [self captureHeaderAnchorForSection:section];
|
||||
|
||||
NSLog(@"[KBAICommentView] collapse(before) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f rows=%ld",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
(long)rowCount);
|
||||
|
||||
// 计算要删除的行
|
||||
NSMutableArray *deleteIndexPaths = [NSMutableArray array];
|
||||
for (NSInteger i = 0; i < rowCount; i++) {
|
||||
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:i
|
||||
inSection:section]];
|
||||
}
|
||||
|
||||
// 收起回复
|
||||
|
||||
// 收起全部回复
|
||||
[comment collapseReplies];
|
||||
|
||||
// 删除行(带轻微动画)
|
||||
[self.tableView beginUpdates];
|
||||
if (deleteIndexPaths.count > 0) {
|
||||
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
}
|
||||
[self.tableView endUpdates];
|
||||
[self.tableView layoutIfNeeded];
|
||||
[self restoreHeaderAnchor:anchor];
|
||||
|
||||
NSLog(@"[KBAICommentView] collapse(after) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f rows=%ld",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
(long)comment.displayedReplies.count);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"[KBAICommentView] collapse(next) section=%ld offsetY=%.2f contentSizeH=%.2f boundsH=%.2f viewBoundsH=%.2f",
|
||||
(long)section,
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height,
|
||||
self.bounds.size.height);
|
||||
});
|
||||
|
||||
// 手动刷新 Footer(避免使用 reloadSections 导致滚动)
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
[footerView configureWithComment:comment];
|
||||
}
|
||||
|
||||
// 直接刷新该 section
|
||||
// [UIView performWithoutAnimation:^{
|
||||
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
// }];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
@@ -484,20 +405,14 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
return _closeButton;
|
||||
}
|
||||
|
||||
- (UITableView *)tableView {
|
||||
- (BaseTableView *)tableView {
|
||||
if (!_tableView) {
|
||||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero
|
||||
_tableView = [[BaseTableView alloc] initWithFrame:CGRectZero
|
||||
style:UITableViewStyleGrouped];
|
||||
_tableView.dataSource = self;
|
||||
_tableView.delegate = self;
|
||||
_tableView.backgroundColor = [UIColor whiteColor];
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.estimatedRowHeight = 0;
|
||||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_tableView.estimatedSectionHeaderHeight = 0;
|
||||
_tableView.estimatedSectionFooterHeight = 0;
|
||||
_tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
|
||||
_tableView.sectionFooterHeight = UITableViewAutomaticDimension;
|
||||
_tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
||||
|
||||
// 注册 Header/Cell/Footer
|
||||
@@ -531,81 +446,4 @@ static NSInteger const kRepliesLoadCount = 3;
|
||||
return _inputView;
|
||||
}
|
||||
|
||||
#pragma mark - Header Anchor
|
||||
|
||||
- (NSDictionary *)captureHeaderAnchorForSection:(NSInteger)section {
|
||||
CGRect rect = [self.tableView rectForHeaderInSection:section];
|
||||
NSLog(@"[KBAICommentView] anchor capture(header) section=%ld rect=%@ offsetY=%.2f contentSizeH=%.2f boundsH=%.2f",
|
||||
(long)section,
|
||||
NSStringFromCGRect(rect),
|
||||
self.tableView.contentOffset.y,
|
||||
self.tableView.contentSize.height,
|
||||
self.tableView.bounds.size.height);
|
||||
if (CGRectIsEmpty(rect) || CGRectIsNull(rect)) {
|
||||
NSLog(@"[KBAICommentView] anchor capture(header) empty section=%ld",
|
||||
(long)section);
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGFloat offset = self.tableView.contentOffset.y - rect.origin.y;
|
||||
return @{
|
||||
@"section" : @(section),
|
||||
@"offset" : @(offset),
|
||||
@"fallbackOffset" : @(self.tableView.contentOffset.y)
|
||||
};
|
||||
}
|
||||
|
||||
- (void)restoreHeaderAnchor:(NSDictionary *)anchor {
|
||||
if (!anchor) {
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) skipped (nil)");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger section = [anchor[@"section"] integerValue];
|
||||
if (section < 0 || section >= self.comments.count) {
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) invalid section=%ld",
|
||||
(long)section);
|
||||
return;
|
||||
}
|
||||
|
||||
CGRect rect = [self.tableView rectForHeaderInSection:section];
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) section=%ld rect=%@",
|
||||
(long)section,
|
||||
NSStringFromCGRect(rect));
|
||||
if (CGRectIsEmpty(rect) || CGRectIsNull(rect)) {
|
||||
NSNumber *fallbackOffset = anchor[@"fallbackOffset"];
|
||||
if (!fallbackOffset) {
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) no fallback section=%ld",
|
||||
(long)section);
|
||||
return;
|
||||
}
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) fallback section=%ld offsetY=%.2f",
|
||||
(long)section,
|
||||
[fallbackOffset doubleValue]);
|
||||
[self setTableViewOffset:[fallbackOffset doubleValue]];
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat targetOffsetY = rect.origin.y + [anchor[@"offset"] doubleValue];
|
||||
NSLog(@"[KBAICommentView] anchor restore(header) target section=%ld targetY=%.2f",
|
||||
(long)section,
|
||||
targetOffsetY);
|
||||
[self setTableViewOffset:targetOffsetY];
|
||||
}
|
||||
|
||||
- (void)setTableViewOffset:(CGFloat)offsetY {
|
||||
UIEdgeInsets inset = self.tableView.adjustedContentInset;
|
||||
CGFloat minOffsetY = -inset.top;
|
||||
CGFloat maxOffsetY =
|
||||
MAX(minOffsetY, self.tableView.contentSize.height + inset.bottom -
|
||||
self.tableView.bounds.size.height);
|
||||
CGFloat targetOffsetY = MIN(MAX(offsetY, minOffsetY), maxOffsetY);
|
||||
NSLog(@"[KBAICommentView] set offset target=%.2f min=%.2f max=%.2f",
|
||||
targetOffsetY,
|
||||
minOffsetY,
|
||||
maxOffsetY);
|
||||
[self.tableView setContentOffset:CGPointMake(0, targetOffsetY)
|
||||
animated:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user