处理好评论了

This commit is contained in:
2026-01-16 19:09:54 +08:00
parent 619c02f236
commit 3dfb8f31e2
9 changed files with 211 additions and 402 deletions

View File

@@ -67,16 +67,6 @@
forState:UIControlStateNormal];
break;
}
case KBAIReplyFooterStateLoadMore: {
self.actionButton.hidden = NO;
NSInteger remaining =
comment.totalReplyCount - comment.displayedReplies.count;
title =
[NSString stringWithFormat:@"展开更多回复 (%ld条)", (long)remaining];
[self.actionButton setImage:[UIImage systemImageNamed:@"chevron.down"]
forState:UIControlStateNormal];
break;
}
case KBAIReplyFooterStateCollapse: {
self.actionButton.hidden = NO;
title = @"收起";

View File

@@ -62,7 +62,7 @@
make.left.equalTo(self.userNameLabel);
make.top.equalTo(self.timeLabel.mas_bottom).offset(8);
make.right.equalTo(self.contentView).offset(-50);
make.bottom.equalTo(self.contentView).offset(-12);
make.bottom.equalTo(self.contentView).offset(-12).priority(MASLayoutPriorityDefaultHigh);
}];
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {

View File

@@ -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

View File

@@ -56,7 +56,8 @@
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentLabel);
make.top.equalTo(self.contentLabel.mas_bottom).offset(4);
make.bottom.equalTo(self.contentView).offset(-8);
// TableView
make.bottom.equalTo(self.contentView).offset(-8).priority(MASLayoutPriorityDefaultHigh);
}];
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {