tableview倒置

This commit is contained in:
2026-01-31 22:40:50 +08:00
parent d2f582b7f8
commit 6ae504823b
35 changed files with 2340 additions and 128 deletions

View File

@@ -127,6 +127,60 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
#pragma mark - Public Methods
- (void)setInverted:(BOOL)inverted {
if (_inverted == inverted) {
return;
}
_inverted = inverted;
self.tableView.transform = inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity;
[self updateContentBottomInset:self.contentBottomInset];
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
}
static inline CGFloat KBChatAbsTimeInterval(NSTimeInterval interval) {
return interval < 0 ? -interval : interval;
}
- (BOOL)shouldInsertTimestampBetweenMessage:(KBAiChatMessage *)message
andReference:(KBAiChatMessage *)reference {
if (!message.timestamp || !reference.timestamp) {
return YES;
}
NSTimeInterval interval = KBChatAbsTimeInterval([message.timestamp timeIntervalSinceDate:reference.timestamp]);
if (interval >= kTimestampInterval) {
return YES;
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *a = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear
fromDate:reference.timestamp];
NSDateComponents *b = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear
fromDate:message.timestamp];
return ![a isEqual:b];
}
- (NSInteger)firstNonTimeIndex {
for (NSInteger i = 0; i < self.messages.count; i++) {
if (self.messages[i].type != KBAiChatMessageTypeTime) {
return i;
}
}
return NSNotFound;
}
- (NSInteger)lastNonTimeIndexBeforeIndex:(NSInteger)index {
NSInteger maxIndex = MIN(index, self.messages.count);
for (NSInteger i = maxIndex - 1; i >= 0; i--) {
if (self.messages[i].type != KBAiChatMessageTypeTime) {
return i;
}
}
return NSNotFound;
}
- (void)addUserMessage:(NSString *)text {
KBAiChatMessage *message = [KBAiChatMessage userMessageWithText:text];
[self addMessage:message autoScroll:YES];
@@ -138,17 +192,31 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
- (void)updateLastUserMessage:(NSString *)text {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
message.text = text;
message.isLoading = NO;
message.isComplete = YES;
//
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
return;
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
message.text = text;
message.isLoading = NO;
message.isComplete = YES;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
return;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser && message.isLoading) {
message.text = text;
message.isLoading = NO;
message.isComplete = YES;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
return;
}
}
}
}
@@ -172,24 +240,44 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
- (void)updateLastAssistantMessage:(NSString *)text {
// AI
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && !message.isComplete) {
NSLog(@"[KBChatTableView] 更新最后一条 AI 消息 - 索引: %ld, 文本长度: %lu, needsTypewriter: %d",
(long)i, (unsigned long)text.length, message.needsTypewriterEffect);
message.text = text;
// Cell Cell
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[KBChatAssistantMessageCell class]]) {
NSLog(@"[KBChatTableView] 找到 Cell直接配置消息");
// configureWithMessage Cell 使
[cell configureWithMessage:message];
} else {
NSLog(@"[KBChatTableView] 未找到 Cell 或类型不匹配");
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && !message.isComplete) {
NSLog(@"[KBChatTableView] 更新最后一条 AI 消息 - 索引: %ld, 文本长度: %lu, needsTypewriter: %d",
(long)i, (unsigned long)text.length, message.needsTypewriterEffect);
message.text = text;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[KBChatAssistantMessageCell class]]) {
NSLog(@"[KBChatTableView] 找到 Cell直接配置消息");
[cell configureWithMessage:message];
cell.contentView.transform = self.inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity;
} else {
NSLog(@"[KBChatTableView] 未找到 Cell 或类型不匹配");
}
return;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && !message.isComplete) {
NSLog(@"[KBChatTableView] 更新最后一条 AI 消息 - 索引: %ld, 文本长度: %lu, needsTypewriter: %d",
(long)i, (unsigned long)text.length, message.needsTypewriterEffect);
message.text = text;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[KBChatAssistantMessageCell class]]) {
NSLog(@"[KBChatTableView] 找到 Cell直接配置消息");
[cell configureWithMessage:message];
} else {
NSLog(@"[KBChatTableView] 未找到 Cell 或类型不匹配");
}
return;
}
return;
}
}
@@ -199,33 +287,63 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
- (void)markLastAssistantMessageComplete {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant) {
NSLog(@"[KBChatTableView] 标记消息完成 - 索引: %ld, 文本: %@", (long)i, message.text);
message.isComplete = YES;
message.needsTypewriterEffect = NO; //
// Cell
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
return;
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant) {
NSLog(@"[KBChatTableView] 标记消息完成 - 索引: %ld, 文本: %@", (long)i, message.text);
message.isComplete = YES;
message.needsTypewriterEffect = NO;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
return;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant) {
NSLog(@"[KBChatTableView] 标记消息完成 - 索引: %ld, 文本: %@", (long)i, message.text);
message.isComplete = YES;
message.needsTypewriterEffect = NO; //
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
return;
}
}
}
}
- (void)markLastUserMessageLoadingComplete {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser) {
message.isLoading = NO;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView layoutIfNeeded];
[self scrollToBottomAnimated:NO];
return;
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser) {
message.isLoading = NO;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView layoutIfNeeded];
[self scrollToBottomAnimated:NO];
return;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeUser) {
message.isLoading = NO;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView layoutIfNeeded];
[self scrollToBottomAnimated:NO];
return;
}
}
}
}
@@ -237,14 +355,41 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
- (void)removeLoadingAssistantMessage {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && message.isLoading) {
[self.messages removeObjectAtIndex:i];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
NSLog(@"[KBChatTableView] 移除 loading AI 消息,索引: %ld", (long)i);
break;
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && message.isLoading) {
if (self.playingCellIndexPath) {
if (self.playingCellIndexPath.row == i) {
[self stopPlayingAudio];
} else if (self.playingCellIndexPath.row > i) {
self.playingCellIndexPath = [NSIndexPath indexPathForRow:self.playingCellIndexPath.row - 1 inSection:0];
}
}
[self.messages removeObjectAtIndex:i];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
NSLog(@"[KBChatTableView] 移除 loading AI 消息,索引: %ld", (long)i);
break;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && message.isLoading) {
if (self.playingCellIndexPath) {
if (self.playingCellIndexPath.row == i) {
[self stopPlayingAudio];
} else if (self.playingCellIndexPath.row > i) {
self.playingCellIndexPath = [NSIndexPath indexPathForRow:self.playingCellIndexPath.row - 1 inSection:0];
}
}
[self.messages removeObjectAtIndex:i];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
NSLog(@"[KBChatTableView] 移除 loading AI 消息,索引: %ld", (long)i);
break;
}
}
}
}
@@ -258,6 +403,12 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
// 使 layoutIfNeeded
[self.tableView layoutIfNeeded];
if (self.inverted) {
CGFloat minOffsetY = -self.tableView.contentInset.top;
[self.tableView setContentOffset:CGPointMake(0, minOffsetY) animated:animated];
return;
}
//
CGFloat contentHeight = self.tableView.contentSize.height;
@@ -299,7 +450,11 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
// contentInset
UIEdgeInsets insets = UIEdgeInsetsZero;
insets.bottom = bottomInset;
if (self.inverted) {
insets.top = bottomInset;
} else {
insets.bottom = bottomInset;
}
self.tableView.contentInset = insets;
self.tableView.scrollIndicatorInsets = insets;
@@ -319,22 +474,46 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
NSInteger oldCount = self.messages.count;
[self insertMessageWithTimestamp:message];
NSArray<KBAiChatMessage *> *itemsToInsert = nil;
NSInteger insertIndex = oldCount;
if (self.inverted) {
itemsToInsert = [self invertedItemsForNewMessageAtBeginning:message];
insertIndex = 0;
if (itemsToInsert.count > 0) {
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(insertIndex, itemsToInsert.count)];
[self.messages insertObjects:itemsToInsert atIndexes:set];
}
} else {
[self insertMessageWithTimestamp:message];
itemsToInsert = nil;
insertIndex = oldCount;
}
NSInteger newCount = self.messages.count;
NSInteger insertedCount = newCount - oldCount;
if (insertedCount > 0) {
NSMutableArray *indexPaths = [NSMutableArray array];
for (NSInteger i = oldCount; i < newCount; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
if (self.inverted && self.playingCellIndexPath) {
self.playingCellIndexPath = [NSIndexPath indexPathForRow:self.playingCellIndexPath.row + insertedCount inSection:0];
}
NSMutableArray *indexPaths = [NSMutableArray array];
if (self.inverted) {
for (NSInteger i = 0; i < insertedCount; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
} else {
for (NSInteger i = insertIndex; i < newCount; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
}
//
[self.tableView layoutIfNeeded];
[self.tableView insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
[self updateFooterVisibility];
@@ -362,6 +541,9 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
- (void)reloadWithMessages:(NSArray<KBAiChatMessage *> *)messages
keepOffset:(BOOL)keepOffset
scrollToBottom:(BOOL)scrollToBottom {
if (self.inverted) {
keepOffset = NO;
}
CGFloat oldContentHeight = self.tableView.contentSize.height;
CGFloat oldOffsetY = self.tableView.contentOffset.y;
KBAiChatMessage *anchorMessage = nil;
@@ -400,8 +582,14 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
[self.messages removeAllObjects];
if (messages.count > 0) {
for (KBAiChatMessage *message in messages) {
[self insertMessageWithTimestamp:message];
if (self.inverted) {
NSArray<KBAiChatMessage *> *rebuilt = [self invertedMessagesByInsertingTimestamps:messages
startingReference:nil];
[self.messages addObjectsFromArray:rebuilt];
} else {
for (KBAiChatMessage *message in messages) {
[self insertMessageWithTimestamp:message];
}
}
}
@@ -432,6 +620,9 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
NSLog(@"[KBChatTableView] ========== reloadWithMessages 结束 ==========");
if (keepOffset) {
if (self.inverted) {
keepOffset = NO;
}
CGFloat offsetY = oldOffsetY;
if (anchorMessage) {
NSInteger newIndex = [self.messages indexOfObjectIdenticalTo:anchorMessage];
@@ -480,6 +671,36 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
[self.messages addObject:message];
}
- (NSArray<KBAiChatMessage *> *)invertedItemsForNewMessageAtBeginning:(KBAiChatMessage *)message {
NSMutableArray<KBAiChatMessage *> *result = [NSMutableArray array];
NSInteger firstIndex = [self firstNonTimeIndex];
KBAiChatMessage *reference = (firstIndex != NSNotFound) ? self.messages[firstIndex] : nil;
[result addObject:message];
if (!reference || [self shouldInsertTimestampBetweenMessage:message andReference:reference]) {
KBAiChatMessage *timeMessage = [KBAiChatMessage timeMessageWithTimestamp:message.timestamp];
[result addObject:timeMessage];
}
return result;
}
- (NSArray<KBAiChatMessage *> *)invertedMessagesByInsertingTimestamps:(NSArray<KBAiChatMessage *> *)messages
startingReference:(nullable KBAiChatMessage *)reference {
NSMutableArray<KBAiChatMessage *> *result = [NSMutableArray array];
KBAiChatMessage *ref = reference;
for (KBAiChatMessage *msg in messages) {
if (ref && [self shouldInsertTimestampBetweenMessage:msg andReference:ref]) {
KBAiChatMessage *timeMessage = [KBAiChatMessage timeMessageWithTimestamp:msg.timestamp];
[result addObject:timeMessage];
} else if (!ref) {
//
}
[result addObject:msg];
ref = msg;
}
return result;
}
- (NSInteger)firstVisibleNonTimeRowExcludingMessage:(KBAiChatMessage *)excludedMessage {
NSArray<NSIndexPath *> *visible = self.tableView.indexPathsForVisibleRows;
if (visible.count == 0) { return NSNotFound; }
@@ -605,6 +826,48 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}];
}
- (void)appendHistoryMessages:(NSArray<KBAiChatMessage *> *)messages
openingMessage:(nullable KBAiChatMessage *)openingMessage {
if (messages.count == 0) {
return;
}
NSInteger insertIndex = self.messages.count;
if (openingMessage) {
NSInteger openingIndex = [self.messages indexOfObjectIdenticalTo:openingMessage];
if (openingIndex != NSNotFound) {
insertIndex = openingIndex;
}
}
NSInteger referenceIndex = [self lastNonTimeIndexBeforeIndex:insertIndex];
KBAiChatMessage *reference = (referenceIndex != NSNotFound) ? self.messages[referenceIndex] : nil;
NSArray<KBAiChatMessage *> *toInsert = nil;
if (self.inverted) {
toInsert = [self invertedMessagesByInsertingTimestamps:messages startingReference:reference];
} else {
toInsert = [self messagesByInsertingTimestamps:messages];
}
[UIView performWithoutAnimation:^{
NSRange range = NSMakeRange(insertIndex, toInsert.count);
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
[self.messages insertObjects:toInsert atIndexes:set];
NSMutableArray<NSIndexPath *> *indexPaths = [NSMutableArray array];
for (NSInteger i = 0; i < toInsert.count; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:insertIndex + i inSection:0]];
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
[self.tableView layoutIfNeeded];
}];
}
///
- (BOOL)shouldInsertTimestampForMessage:(KBAiChatMessage *)message {
//
@@ -659,19 +922,37 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
- (void)stopPreviousIncompleteAssistantMessageIfNeeded {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *msg = self.messages[i];
if (msg.type == KBAiChatMessageTypeAssistant && !msg.isComplete) {
msg.isComplete = YES;
msg.needsTypewriterEffect = NO;
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *oldCell = [self.tableView cellForRowAtIndexPath:oldIndexPath];
if ([oldCell isKindOfClass:[KBChatAssistantMessageCell class]]) {
[oldCell stopTypewriterEffect];
oldCell.messageLabel.text = msg.text;
if (self.inverted) {
for (NSInteger i = 0; i < self.messages.count; i++) {
KBAiChatMessage *msg = self.messages[i];
if (msg.type == KBAiChatMessageTypeAssistant && !msg.isComplete) {
msg.isComplete = YES;
msg.needsTypewriterEffect = NO;
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *oldCell = [self.tableView cellForRowAtIndexPath:oldIndexPath];
if ([oldCell isKindOfClass:[KBChatAssistantMessageCell class]]) {
[oldCell stopTypewriterEffect];
oldCell.messageLabel.text = msg.text;
}
break;
}
}
} else {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *msg = self.messages[i];
if (msg.type == KBAiChatMessageTypeAssistant && !msg.isComplete) {
msg.isComplete = YES;
msg.needsTypewriterEffect = NO;
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:i inSection:0];
KBChatAssistantMessageCell *oldCell = [self.tableView cellForRowAtIndexPath:oldIndexPath];
if ([oldCell isKindOfClass:[KBChatAssistantMessageCell class]]) {
[oldCell stopTypewriterEffect];
oldCell.messageLabel.text = msg.text;
}
break;
}
break;
}
}
}
@@ -700,6 +981,7 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
KBChatUserMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:kUserCellIdentifier
forIndexPath:indexPath];
[cell configureWithMessage:message];
cell.contentView.transform = self.inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity;
return cell;
}
@@ -713,6 +995,7 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
BOOL isPlaying = [indexPath isEqual:self.playingCellIndexPath];
[cell updateVoicePlayingState:isPlaying];
cell.contentView.transform = self.inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity;
return cell;
}
@@ -720,6 +1003,7 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
KBChatTimeCell *cell = [tableView dequeueReusableCellWithIdentifier:kTimeCellIdentifier
forIndexPath:indexPath];
[cell configureWithMessage:message];
cell.contentView.transform = self.inverted ? CGAffineTransformMakeScale(1, -1) : CGAffineTransformIdentity;
return cell;
}
}
@@ -732,6 +1016,42 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
#pragma mark - UIScrollViewDelegate
- (UICollectionView *)nearestCollectionView {
UIView *view = self;
while (view) {
if ([view isKindOfClass:[UICollectionView class]]) {
return (UICollectionView *)view;
}
view = view.superview;
}
return nil;
}
- (void)setNearestCollectionViewScrollEnabled:(BOOL)enabled {
UICollectionView *collectionView = [self nearestCollectionView];
if (!collectionView) {
return;
}
if (collectionView.scrollEnabled == enabled) {
return;
}
collectionView.scrollEnabled = enabled;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self setNearestCollectionViewScrollEnabled:NO];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self setNearestCollectionViewScrollEnabled:YES];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self setNearestCollectionViewScrollEnabled:YES];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([self.delegate respondsToSelector:@selector(chatTableViewDidScroll:scrollView:)]) {
[self.delegate chatTableViewDidScroll:self scrollView:scrollView];
@@ -743,23 +1063,18 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
CGFloat scrollViewHeight = scrollView.bounds.size.height;
CGFloat minOffset = -scrollView.contentInset.top;
CGFloat maxOffset = contentHeight - scrollViewHeight + scrollView.contentInset.bottom;
if (maxOffset < minOffset) {
maxOffset = minOffset;
}
//
if (contentHeight <= scrollViewHeight) {
scrollView.bounces = NO;
} else {
scrollView.bounces = YES;
//
if (velocity.y < 0) { //
CGFloat maxOffset = contentHeight - scrollViewHeight + scrollView.contentInset.bottom;
if (targetContentOffset->y > maxOffset) {
targetContentOffset->y = maxOffset;
}
}
if (targetContentOffset->y < minOffset) {
targetContentOffset->y = minOffset;
} else if (targetContentOffset->y > maxOffset) {
targetContentOffset->y = maxOffset;
}
}