YapDatabaseViewRowChange
@interface YapDatabaseViewRowChange : NSObject <NSCopying>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
The type will be one of:
- YapDatabaseViewChangeInsert
- YapDatabaseViewChangeDelete
- YapDatabaseViewChangeMove
YapDatabaseViewChangeUpdate
See
YapDatabaseViewChangeType
Declaration
Objective-C
@property (readonly, nonatomic) YapDatabaseViewChangeType type;
Swift
var type: YapDatabaseViewChangeType { get }
-
The changes property is a bitmask representing what changed for the corresponding row in the database.
There are 3 types represented in the bit mask:
- YapDatabaseViewChangedObject
- YapDatabaseViewChangedMetadata
- YapDatabaseViewChangedDependency
YapDatabaseViewChangedObject means the object was changed via setObject:forKey:inCollection:.
YapDatabaseViewChangedMetadata means the metadata was changed. This might have happend implicitly if the user invoked setObject:forKey:inCollection: (implicitly setting the metadata to nil). Or explicitly if the user invoked setObject:forKey:inCollection:withMetadata: or replaceMetadata:forKey:inCollection:.
YapDatabaseViewChangedDependency means the row was flagged due to a cell drawing dependency configuration. See YapDatabaseViewMappings: setCellDrawingDependencyForNeighboringCellWithOffset:forGroup:
Keep in mind that this is a bitmask. So, for example, all bits might be set if a row was updated, and was also flagged due to an inter-cell drawing dependency.
This may be useful for various optimizations. For example: The drawing of your cell depends only on the object. However, your objects are rather large, and you’re using metadata to store small subsets of the object that often need to be fetched. In addition, you’re keeping other information in metadata such as refresh dates for pulling updates from the server. The grouping and sorting block are optimized and use only the metadata. However this means that the metadata may change (due to a refresh date update), when in fact the object itself didn’t change. So you could optimize a bit here by skipping some cell updates.
if (change.type == YapDatabaseViewChangeUpdate) { if (change.modifiedColumns & YapDatabaseViewChangedObject) { // object changed, update cell } else { // only the metadata changed, so we can skip updating the cell } }
See
YapDatabaseViewChangesBitMaskDeclaration
Objective-C
@property (readonly, nonatomic) YapDatabaseViewChangesBitMask changes;
Swift
var changes: YapDatabaseViewChangesBitMask { get }
-
The indexPath & newIndexPath are available after you’ve invoked getSectionChanges:rowChanges:forNotifications:withMappings:.
See
YapDatabaseConnection getSectionChanges:rowChanges:forNotifications:withMappings:These properties are designed to help facilitate animations to tableViews and collectionsViews.
Recall that a view has no concept of sections. That is, a view has groups not sections. A group is a string, and a section is just a number.
Using groups allows a view to be more dynamic. Your view may contain dozens of groups, but a particular tableView within your app may only display a subset of the groups.
For example, you may have a view for displaying products in a grocery store. Each product is grouped by department (e.g. produce, deli, bakery), and sorted sorted by name. Using this view you can easily bring up a table view which displays only a few departments such as: liquor, wine, beer.
In this example:
- Section 0 = liquor
- Section 1 = wine
- Section 2 = beer
NSArray *groups = @{ @
liquor
:@(0), @wine
:@(1), @beer
:@(2) }; YapDatabaseMappings *mappings = [YapDatabaseViewMappings mappingsWithGroups:groups view:@order
];The mappings are then used to
map
between the ‘groups in the view’ and ‘items in the table’.Mappings can provide a lot of additional functionality as well. For example, you can configure the mappings to only display a particular range within a group. This is similar to a LIMIT & OFFSET in SQL. This is the tip of the iceberg. See YapDatabaseViewMappings.h for more info.
In order to animate changes to your tableView or collectionView, you eventually do something like this:
NSArray *sectionChanges = nil; NSArray *rowChanges = nil; [databaseConnection getSectionChanges:§ionChanges rowChanges:&rowChanges forNotifications:notifications withMappings:mappings];
This gives you a list of changes as they affect your tableView / collectionView.
The indexPath and newIndexPath properties are modeled after: NSFetchedResultsControllerDelegate controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
indexPath represents the ORIGINAL indexPath for the row. It is non-nil for the following types : Delete, Move, Update. (And nil for insert since there was no original indexPath.)
newIndexPath represents the FINAL indexPath for the row. It is non-nil for the following types : Insert, Move. (And nil for delete since there is no final indexPath.) (And nil for update since that’s how NSFetchedResultsController works, and thus how existing code might expect it to work.)
Once you have the sectionChanges & rowChanges, you can animate your tableView very simply like so:
PS - For a FULL CODE EXAMPLE, see the wiki: https://github.com/yapstudios/YapDatabase/wiki/Views#wiki-animating_updates_in_tableviews_collectionviews
if ([sectionChanges count] == 0 & [rowChanges count] == 0) { // Nothing has changed that affects our tableView return; }
// Familiar with NSFetchedResultsController? // Then this should look pretty familiar
[self.tableView beginUpdates];
for (YapDatabaseViewSectionChange *sectionChange in sectionChanges) { switch (sectionChange.type) { case YapDatabaseViewChangeDelete : { [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionChange.index] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } case YapDatabaseViewChangeInsert : { [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionChange.index] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } } }
for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeDelete : { [self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } case YapDatabaseViewChangeInsert : { [self.tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } case YapDatabaseViewChangeMove : { [self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } case YapDatabaseViewChangeUpdate : { [self.tableView reloadRowsAtIndexPaths:@[ rowChange.indexPath ] withRowAnimation:UITableViewRowAnimationNone]; break; } } }
[self.tableView endUpdates];
Declaration
Objective-C
@property (readonly, nonatomic, nullable) NSIndexPath *indexPath;
Swift
var indexPath: IndexPath? { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly, nullable) NSIndexPath *newIndexPath
Swift
var newIndexPath: IndexPath? { get }
-
The
original
values represent the location of the changed item at the BEGINNING of the read-write transaction(s).The
final
values represent the location of the changed item at the END of the read-write transaction(s).This information also available in another form via the indexPath & newIndexPath properties.
Declaration
Objective-C
@property (readonly, nonatomic) NSUInteger originalIndex;
Swift
var originalIndex: UInt { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly) NSUInteger finalIndex
Swift
var finalIndex: UInt { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly) NSUInteger originalSection
Swift
var originalSection: UInt { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly) NSUInteger finalSection
Swift
var finalSection: UInt { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly) NSString *originalGroup
Swift
var originalGroup: String { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, readonly) NSString *finalGroup
Swift
var finalGroup: String { get }
-
Gives you the {collection,key} tuple that caused the row change.
Please note that this information is not always available. In particular, it may not be available if:
- the rowChange was due solely to a dependency (YapDatabaseViewChangedDependency)
- the rowChange was due solely to satisfy a range constraint (YapDatabaseViewRangeOptions)
- the rowChange was due to the database being cleared (removeAllObjectsInAllCollections)
However, it will be available for the most important situation, which is when a particular item from the database has been removed. (YapDatabaseViewChangeDelete)
In other situations (YapDatabaseViewChangeInsert, YapDatabaseViewChangeUpdate, YapDatabaseViewChangeMove) you’d be able to fetch the corresponding information directly from the View. For example:
for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { // … case YapDatabaseViewChangeInsert : { // What changed exactly ? __block NSString *collection = nil; __block NSString *key = nil; [databaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction){ [[transaction ext:@
view
] getKey:&key collection:&collection atIndexPath:rowChange.newIndexPath withMappings:mappings]; // … } // …. } }However, you’ll notice that you wouldn’t be able to fetch the collection/key for a deleted item, because the rowChange.indexPath is no longer valid for the current state of the database/view.
And thus that information is available via this property, should you ever need it.
Declaration
Objective-C
@property (readonly, nonatomic) YapCollectionKey *_Nonnull collectionKey;
Swift
var collectionKey: YapCollectionKey { get }