diff --git a/CueTableReloader.podspec b/CueTableReloader.podspec new file mode 100644 index 0000000..6141648 --- /dev/null +++ b/CueTableReloader.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'CueTableReloader' + s.version = '0.0.1' + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.summary = 'CueTableReloader' + s.homepage = 'https://github.com/Cue/CueTableReloader' + s.author = { 'Aaron Sarazan' => 'https://github.com/Cue/CueTableReloader' } + s.source = { :git => 'https://github.com/Cue/CueTableReloader.git', :commit => 'eff314d703e9e8e57b07c5026af722bde9a3e94b' } + s.description = 'A really handy class that automatically figures out insertions, deletions, moves, and reloads in UITableView based on unique item keys.' + s.platform = :ios, '6.0' + s.ios.deployment_target = '6.0' + s.source_files = 'CueTableReloader/**/*.{h,m}' + s.public_header_files = 'CueTableReloader/**/*.h' + s.framework = 'UIKit', 'QuartzCore' + s.requires_arc = true +end diff --git a/CueTableReloader/CueTableReloader.h b/CueTableReloader/CueTableReloader.h index 178b5d7..bf04a34 100644 --- a/CueTableReloader/CueTableReloader.h +++ b/CueTableReloader/CueTableReloader.h @@ -1,3 +1,4 @@ + /* * Copyright 2013 CueTableReloader Authors. * @@ -22,7 +23,6 @@ */ @interface CueTableReloader : NSObject - /** * Preferred method of initialization. */ @@ -43,20 +43,37 @@ * Default: YES * If you know that pre-existing rows aren't subject to change, set this to NO and skip reloading them. */ -@property BOOL reloadUnchangedRows; +@property (nonatomic, assign) BOOL reloadUnchangedRows; /** * Default: NO * By default, setting an empty array will not animate. */ -@property BOOL animateClear; +@property (nonatomic, assign) BOOL animateClear; /** * Default: NO * By default, setting data for the first time will not animate. */ -@property BOOL animatePopulate; +@property (nonatomic, assign) BOOL animatePopulate; + +/** + * Default: UITableViewRowAnimationLeft + * Animation for a table row insert. + */ +@property (nonatomic, assign) UITableViewRowAnimation insertAnimation; +/** + * Default: UITableViewRowAnimationRight + * Animation for a table row delete. + */ +@property (nonatomic, assign) UITableViewRowAnimation deleteAnimation; + +/** + * Default: UITableViewRowAnimationNone + * Animation for a table row update. + */ +@property (nonatomic, assign) UITableViewRowAnimation updateAnimation; @end diff --git a/CueTableReloader/CueTableReloader.m b/CueTableReloader/CueTableReloader.m index f72a248..3e40e55 100644 --- a/CueTableReloader/CueTableReloader.m +++ b/CueTableReloader/CueTableReloader.m @@ -16,19 +16,26 @@ #import "CueTableReloader.h" -@implementation CueTableReloader { +@interface CueTableReloader () { UITableView *_tableView; NSArray *_oldSections; } -- (id)initWithTableView:(UITableView *)tableView; -{ +@end + +@implementation CueTableReloader + +- (id)initWithTableView:(UITableView *)tableView { self = [super init]; + if (self) { _tableView = tableView; _reloadUnchangedRows = YES; _animateClear = NO; _animatePopulate = NO; + _insertAnimation = UITableViewRowAnimationLeft; + _deleteAnimation = UITableViewRowAnimationRight; + _updateAnimation = UITableViewRowAnimationNone; } return self; } @@ -53,7 +60,7 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; if (!shouldAnimate) { [_tableView reloadData]; - } else { + } else { // Apply a simple algorithm to each section. // It's very good at handling new and deleted rows, not so much with reorderings. // It will not catch cross-section moves, and doesn't handle section count changes well. @@ -65,10 +72,10 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; newSection = sections[i]; } else { [_tableView deleteSections:[NSIndexSet indexSetWithIndex:i] - withRowAnimation:UITableViewRowAnimationNone]; + withRowAnimation: _deleteAnimation]; if (i == 0) { [_tableView insertSections:[NSIndexSet indexSetWithIndex:0] - withRowAnimation:UITableViewRowAnimationNone]; + withRowAnimation:_insertAnimation]; } continue; } @@ -77,11 +84,11 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; oldSection = _oldSections[i]; } else if (i > 0) { [_tableView insertSections:[NSIndexSet indexSetWithIndex:i] - withRowAnimation:UITableViewRowAnimationFade]; + withRowAnimation:_insertAnimation]; } NSMutableDictionary *newIndexes = [NSMutableDictionary dictionaryWithCapacity:newSection.count]; - for (int i = 0; i < oldSection.count; ++i) { + for (int i = 0; i < newSection.count; ++i) { NSObject *item = newSection[i]; newIndexes[item.tableItemKey] = @(i); } @@ -91,11 +98,11 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; NSObject *item = oldSection[i]; oldIndexes[item.tableItemKey] = @(i); } - - NSMutableArray *deletions = [@[] mutableCopy]; - NSMutableArray *insertions = [@[] mutableCopy]; - NSMutableArray *reloads = [@[] mutableCopy]; - NSMutableArray *moves = [@[] mutableCopy]; + + NSMutableSet *deletions = [NSMutableSet set]; + NSMutableSet *insertions = [NSMutableSet set]; + NSMutableSet *reloads = [NSMutableSet set]; + NSMutableSet *moves = [NSMutableSet set]; int oldIndex = 0; int newIndex = 0; int insertionIndex = 0; @@ -108,7 +115,7 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; NSIndexPath *insertPath = [NSIndexPath indexPathForRow:insertionIndex inSection:i]; NSIndexPath *deletePath = [NSIndexPath indexPathForRow:oldIndex inSection:i]; NSIndexPath *reloadPath = [NSIndexPath indexPathForRow:insertionIndex inSection:i]; - + NSObject *oldItem = nil; if (oldIndex < oldSection.count) { // Delete oldItem = oldSection[oldIndex]; @@ -135,8 +142,8 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; continue; } else { if (oldIndexes[newItem.tableItemKey]) { // Move - int iOld = [oldIndexes[oldItem.tableItemKey] intValue]; - int iNew = [newIndexes[oldItem.tableItemKey] intValue]; + NSInteger iOld = [oldIndexes[oldItem.tableItemKey] integerValue]; + NSInteger iNew = [newIndexes[oldItem.tableItemKey] integerValue]; NSInteger diff = iNew - iOld; NSIndexPath *from = [NSIndexPath indexPathForRow:oldIndex inSection:i]; NSIndexPath *to = [NSIndexPath indexPathForRow:oldIndex+diff inSection:i]; @@ -156,19 +163,46 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; "and that the ordering is constant!"]; } } + + NSMutableSet *updatedReloads = [NSMutableSet set]; + + // Find intersecting index paths in deletions + for (id obj in deletions) { + if ([insertions containsObject: obj]) { + [updatedReloads addObject: obj]; + } + } + + // Find intersecting index paths in insertions + for (id obj in insertions) { + if ([deletions containsObject: obj]) { + [updatedReloads addObject: obj]; + } + } + + NSMutableSet *deletionsCopy = [deletions mutableCopy]; + NSMutableSet *insertionsCopy = [insertions mutableCopy]; + [deletions minusSet: insertionsCopy]; + [insertions minusSet: deletionsCopy]; + deletionsCopy = nil; + insertionsCopy = nil; + [_tableView beginUpdates]; - [_tableView deleteRowsAtIndexPaths:deletions - withRowAnimation:UITableViewRowAnimationNone]; - [_tableView insertRowsAtIndexPaths:insertions - withRowAnimation:UITableViewRowAnimationFade]; + [_tableView deleteRowsAtIndexPaths:[deletions allObjects] + withRowAnimation:_deleteAnimation]; + [_tableView insertRowsAtIndexPaths:[insertions allObjects] + withRowAnimation:_insertAnimation]; for (NSArray *pair in moves) { [_tableView moveRowAtIndexPath:pair[0] toIndexPath:pair[1]]; } [_tableView endUpdates]; if (_reloadUnchangedRows) { - [_tableView reloadRowsAtIndexPaths:reloads - withRowAnimation:UITableViewRowAnimationNone]; + [_tableView reloadRowsAtIndexPaths:[reloads allObjects] + withRowAnimation:_updateAnimation]; } + + // Reload updated index paths based on intersecting index paths above (if applicable) + [_tableView reloadRowsAtIndexPaths:[updatedReloads allObjects] withRowAnimation: _updateAnimation]; } @catch (NSException *e) { [_tableView reloadData]; } @@ -181,4 +215,4 @@ - (void)reloadData:(NSArray *)sections animated:(BOOL)animated; _oldSections = deepCopy; } -@end +@end \ No newline at end of file