-
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
Jan 27, 2017
1 parent
a7b537b
commit c60a552
Showing
21 changed files
with
900 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,55 @@ | ||
/** | ||
* 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; | ||
|
||
/** | ||
Other naming options | ||
- IGListDiffingSectionController | ||
- IGListBindingSectionController | ||
- IGListAutoSectionController | ||
*/ | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@protocol IGListAutoSectionControllerDataSource <NSObject> | ||
|
||
- (NSArray<id<IGListDiffable>> *)viewModelsForObject:(id)object; | ||
- (UICollectionViewCell<IGListBindable> *)cellForViewModel:(id)viewModel atIndex:(NSInteger)index; | ||
- (CGSize)sizeForViewModel:(id)viewModel; | ||
|
||
@end | ||
|
||
@interface IGListAutoSectionController : IGListSectionController<IGListSectionType> | ||
|
||
/** | ||
func transform(object: IGListDiffable) -> [IGListDiffable] | ||
func cellClass(viewModel: IGListDiffable) -> Class (of type UICollectionView<IGListBindable>) | ||
how to add "extra" stuff to the cell, e.g. delegates. options: | ||
- override superclass method (don't call super tho) | ||
- use IGListDisplayDelegate (adds lots of boilerplate) | ||
- new, optional delegate w/ bindable method | ||
*/ | ||
|
||
@property (nonatomic, weak, nullable) id<IGListAutoSectionControllerDataSource> dataSource; | ||
|
||
- (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,119 @@ | ||
/** | ||
* 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 "IGListAutoSectionController.h" | ||
|
||
#import <IGListKit/IGListAssert.h> | ||
#import <IGListKit/IGListDiffable.h> | ||
#import <IGListKit/IGListDiff.h> | ||
#import <IGListKit/IGListBindable.h> | ||
|
||
typedef NS_ENUM(NSInteger, IGListAutoSectionState) { | ||
IGListAutoSectionStateIdle = 0, | ||
IGListAutoSectionStateUpdateQueued, | ||
IGListAutoSectionStateUpdateApplied | ||
}; | ||
|
||
@interface IGListAutoSectionController() | ||
|
||
@property (nonatomic, strong) id object; | ||
@property (nonatomic, strong) NSArray<id<IGListDiffable>> *viewModels; | ||
@property (nonatomic, assign) IGListAutoSectionState state; | ||
|
||
@end | ||
|
||
@implementation IGListAutoSectionController | ||
|
||
#pragma mark - Public API | ||
|
||
- (void)updateAnimated:(BOOL)animated completion:(void (^)())completion { | ||
IGAssertMainThread(); | ||
|
||
if (self.state != IGListAutoSectionStateIdle) { | ||
return; | ||
} | ||
self.state = IGListAutoSectionStateUpdateQueued; | ||
|
||
__block IGListIndexSetResult *result = nil; | ||
__block NSArray<id<IGListDiffable>> *oldViewModels = nil; | ||
|
||
id<IGListCollectionContext> collectionContext = self.collectionContext; | ||
|
||
[collectionContext performBatchAnimated:YES updates:^{ | ||
if (self.state != IGListAutoSectionStateUpdateQueued) { | ||
return; | ||
} | ||
|
||
oldViewModels = self.viewModels; | ||
self.viewModels = [self.dataSource 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 = IGListAutoSectionStateUpdateApplied; | ||
} completion:^(BOOL finished) { | ||
self.state = IGListAutoSectionStateIdle; | ||
|
||
// "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 sizeForViewModel:self.viewModels[index]]; | ||
} | ||
|
||
- (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index { | ||
id<IGListDiffable> viewModel = self.viewModels[index]; | ||
UICollectionViewCell<IGListBindable> *cell = [self.dataSource 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 viewModelsForObject:object]; | ||
} else { | ||
[self updateAnimated:YES completion:nil]; | ||
} | ||
// self.viewModels = [self.dataSource viewModelsForObject:object]; | ||
|
||
// atm this will queue an update next turn which isn't ideal. really we want to unload this update right here and now | ||
// maybe we can update performBatchAnimated:completion to fire if we're inside an update block? | ||
// [self updateAnimated:YES completion:nil]; | ||
} | ||
|
||
- (void)didSelectItemAtIndex:(NSInteger)index { | ||
// would be nice to have a "selection delegate" here | ||
} | ||
|
||
@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,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
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.