Skip to content

Commit

Permalink
feat(ios): reload list on changed index, not all indices
Browse files Browse the repository at this point in the history
  • Loading branch information
ozonelmy authored and hippy-actions[bot] committed Aug 28, 2023
1 parent cb186d8 commit 22e1f8f
Show file tree
Hide file tree
Showing 11 changed files with 841 additions and 141 deletions.
7 changes: 4 additions & 3 deletions renderer/native/ios/renderer/NativeRenderImpl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ - (UIView *)createViewRecursiveFromRenderObjectWithNOLock:(NativeRenderObjectVie
view.renderManager = [self renderManager];
[view clearSortedSubviews];
[view didUpdateNativeRenderSubviews];
NSMutableSet<NativeRenderApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:1];
NSMutableSet<NativeRenderApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:256];
[renderObject amendLayoutBeforeMount:applierBlocks];
if (applierBlocks.count) {
NSDictionary<NSNumber *, UIView *> *viewRegistry =
Expand Down Expand Up @@ -676,7 +676,7 @@ - (void)addUIBlock:(NativeRenderRenderUIBlock)block {
}
- (void)amendPendingUIBlocksWithStylePropagationUpdateForRenderObject:(NativeRenderObjectView *)topView {
NSMutableSet<NativeRenderApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:1];
NSMutableSet<NativeRenderApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:256];
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
if (applierBlocks.count) {
Expand Down Expand Up @@ -1468,7 +1468,8 @@ - (void)layoutAndMountOnRootNode:(std::weak_ptr<RootNode>)rootNode {
}
[self addUIBlock:^(NativeRenderImpl *renderContext, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
NativeRenderImpl *uiManager = (NativeRenderImpl *)renderContext;
for (id<NativeRenderComponentProtocol> node in uiManager->_componentTransactionListeners) {
NSSet<id<NativeRenderComponentProtocol>> *nodes = [uiManager->_componentTransactionListeners copy];
for (id<NativeRenderComponentProtocol> node in nodes) {
[node nativeRenderComponentDidFinishTransaction];
}
}];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ - (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
_isInitialListReady = NO;
self.preloadItemNumber = 1;
_dataSource = [[NativeRenderBaseListViewDataSource alloc] init];
self.dataSource.itemViewName = [self compoentItemName];
}

return self;
}

Expand Down Expand Up @@ -120,13 +118,19 @@ - (void)setInitialListReady:(NativeRenderDirectEventBlock)initialListReady {

#pragma mark Data Load

- (BOOL)flush {
[self refreshItemNodes];
return YES;
}

- (void)reloadData {
[self.collectionView reloadData];
[self refreshItemNodes];
[_dataSource applyDiff:_previousDataSource
changedConext:self.changeContext
forWaterfallView:self.collectionView
completion:^(BOOL success) {
if (success) {
self->_previousDataSource = [self->_dataSource copy];
}
else {
self->_previousDataSource = nil;
}
}];
if (self.initialContentOffset) {
CGFloat initialContentOffset = self.initialContentOffset;
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down Expand Up @@ -163,7 +167,6 @@ - (void)insertNativeRenderSubview:(UIView *)subview atIndex:(NSInteger)atIndex {
}

- (void)didUpdateNativeRenderSubviews {
[self refreshItemNodes];
self.dirtyContent = YES;
}

Expand All @@ -175,8 +178,10 @@ - (void)nativeRenderComponentDidFinishTransaction {
}

- (void)refreshItemNodes {
NSArray<NativeRenderObjectView *> *datasource = self.nativeRenderObjectView.subcomponents;
[self.dataSource setDataSource:datasource containBannerView:NO];
NSArray<NativeRenderObjectView *> *datasource = [self popDataSource];
self->_dataSource = [[NativeRenderBaseListViewDataSource alloc] initWithDataSource:datasource
itemViewName:[self compoentItemName]
containBannerView:NO];
}

#pragma mark -Scrollable
Expand Down Expand Up @@ -304,8 +309,10 @@ - (void)collectionView:(UICollectionView *)collectionView
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if ([cell isKindOfClass:[NativeRenderBaseListViewCell class]]) {
NativeRenderBaseListViewCell *hpCell = (NativeRenderBaseListViewCell *)cell;
[_cachedItems setObject:[hpCell.cellView componentTag] forKey:indexPath];
hpCell.cellView = nil;
if (hpCell.cellView) {
[_cachedItems setObject:[hpCell.cellView componentTag] forKey:indexPath];
hpCell.cellView = nil;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
#import <UIKit/UIKit.h>
#import "NativeRenderBaseListViewDataSource.h"
#import "NativeRenderObjectView.h"
#import "NativeRenderObjectWaterfall.h"

@interface NativeRenderBaseListViewDataSource () {
NSMutableArray *_headerRenderObjects;
NSMutableArray<NSMutableArray<NativeRenderObjectView *> *> *_cellRenderObjects;
}

@end
Expand Down Expand Up @@ -65,12 +65,12 @@ - (void)setDataSource:(NSArray<NativeRenderObjectView *> *)dataSource containBan
[cellRenderObjects addObject:sectionCellRenderObject];
}
_headerRenderObjects = headerRenderObjects;
_cellRenderObjects = cellRenderObjects;
self.cellRenderObjectViews = [cellRenderObjects copy];
}

- (NativeRenderObjectView *)cellForIndexPath:(NSIndexPath *)indexPath {
if (_cellRenderObjects.count > indexPath.section) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObject = [_cellRenderObjects objectAtIndex:indexPath.section];
if (self.cellRenderObjectViews.count > indexPath.section) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObject = [self.cellRenderObjectViews objectAtIndex:indexPath.section];
if (sectionCellRenderObject.count > indexPath.row) {
return [sectionCellRenderObject objectAtIndex:indexPath.row];
}
Expand All @@ -81,8 +81,8 @@ - (NativeRenderObjectView *)cellForIndexPath:(NSIndexPath *)indexPath {
- (NSIndexPath *)indexPathOfCell:(NativeRenderObjectView *)cell {
NSInteger section = 0;
NSInteger row = 0;
for (NSInteger sec = 0; sec < [_cellRenderObjects count]; sec++) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [_cellRenderObjects objectAtIndex:sec];
for (NSInteger sec = 0; sec < [self.cellRenderObjectViews count]; sec++) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [self.cellRenderObjectViews objectAtIndex:sec];
for (NSUInteger r = 0; r < [sectionCellRenderObjects count]; r++) {
NativeRenderObjectView *cellRenderObject = [sectionCellRenderObjects objectAtIndex:r];
if (cellRenderObject == cell) {
Expand All @@ -102,12 +102,13 @@ - (NativeRenderObjectView *)headerForSection:(NSInteger)section {
}

- (NSInteger)numberOfSection {
return _cellRenderObjects.count;
NSInteger numberOfSection = self.cellRenderObjectViews.count;
return numberOfSection;
}

- (NSInteger)numberOfCellForSection:(NSInteger)section {
if (_cellRenderObjects.count > section) {
return [[_cellRenderObjects objectAtIndex:section] count];
if (self.cellRenderObjectViews.count > section) {
return [[self.cellRenderObjectViews objectAtIndex:section] count];
}
return 0;
}
Expand All @@ -116,8 +117,8 @@ - (NSIndexPath *)indexPathForFlatIndex:(NSInteger)index {
NSInteger sectionIndex = 0;
NSInteger rowIndex = 0;
NSInteger selfIncreaseIndex = 0;
for (NSInteger sec = 0; sec < [_cellRenderObjects count]; sec++) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [_cellRenderObjects objectAtIndex:sec];
for (NSInteger sec = 0; sec < [self.cellRenderObjectViews count]; sec++) {
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [self.cellRenderObjectViews objectAtIndex:sec];
for (NSUInteger r = 0; r < [sectionCellRenderObjects count]; r++) {
if (index == selfIncreaseIndex) {
sectionIndex = sec;
Expand All @@ -139,7 +140,7 @@ - (NSInteger)flatIndexForIndexPath:(NSIndexPath *)indexPath {
flatIndex += row;
}
else {
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [_cellRenderObjects objectAtIndex:sec];
NSArray<NativeRenderObjectView *> *sectionCellRenderObjects = [self.cellRenderObjectViews objectAtIndex:sec];
flatIndex += [sectionCellRenderObjects count];
}
}
Expand All @@ -154,4 +155,196 @@ - (UIView *)bannerView {
return nil;
}

- (void)applyDiff:(NativeRenderBaseListViewDataSource *)another
changedConext:(WaterfallItemChangeContext *)context
forWaterfallView:(UICollectionView *)view
completion:(void(^)(BOOL success))completion {
if (!another ||
!context ||
![[another cellRenderObjectViews] count]) {
[view reloadData];
completion(YES);
return;
}
// NSArray<NSInvocation *> *batchUpdateInvocations = [self cellViewChangeInvocation:another context:context forCollectionView:view];
NSMutableArray<NSInvocation *> *batchUpdate = [NSMutableArray arrayWithCapacity:8];
[self cellDiffFromAnother:another
sectionStartAt:0
frameChangedItems:context.frameChangedItems
result:^(NSArray<NSIndexPath *> *reloadedItemIndexPath,
NSArray<NSIndexPath *> *InsertedIndexPath,
NSArray<NSIndexPath *> *deletedIndexPath,
NSIndexSet *insertedSecionIndexSet,
NSIndexSet *deletedSectionIndexSet) {
if ([insertedSecionIndexSet count]) {
NSInvocation *invocation = InvocationFromSelector(view, @selector(insertSections:), insertedSecionIndexSet);
if (invocation) {
[batchUpdate addObject:invocation];
}
}
if ([deletedSectionIndexSet count]) {
NSInvocation *invocation = InvocationFromSelector(view, @selector(deleteSections:), deletedSectionIndexSet);
if (invocation) {
[batchUpdate addObject:invocation];
}
}
if ([reloadedItemIndexPath count]) {
NSInvocation *invocation = InvocationFromSelector(view, @selector(reloadItemsAtIndexPaths:), reloadedItemIndexPath);
if (invocation) {
[batchUpdate addObject:invocation];
}
}
if ([InsertedIndexPath count]) {
NSInvocation *invocation = InvocationFromSelector(view, @selector(insertItemsAtIndexPaths:), InsertedIndexPath);
if (invocation) {
[batchUpdate addObject:invocation];
}
}
if ([deletedIndexPath count]) {
NSInvocation *invocation = InvocationFromSelector(view, @selector(deleteItemsAtIndexPaths:), deletedIndexPath);
if (invocation) {
[batchUpdate addObject:invocation];
}
}
}];

BOOL success = YES;
[UIView setAnimationsEnabled:NO];
if ([batchUpdate count]) {
@try {
[view performBatchUpdates:^{
for (NSInvocation *invocation in batchUpdate) {
[invocation invoke];
}
} completion:^(BOOL finished) {
[UIView setAnimationsEnabled:YES];
}];
} @catch (NSException *exception) {
[view reloadData];
success = NO;
[UIView setAnimationsEnabled:YES];
}
}
completion(success);
}

static NSInvocation *InvocationFromSelector(id object, SEL selector, id param) {
if (!selector || !object) {
return nil;
}
NSMethodSignature *methodSignature = [UICollectionView instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:object];
[invocation setSelector:selector];
if (param) {
[invocation setArgument:&param atIndex:2];
}
if (![invocation argumentsRetained]) {
[invocation retainArguments];
}
return invocation;
}

static inline void EnumCellRenderObjects(NSArray<NSArray<__kindof NativeRenderObjectView *> *> *objects,
void (^ _Nonnull block)(__kindof NativeRenderObjectView * object, NSUInteger section, NSUInteger row)) {
for (NSUInteger section = 0; section < [objects count]; section ++) {
NSArray<__kindof NativeRenderObjectView *> *sectionObjects = [objects objectAtIndex:section];
for (NSUInteger row = 0; row < [sectionObjects count]; row++) {
__kindof NativeRenderObjectView *object = [sectionObjects objectAtIndex:row];
block(object, section, row);
}
}
}

- (NSArray<NSInvocation *> *)cellViewChangeInvocation:(NativeRenderWaterfallViewDataSource *)another
context:(WaterfallItemChangeContext *)context
forCollectionView:(UICollectionView *)collectionView {
//todo 如果包含move的item,直接返回吧,不好算
// if ([[context movedItems] count]) {
// NSInvocation *invocation = InvocationFromSelector(collectionView, @selector(reloadData), nil);
// return @[invocation];
// }

NSMutableArray<NSInvocation *> *invocations = [NSMutableArray arrayWithCapacity:8];
NSHashTable<__kindof NativeRenderObjectView *> *insertedItems = [context addedItems];
NSMutableSet<__kindof NativeRenderObjectView *> *deletedItems = [[context deletedItems] mutableCopy];
NSHashTable<__kindof NativeRenderObjectView *> *frameChangedItems = [context frameChangedItems];
//get section number change
//section number increased or decreased
NSUInteger selfSectionCount = [self.cellRenderObjectViews count];
NSUInteger anotherSectionCount = [another.cellRenderObjectViews count];
if (selfSectionCount > anotherSectionCount) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
do {
//remove added items from [WaterfallItemChangeContext addedItems] to avoid insertItemsAtIndexes: below
NSArray<__kindof NativeRenderObjectView *> *objects = [self.cellRenderObjectViews objectAtIndex:anotherSectionCount];
[objects enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[insertedItems removeObject:obj];
}];
[indexSet addIndex:anotherSectionCount];
anotherSectionCount++;
} while (selfSectionCount != anotherSectionCount);
NSInvocation *invocation = InvocationFromSelector(collectionView, @selector(insertSections:), indexSet);
[invocations addObject:invocation];
}
else if (selfSectionCount < anotherSectionCount) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
do {
anotherSectionCount--;
//remove deleted items from [WaterfallItemChangeContext deletedItems] to avoid deleteItemsAtIndexPaths: below
NSArray<__kindof NativeRenderObjectView *> *objects = [another.cellRenderObjectViews objectAtIndex:anotherSectionCount];
[objects enumerateObjectsUsingBlock:^(__kindof NativeRenderObjectView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[deletedItems removeObject:obj];
}];
[indexSet addIndex:anotherSectionCount];
} while (selfSectionCount != anotherSectionCount);
NSInvocation *invocation = InvocationFromSelector(collectionView, @selector(deleteSections:), indexSet);
[invocations addObject:invocation];
}
//section number unchanged
else {
//get inserted items and frame changed items if exists
if ([insertedItems count] || [frameChangedItems count]) {
NSMutableArray<NSIndexPath *> *insertedIndexPaths = [NSMutableArray arrayWithCapacity:16];
NSMutableArray<NSIndexPath *> *frameChangedIndexPaths = [NSMutableArray arrayWithCapacity:16];
EnumCellRenderObjects(self.cellRenderObjectViews, ^(__kindof NativeRenderObjectView *object, NSUInteger section, NSUInteger row) {
if ([insertedItems count] && [insertedItems containsObject:object]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[insertedIndexPaths addObject:indexPath];
}
if ([frameChangedItems count] && [frameChangedItems containsObject:object]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[frameChangedIndexPaths addObject:indexPath];
}
});
if ([insertedIndexPaths count]) {
NSInvocation *invocation =
InvocationFromSelector(collectionView, @selector(insertItemsAtIndexPaths:), insertedIndexPaths);
[invocations addObject:invocation];
}
if ([frameChangedIndexPaths count]) {
NSInvocation *invocation =
InvocationFromSelector(collectionView, @selector(reloadItemsAtIndexPaths:), frameChangedIndexPaths);
[invocations addObject:invocation];
}
}
//get deleted items
if ([deletedItems count]) {
NSMutableArray<NSIndexPath *> *deletedIndexPaths = [NSMutableArray arrayWithCapacity:16];
EnumCellRenderObjects(another.cellRenderObjectViews, ^(__kindof NativeRenderObjectView *object, NSUInteger section, NSUInteger row) {
if ([deletedItems containsObject:object]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[deletedIndexPaths addObject:indexPath];
}
});
if ([deletedIndexPaths count]) {
NSInvocation *invocation =
InvocationFromSelector(collectionView, @selector(deleteItemsAtIndexPaths:), deletedIndexPaths);
[invocations addObject:invocation];
}
}
}
return invocations;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,24 @@
*/

#import "NativeRenderObjectView.h"
#import "NativeRenderObjectWaterfallItem.h"

NS_ASSUME_NONNULL_BEGIN

@interface NativeRenderObjectWaterfall : NativeRenderObjectView
@interface WaterfallItemChangeContext : NSObject<NSCopying>

- (NSHashTable<__kindof NativeRenderObjectView *> *)addedItems;
- (NSHashTable<__kindof NativeRenderObjectView *> *)frameChangedItems;
- (NSSet<__kindof NativeRenderObjectView *> *)deletedItems;
- (NSHashTable<__kindof NativeRenderObjectView *> *)movedItems;

- (void)clear;

@end

@interface NativeRenderObjectWaterfall : NativeRenderObjectView<NativeRenderObjectWaterfallItemFrameChangedProtocol>

@property(nonatomic, readonly, strong)WaterfallItemChangeContext *itemChangeContext;

@end

Expand Down
Loading

0 comments on commit 22e1f8f

Please sign in to comment.