From 5df4ac92b796171c35f3a98b889c06ff5ec03d64 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 1 Nov 2017 10:21:07 -0400 Subject: [PATCH 1/3] Don't animate message sends. // FREEBIE --- .../ConversationViewController.m | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 47f17eedd..2b34ea76b 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2860,6 +2860,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { } } + NSUInteger oldViewItemCount = self.viewItems.count; NSMutableSet *rowsThatChangedSize = [[self reloadViewItems] mutableCopy]; BOOL wasAtBottom = [self isScrolledToBottom]; @@ -2873,7 +2874,30 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // b) is inserting new interactions. __block BOOL scrollToBottom = wasAtBottom; - [self.collectionView performBatchUpdates:^{ + // If user sends a new outgoing message, don't animate the change. + BOOL areAllUpdatesNewOutgoingMessages = YES; + for (YapDatabaseViewRowChange *rowChange in messageRowChanges) { + if (!areAllUpdatesNewOutgoingMessages) { + break; + } + switch (rowChange.type) { + case YapDatabaseViewChangeInsert: { + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.newIndexPath.row]; + if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] + && rowChange.newIndexPath.row >= (NSInteger)oldViewItemCount) { + continue; + } + } + case YapDatabaseViewChangeDelete: + case YapDatabaseViewChangeMove: + case YapDatabaseViewChangeUpdate: + areAllUpdatesNewOutgoingMessages = NO; + break; + } + } + BOOL shouldAnimateUpdates = !areAllUpdatesNewOutgoingMessages; + + void (^batchUpdates)() = ^{ for (YapDatabaseViewRowChange *rowChange in messageRowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeDelete: { @@ -2925,20 +2949,27 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { [rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]]; } [self.collectionView reloadItemsAtIndexPaths:rowsToReload]; - } - completion:^(BOOL finished) { - OWSAssert([NSThread isMainThread]); + }; + void (^batchUpdatesCompletion)(BOOL) = ^(BOOL finished) { + OWSAssert([NSThread isMainThread]); - if (!finished) { - DDLogInfo(@"%@ performBatchUpdates did not finish", self.tag); - } + if (!finished) { + DDLogInfo(@"%@ performBatchUpdates did not finish", self.tag); + } - [self updateLastVisibleTimestamp]; + [self updateLastVisibleTimestamp]; - if (scrollToBottom) { - [self scrollToBottomAnimated:shouldAnimateScrollToBottom]; - } - }]; + if (scrollToBottom) { + [self scrollToBottomAnimated:shouldAnimateScrollToBottom]; + } + }; + + if (shouldAnimateUpdates) { + [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; + } else { + batchUpdates(); + batchUpdatesCompletion(YES); + } } - (BOOL)isScrolledToBottom From 40e04ffb92de652274799ea4fdf4394633bb10a3 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 1 Nov 2017 13:09:55 -0400 Subject: [PATCH 2/3] Respond to CR. // FREEBIE --- .../ConversationView/ConversationViewController.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 2b34ea76b..f00fed0ec 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2967,8 +2967,9 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { if (shouldAnimateUpdates) { [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; } else { - batchUpdates(); - batchUpdatesCompletion(YES); + [UIView performWithoutAnimation:^{ + [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; + }]; } } From af5489952c459105567b380af46650c7cb874bca Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 1 Nov 2017 13:58:20 -0400 Subject: [PATCH 3/3] Don't animate message sends. // FREEBIE --- .../ConversationViewController.m | 93 +++++++++++++------ 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index f00fed0ec..ea7c32648 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2820,14 +2820,14 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { return; } - NSArray *messageRowChanges = nil; - NSArray *sectionChanges = nil; + NSArray *sectionChanges = nil; + NSArray *rowChanges = nil; [[self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName] getSectionChanges:§ionChanges - rowChanges:&messageRowChanges + rowChanges:&rowChanges forNotifications:notifications withMappings:self.messageMappings]; - if ([sectionChanges count] == 0 && [messageRowChanges count] == 0) { + if ([sectionChanges count] == 0 && [rowChanges count] == 0) { // YapDatabase will ignore insertions within the message mapping's // range that are not within the current mapping's contents. We // may need to extend the mapping's contents to reflect the current @@ -2844,7 +2844,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // We need to reload any modified interactions _before_ we call // reloadViewItems. - for (YapDatabaseViewRowChange *rowChange in messageRowChanges) { + for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeUpdate: { YapCollectionKey *collectionKey = rowChange.collectionKey; @@ -2874,31 +2874,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // b) is inserting new interactions. __block BOOL scrollToBottom = wasAtBottom; - // If user sends a new outgoing message, don't animate the change. - BOOL areAllUpdatesNewOutgoingMessages = YES; - for (YapDatabaseViewRowChange *rowChange in messageRowChanges) { - if (!areAllUpdatesNewOutgoingMessages) { - break; - } - switch (rowChange.type) { - case YapDatabaseViewChangeInsert: { - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.newIndexPath.row]; - if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] - && rowChange.newIndexPath.row >= (NSInteger)oldViewItemCount) { - continue; - } - } - case YapDatabaseViewChangeDelete: - case YapDatabaseViewChangeMove: - case YapDatabaseViewChangeUpdate: - areAllUpdatesNewOutgoingMessages = NO; - break; - } - } - BOOL shouldAnimateUpdates = !areAllUpdatesNewOutgoingMessages; + BOOL shouldAnimateUpdates = [self shouldAnimateRowUpdates:rowChanges oldViewItemCount:oldViewItemCount]; void (^batchUpdates)() = ^{ - for (YapDatabaseViewRowChange *rowChange in messageRowChanges) { + for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeDelete: { DDLogVerbose(@"YapDatabaseViewChangeDelete: %@, %@", rowChange.collectionKey, rowChange.indexPath); @@ -2973,6 +2952,64 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { } } +- (BOOL)shouldAnimateRowUpdates:(NSArray *)rowChanges + oldViewItemCount:(NSUInteger)oldViewItemCount +{ + OWSAssert(rowChanges); + + // If user sends a new outgoing message, don't animate the change. + BOOL isOnlyInsertingNewOutgoingMessages = YES; + BOOL isOnlyUpdatingLastOutgoingMessage = YES; + NSNumber *_Nullable lastUpdateRow = nil; + NSNumber *_Nullable lastNonUpdateRow = nil; + for (YapDatabaseViewRowChange *rowChange in rowChanges) { + switch (rowChange.type) { + case YapDatabaseViewChangeDelete: + isOnlyInsertingNewOutgoingMessages = NO; + isOnlyUpdatingLastOutgoingMessage = NO; + if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.indexPath.row) { + lastNonUpdateRow = @(rowChange.indexPath.row); + } + break; + case YapDatabaseViewChangeInsert: { + isOnlyUpdatingLastOutgoingMessage = NO; + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.newIndexPath.row]; + if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] + && rowChange.newIndexPath.row >= (NSInteger)oldViewItemCount) { + continue; + } + if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.newIndexPath.row) { + lastNonUpdateRow = @(rowChange.newIndexPath.row); + } + } + case YapDatabaseViewChangeMove: + isOnlyInsertingNewOutgoingMessages = NO; + isOnlyUpdatingLastOutgoingMessage = NO; + if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.indexPath.row) { + lastNonUpdateRow = @(rowChange.indexPath.row); + } + if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.newIndexPath.row) { + lastNonUpdateRow = @(rowChange.newIndexPath.row); + } + break; + case YapDatabaseViewChangeUpdate: { + isOnlyInsertingNewOutgoingMessages = NO; + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.indexPath.row]; + if (![viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] + || rowChange.indexPath.row != (NSInteger)(oldViewItemCount - 1)) { + isOnlyUpdatingLastOutgoingMessage = NO; + } + if (!lastUpdateRow || lastUpdateRow.integerValue < rowChange.indexPath.row) { + lastUpdateRow = @(rowChange.indexPath.row); + } + break; + } + } + } + BOOL shouldAnimateRowUpdates = !(isOnlyInsertingNewOutgoingMessages || isOnlyUpdatingLastOutgoingMessage); + return shouldAnimateRowUpdates; +} + - (BOOL)isScrolledToBottom { CGFloat contentHeight = self.safeContentHeight;