-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ryan Nystrom
committed
Feb 15, 2017
1 parent
7a94167
commit 0ae5a5c
Showing
21 changed files
with
917 additions
and
63 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Copyright (c) 2016-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
@protocol IGListDiffable; | ||
|
||
@protocol IGListBindable <NSObject> | ||
|
||
- (void)bindViewModel:(id)viewModel; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* Copyright (c) 2016-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
#import <IGListKit/IGListMacros.h> | ||
#import <IGListKit/IGListSectionType.h> | ||
#import <IGListKit/IGListSectionController.h> | ||
|
||
@protocol IGListDiffable; | ||
@protocol IGListBindable; | ||
|
||
@class IGListDiffingSectionController; | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@protocol IGListDiffingSectionControllerDataSource <NSObject> | ||
|
||
- (NSArray<id<IGListDiffable>> *)sectionController:(IGListDiffingSectionController *)sectionController | ||
viewModelsForObject:(id)object; | ||
|
||
- (UICollectionViewCell<IGListBindable> *)sectionController:(IGListDiffingSectionController *)sectionController | ||
cellForViewModel:(id)viewModel atIndex:(NSInteger)index; | ||
|
||
- (CGSize)sectionController:(IGListDiffingSectionController *)sectionController | ||
sizeForViewModel:(id)viewModel; | ||
|
||
@end | ||
|
||
@protocol IGListDiffingSectionControllerSelectionDelegate | ||
|
||
- (void)sectionController:(IGListDiffingSectionController *)sectionController | ||
didSelectItemAtIndex:(NSInteger)index | ||
viewModel:(id)viewModel; | ||
|
||
@end | ||
|
||
@interface IGListDiffingSectionController : IGListSectionController<IGListSectionType> | ||
|
||
@property (nonatomic, weak, nullable) id<IGListDiffingSectionControllerDataSource> dataSource; | ||
|
||
@property (nonatomic, weak, nullable) id<IGListDiffingSectionControllerSelectionDelegate> selectionDelegate; | ||
|
||
@property (nonatomic, strong, readonly) NSArray<id<IGListDiffable>> *viewModels; | ||
|
||
- (void)updateAnimated:(BOOL)animated completion:(nullable void (^)())completion; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/** | ||
* Copyright (c) 2016-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import "IGListDiffingSectionController.h" | ||
|
||
#import <IGListKit/IGListAssert.h> | ||
#import <IGListKit/IGListDiffable.h> | ||
#import <IGListKit/IGListDiff.h> | ||
#import <IGListKit/IGListBindable.h> | ||
|
||
typedef NS_ENUM(NSInteger, IGListDiffingSectionState) { | ||
IGListDiffingSectionStateIdle = 0, | ||
IGListDiffingSectionStateUpdateQueued, | ||
IGListDiffingSectionStateUpdateApplied | ||
}; | ||
|
||
@interface IGListDiffingSectionController() | ||
|
||
@property (nonatomic, strong, readwrite) NSArray<id<IGListDiffable>> *viewModels; | ||
|
||
@property (nonatomic, strong) id object; | ||
@property (nonatomic, assign) IGListDiffingSectionState state; | ||
|
||
@end | ||
|
||
@implementation IGListDiffingSectionController | ||
|
||
#pragma mark - Public API | ||
|
||
- (void)updateAnimated:(BOOL)animated completion:(void (^)())completion { | ||
IGAssertMainThread(); | ||
|
||
if (self.state != IGListDiffingSectionStateIdle) { | ||
return; | ||
} | ||
self.state = IGListDiffingSectionStateUpdateQueued; | ||
|
||
__block IGListIndexSetResult *result = nil; | ||
__block NSArray<id<IGListDiffable>> *oldViewModels = nil; | ||
|
||
id<IGListCollectionContext> collectionContext = self.collectionContext; | ||
|
||
[collectionContext performBatchAnimated:YES updates:^{ | ||
if (self.state != IGListDiffingSectionStateUpdateQueued) { | ||
return; | ||
} | ||
|
||
oldViewModels = self.viewModels; | ||
self.viewModels = [self.dataSource sectionController:self viewModelsForObject:self.object]; | ||
result = IGListDiff(oldViewModels, self.viewModels, IGListDiffEquality); | ||
|
||
[collectionContext deleteInSectionController:self atIndexes:result.deletes]; | ||
[collectionContext insertInSectionController:self atIndexes:result.inserts]; | ||
|
||
for (IGListMoveIndex *move in result.moves) { | ||
[collectionContext moveInSectionController:self fromIndex:move.from toIndex:move.to]; | ||
} | ||
|
||
self.state = IGListDiffingSectionStateUpdateApplied; | ||
} completion:^(BOOL finished) { | ||
self.state = IGListDiffingSectionStateIdle; | ||
|
||
// "reload" cells after updating since the cells can't be moved and reloaded at the same time. | ||
// this lets the cell do an animated move and then update its contents | ||
[result.updates enumerateIndexesUsingBlock:^(NSUInteger oldUpdatedIndex, BOOL *stop) { | ||
id identifier = [oldViewModels[oldUpdatedIndex] diffIdentifier]; | ||
const NSInteger indexAfterUpdate = [result newIndexForIdentifier:identifier]; | ||
if (indexAfterUpdate != NSNotFound) { | ||
UICollectionViewCell<IGListBindable> *cell = [collectionContext cellForItemAtIndex:indexAfterUpdate | ||
sectionController:self]; | ||
[cell bindViewModel:self.viewModels[indexAfterUpdate]]; | ||
} | ||
}]; | ||
}]; | ||
} | ||
|
||
#pragma mark - IGListSectionType | ||
|
||
- (NSInteger)numberOfItems { | ||
return self.viewModels.count; | ||
} | ||
|
||
- (CGSize)sizeForItemAtIndex:(NSInteger)index { | ||
return [self.dataSource sectionController:self sizeForViewModel:self.viewModels[index]]; | ||
} | ||
|
||
- (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index { | ||
id<IGListDiffable> viewModel = self.viewModels[index]; | ||
UICollectionViewCell<IGListBindable> *cell = [self.dataSource sectionController:self cellForViewModel:viewModel atIndex:index]; | ||
[cell bindViewModel:viewModel]; | ||
return cell; | ||
} | ||
|
||
- (void)didUpdateToObject:(id)object { | ||
id oldObject = self.object; | ||
self.object = object; | ||
|
||
if (oldObject == nil) { | ||
self.viewModels = [self.dataSource sectionController:self viewModelsForObject:object]; | ||
} else { | ||
[self updateAnimated:YES completion:nil]; | ||
} | ||
} | ||
|
||
- (void)didSelectItemAtIndex:(NSInteger)index { | ||
[self.selectionDelegate sectionController:self didSelectItemAtIndex:index viewModel:self.viewModels[index]]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// | ||
// IGListBatchUpdatesCollection.h | ||
// IGListKit | ||
// | ||
// Created by Ryan Nystrom on 1/26/17. | ||
// Copyright © 2017 Instagram. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
@class IGListMoveIndex; | ||
@class IGListMoveIndexPath; | ||
|
||
@interface IGListBatchUpdatesCollection : NSObject | ||
|
||
//@property (nonatomic, strong, readonly) NSMutableIndexSet *sectionInserts; | ||
@property (nonatomic, strong, readonly) NSMutableIndexSet *sectionReloads; | ||
//@property (nonatomic, strong, readonly) NSMutableIndexSet *sectionDeletes; | ||
//@property (nonatomic, strong, readonly) NSMutableSet<IGListMoveIndex *> *sectionMoves; | ||
@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *itemInserts; | ||
@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *itemDeletes; | ||
@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *itemReloads; | ||
@property (nonatomic, strong, readonly) NSMutableSet<IGListMoveIndexPath *> *itemMoves; | ||
@property (nonatomic, strong, readonly) NSMutableArray<void (^)(BOOL)> *completionBlocks; | ||
|
||
//- (BOOL)hasChanges; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// IGListBatchUpdatesCollection.m | ||
// IGListKit | ||
// | ||
// Created by Ryan Nystrom on 1/26/17. | ||
// Copyright © 2017 Instagram. All rights reserved. | ||
// | ||
|
||
#import "IGListBatchUpdatesCollection.h" | ||
|
||
@implementation IGListBatchUpdatesCollection | ||
|
||
- (instancetype)init { | ||
if (self = [super init]) { | ||
_sectionReloads = [NSMutableIndexSet new]; | ||
_itemInserts = [NSMutableSet new]; | ||
_itemMoves = [NSMutableSet new]; | ||
_itemReloads = [NSMutableSet new]; | ||
_itemDeletes = [NSMutableSet new]; | ||
_completionBlocks = [NSMutableArray new]; | ||
} | ||
return self; | ||
} | ||
|
||
//- (BOOL)hasChanges { | ||
// return self.sectionReloads.count > 0 | ||
// || self.itemInserts.count > 0 | ||
// || self.itemMoves.count > 0 | ||
// || self.itemReloads.count > 0 | ||
// || self.itemDeletes.count > 0 | ||
// || self.completionBlocks.count > 0; | ||
//} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.