Created
June 21, 2018 19:04
-
-
Save bnickel/6cf4e1bf92049d060687f665c4b6253a to your computer and use it in GitHub Desktop.
A class that lets you schedule a bunch of batch updates to UITableView and then perform them all at once or not, with a callback on completion.
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
func setSelectedSite(_ site:SEAPISite, selectedTab:SESiteTab, inSection:Int, tableView:UITableView, animated:Bool, completion:((Void) -> Void)?) { | |
let batchUpdate = SETableViewBatchUpdate(tableView: tableView) | |
if site != selectedSite { | |
if let indexPaths = selectedSiteTransientItemRange?.map({ IndexPath(row: $0, section: inSection) }) { | |
batchUpdate.deleteRows(atIndexPaths: indexPaths, with: .fade) | |
} | |
selectedSite = site | |
rebuildMenu() | |
if let indexPaths = selectedSiteTransientItemRange?.map({ IndexPath(row: $0, section: inSection) }) { | |
batchUpdate.insertRows(atIndexPaths: indexPaths, with: .fade) | |
} | |
} | |
let indexPathToSelect:IndexPath? | |
if let offset = tabIdToChildItemIndexMap[selectedTab], | |
let row = selectedSiteIndex != NSNotFound ? selectedSiteIndex + 1 + offset : Optional<Int>.none { | |
indexPathToSelect = IndexPath(row: row, section: inSection) | |
} else { | |
indexPathToSelect = nil | |
} | |
// The following selection procedure is way uglier than I would hope. If you set a selected cell during a batch update, it can end up with no background during the animation. | |
// As a result, if we're doing a batch update, our best bet is to scroll to the selected cell during the update and then select when the animation completes. | |
if batchUpdate.hasUpdates { | |
// If we're going to select a new row, deselect the old row before animating. | |
if indexPathToSelect != nil { | |
if let selectedIndexPath = tableView.indexPathForSelectedRow { | |
tableView.deselectRow(at: selectedIndexPath, animated: false) | |
} | |
} | |
batchUpdate.run(animated: animated, completion: { | |
if let indexPathToSelect = indexPathToSelect { | |
tableView.selectRow(at: indexPathToSelect, animated: false, scrollPosition: .none) | |
} | |
completion?() | |
}) | |
if let indexPathToSelect = indexPathToSelect { | |
tableView.scrollToRow(at: indexPathToSelect, at: .middle, animated: animated) | |
} | |
} else { | |
if let indexPathToSelect = indexPathToSelect , indexPathToSelect != tableView.indexPathForSelectedRow { | |
tableView.selectRow(at: indexPathToSelect, animated: animated, scrollPosition: .none) | |
} | |
completion?() | |
} | |
} |
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
#import <Foundation/Foundation.h> | |
NS_ASSUME_NONNULL_BEGIN | |
@interface SETableViewBatchUpdate : NSObject | |
- (instancetype)init NS_UNAVAILABLE; | |
- (instancetype)initWithTableView:(UITableView *)tableView NS_DESIGNATED_INITIALIZER; | |
- (void)runAnimated:(BOOL)animated completion:(void (^ _Nullable)())completion; | |
@property (nonatomic, assign, readonly) BOOL hasUpdates; | |
@end | |
@interface SETableViewBatchUpdate (Convenience) | |
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; | |
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; | |
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); | |
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0); | |
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; | |
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; | |
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); | |
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath NS_AVAILABLE_IOS(5_0); | |
@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
#import "SETableViewBatchUpdate.h" | |
@interface SETableViewBatchUpdate () | |
@property (nonatomic, strong, readonly) UITableView *tableView; | |
@property (nonatomic, strong, readonly) NSMutableArray *invocations; | |
@property (nonatomic, assign) BOOL hasRun; | |
@property (nonatomic, assign, readwrite) BOOL hasUpdates; | |
@end | |
@implementation SETableViewBatchUpdate | |
- (instancetype)initWithTableView:(UITableView *)tableView | |
{ | |
NSParameterAssert(tableView != nil); | |
self = [super init]; | |
if (self) { | |
_tableView = tableView; | |
_invocations = [NSMutableArray array]; | |
} | |
return self; | |
} | |
- (BOOL)respondsToSelector:(SEL)selector | |
{ | |
return [super respondsToSelector:selector] || [self.tableView respondsToSelector:selector]; | |
} | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { | |
return [super methodSignatureForSelector:selector] ?: [self.tableView methodSignatureForSelector:selector]; | |
} | |
- (void)forwardInvocation:(NSInvocation *)invocation | |
{ | |
SEL selector = [invocation selector]; | |
if ([self.tableView respondsToSelector:selector]) { | |
[invocation retainArguments]; | |
[self.invocations addObject:invocation]; | |
self.hasUpdates = YES; | |
} else { | |
[self doesNotRecognizeSelector:selector]; | |
} | |
} | |
- (void)runAnimated:(BOOL)animated completion:(void (^)())completion | |
{ | |
NSAssert(!self.hasRun, @"Batch updates should only run once."); | |
self.hasRun = YES; | |
if (animated && self.invocations.count > 0) { | |
[CATransaction begin]; | |
[CATransaction setCompletionBlock:^{ | |
self.tableView.userInteractionEnabled = YES; | |
if (completion) { completion(); } | |
}]; | |
[self.tableView beginUpdates]; | |
self.tableView.userInteractionEnabled = NO; | |
for (NSInvocation *invocation in self.invocations) { | |
[invocation invokeWithTarget:self.tableView]; | |
} | |
[self.tableView endUpdates]; | |
[self.tableView correctHeaderViewsAfterUpdates]; | |
[CATransaction commit]; | |
} else { | |
[self.tableView reloadData]; | |
if (completion) { completion(); } | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment