TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views.

Overview

TLIndexPathTools

TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views. Here are some of the awesome things TLIndexPathTools does:

  • Organize data into sections with ease (now with blocks!)
  • Calculate and perform animated batch updates (inserts, moves and deletions)
  • Simplify data source and delegate methods via rich data model APIs
  • Provide a simpler alternative to Core Data NSFetchedResultsController
  • Provide base table view and collection view classes with advanced features

TLIndexPathTools is as lightweight as you want it to be. Start small by using TLIndexPathDataModel as your data model (instead of an array) and gain the ability to easily organize data into sections and simplify your view controller with APIs like [dataModel numberOfRowsInSection:], [dataModel itemAtIndexPath:], and [dataModel indexPathForItem:]. Or keep reading to learn about automatic batch updates, easier Core Data integration and more.

Installation

Add "TLIndexPathTools" to your podfile or, if you're not using CocoaPods:

  1. Download the TLIndexPathTools project
  2. Add the TLIndexPathTools sub-folder (sibling of the Examples folder) to your Xcode project.
  3. Link to QuartzCore.framework and CoreData.framework (on the Build Phases tab of your project's target).

Overview

NSArray is the standard construct for simple table and collection view data models. However, if multiple sections are involved, the typical setup is an NSArray containing section names and an NSDictionary of NSArrays containing data items, keyed by section name. Since table and collection views work with NSIndexPaths, the following pattern is used repeatedly in data source and delegate methods:

NSString *sectionName = self.sectionNameArray[indexPath.section];
NSArray *sectionArray = self.sectionArraysBySectionName[sectionName];    
id data = sectionArray[indexPath.row];

TLIndexPathDataModel encapsulates this pattern into a single class and provides numerous APIs for easy data access. Furthermore, the TLIndexPathDataModel initializers offer multiple ways to organize raw data into sections (including empty sections). TLIndexPathDataModel is perfectly suitable for single-section views where an NSArray would suffice and has the benefit of being "refactor proof" if additional sections are added later.

TLIndexPathUpdates is a very powerful companion class to TLIndexPathDataModel. One of the great things about table and collection views are their ability to perform batch updates (inserts, deletes and moves) that animate cells smoothly between states. However, calculating batch updates can be a complex (and confusing) task when multiple updates are involved. TLIndexPathUpdates solves this by taking two versions of your data model, calculating the changes for you and automatically performing the batch updates.

Most of the functionality in TLIndexPathTools can be accomplished with just TLIndexPathDataModel and TLIndexPathUpdates. However, there are a few of additional components that provide some great features:

  • TLIndexPathController provides a common programming model for building view controllers that work interchangeably with Core Data NSFetchRequests or plain arrays of any data type. One controller to rule them all.
  • TLTableViewController and TLCollectionViewController are table and collection view base classes that use TLIndexPathController and implement the essential data source and delegate methods to get you up and running quickly. They also support view controller-backed cells (see the View Controller Backed sample project) and automatic cell height calculation for table views (see the Dynamic Height sample project).
  • TLIndexPathItem is a wrapper class for data items that can simplify working with multiple data types or cell types. For example, take a look at the Settings sample project.
  • The Extensions folder contains a number of add-ons for things like collapsable sections and expandable tree views. This is a good resource to see how TLIndexPathDataModel can be easily extended for special data structures.
  • And last, but not least, the Examples folder contains numerous sample projects demonstrating various use cases and features of the framework. Shuffle is a good starting point and be sure to try Core Data.

This version of TLIndexPathTools is designed to handle up to a few thousand items. Larger data sets may have performance issues.

TLIndexPathDataModel

TLIndexPathDataModel is an immutable object you use in your view controller to hold your data items instead of an array (or dictionary of arrays, for multiple sections). There are four initializers, a basic one and three for handling multiple sections:

// single section initializer
TLIndexPathDataModel *dataModel1 = [[TLIndexPathDataModel alloc] initWithItems:items];

// multiple sections defined by a key path property on your data items
TLIndexPathDataModel *dataModel2 = [[TLIndexPathDataModel alloc] initWithItems:items sectionNameKeyPath:@"someKeyPath" identifierKeyPath:nil];

// multiple sections defined by an arbitrary code block
TLIndexPathDataModel *dataModel3 = [[TLIndexPathDataModel alloc] initWithItems:items sectionNameBlock:^NSString *(id item) {
    // organize items by first letter of description (like contacts app)
    return [item.description substringToIndex:1];
} identifierBlock:nil];

// multiple explicitly defined sections (including an empty section)
TLIndexPathSectionInfo *section1 = [[TLIndexPathSectionInfo alloc] initWithItems:@[@"Item 1.1"] name:@"Section 1"];
TLIndexPathSectionInfo *section2 = [[TLIndexPathSectionInfo alloc] initWithItems:@[@"Item 2.1", @"Item 2.2"] name:@"Section 2"];
TLIndexPathSectionInfo *section3 = [[TLIndexPathSectionInfo alloc] initWithItems:nil name:@"Section 3"];
TLIndexPathDataModel *dataModel4 = [[TLIndexPathDataModel alloc] initWithSectionInfos:@[section1, section2, section3] identifierKeyPath:nil];

And there are numerous APIs to simplify delegate and data source implementations:

// access all items across all sections as a flat array
dataModel.items;

// access items organized by sections
dataModel.sections;

// number of sections
[dataModel numberOfSections];

// number of rows in section
[dataModel numberOfRowsInSection:section];

// look up item at a given index path
[dataModel itemAtIndexPath:indexPath];

// look up index path for a given item
[dataModel indexPathForItem:item];

As an immutable object, all of the properties and methods in TLIndexPathDataModel are read-only. So using the data model is very straightforward once you've selected the appropriate initializer.

TLIndexPathUpdates

TLIndexPathUpdates is a companion class to TLIndexPathDataModel for batch updates. You provide two versions of your data model to the initializer and the inserts, deletes, and moves are calculated. Then call either performBatchUpdatesOnTableView: or performBatchUpdatesOnCollectionView: to perform the updates.

// initialize collection view with unordered items
// (assuming view controller has a self.dataModel property)
self.dataModel = [[TLIndexPathDataModel alloc] initWithItems:@[@"B", @"A", @"C"]];
[self.collectionView reloadData];

// ...

// sort items, update data model & perform batch updates (perhaps when a sort button it tapped)
TLIndexPathDataModel *oldDataModel = self.dataModel;
NSArray *sortedItems = [self.dataModel.items sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
self.dataModel = [[TLIndexPathDataModel alloc] initWithItems:sortedItems];
TLIndexPathUpdates *updates = [[TLIndexPathUpdates alloc] initWithOldDataModel:oldDataModel updatedDataModel:self.dataModel];
[updates performBatchUpdatesOnCollectionView:self.collectionView];

Thats all it takes!

TLIndexPathController

TLIndexPathController is TLIndexPathTools' version of NSFetchedResultsController. It should not come as a surprise, then, that you must use this class if you want to integrate with Core Data.

Although it primarily exists for Core Data integration, TLIndexPathController works interchangeably with NSFetchRequest or plain arrays of any data type. Thus, if you choose to standardize your view controllers on TLIndexPathController, it is possible to have a common programming model across all of your table and collection views.

TLIndexPathController also makes a few nice improvements relative to NSFetchedResultsController:

  • Items do not need to be presorted by section. The data model handles organizing sections.
  • Changes to your fetch request are animated. So you can get animated sorting and filtering.
  • Only one delegate method needs to be implemented (versus five for NSFetchedResultsController).

The basic template for using TLIndexPathController in a (table) view controller is as follows:

#import <UIKit/UIKit.h>
#import "TLIndexPathController.h"
@interface ViewController : UITableViewController <TLIndexPathControllerDelegate>
@end

#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) TLIndexPathController *indexPathController;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.indexPathController = [[TLIndexPathController alloc] init];
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.indexPathController.dataModel.numberOfSections;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.indexPathController.dataModel numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    id item = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
    //configure cell using data item
    return cell;
}

#pragma mark - TLIndexPathControllerDelegate

- (void)controller:(TLIndexPathController *)controller didUpdateDataModel:(TLIndexPathUpdates *)updates
{
    [updates performBatchUpdatesOnTableView:self.tableView withRowAnimation:UITableViewRowAnimationFade];    
}

@end

This template works with plain arrays or NSFetchRequests. With plain arrays, you simply set the dataModel property of the controller (or set the items property and get a default data model). With NSFetchRequests, you set the fetchRequest property and call performFetch:. From then on, the controller updates the data model internally every time the fetch results change (using an internal instance of NSFetchedResultsController and responding to controllerDidChangeContent messages).

In either case, whether you explicitly set a data model or the controller converts a fetch result into a data model, the controller creates the TLIndexPathUpdates object for you and passes it to the delegate, giving you an opportunity to perform batch updates:

- (void)controller:(TLIndexPathController *)controller didUpdateDataModel:(TLIndexPathUpdates *)updates
{
    [updates performBatchUpdatesOnTableView:self.tableView withRowAnimation:UITableViewRowAnimationFade];    
}

The willUpdateDataModel delegate method is a really cool feature of TLIndexPathController, providing the delegate an opportunity to modify the data model before didUpdateDataModel gets called. This can be applied in some interesting ways when integrating with Core Data. For example, it can be used to mix in non-Core Data objects (try doing this with NSFetchedResultsController). Another nice application is automatic display of a "No results" message when the data model is empty (the TLNoResultsTableDataModel class is provided in the Extensions folder):

- (TLIndexPathDataModel *)controller:(TLIndexPathController *)controller willUpdateDataModel:(TLIndexPathDataModel *)oldDataModel withDataModel:(TLIndexPathDataModel *)updatedDataModel
{
    if (updatedDataModel.items.count == 0) {
        return [[TLNoResultsTableDataModel alloc] initWithRows:3 blankCellId:@"BlankCell" noResultsCellId:@"NoResultsCell" noResultsText:@"No results to display"];
    }
    return nil;
}

TLTableViewController & TLCollectionViewController

TLTableViewController and TLCollectionViewController are table and collection view base classes that use TLIndexPathController and implement the essential data source and delegate methods to get you up and running quickly. Both classes look much like the code outlined above for integrating with TLIndexPathController.

Both classes support view controller-backed cells. Enabling this feature is as easy as overriding the instantiateViewControllerForCell: method. For example, see the View Controller Backed sample project.

TLTableViewController also includes a default implementation of heightForRowAtIndexPath that calculates static or data-driven cell heights using prototype cell instances. For example, if you're using storyboards, the cell heights specified in the storyboard are automatically used. And if your cell implements the TLDynamicSizeView protocol, the height will be determined by calling the sizeWithData: method on the prototype cell. This is a great way to handle data-driven height because the sizeWithData: method can use the actual layout logic of the cell itself, rather than duplicating the layout logic in the view controller.

Most of the sample projects are based on TLTableViewController or TLCollectionViewController, so a brief perusal will give you a good idea what can be accomplished with a few lines of code.

Documentation

The Xcode docset can be generated by running the Docset project. The build configuration assumes Appledoc is installed at /usr/local/bin/appledoc. This can be changed at TLIndexPathTools project | Docset target | Build Phases tab | Run Script.

The API documentation is also available online.

About SwiftKick Mobile

We build high quality apps! Get in touch if you need help with a project.

Comments
  • TLTableViewController doesn't configure cells correctly under iOS 8

    TLTableViewController doesn't configure cells correctly under iOS 8

    Demo project showing broken cell here: https://github.com/fatuhoku/Demo-TLIndexPathTools-Tables-Broken-Under-iOS8/tree/develop. Tested on Simulator, iPad Air (iOS 8).

    Two symptoms:

    • text labels for default styles of UITableViewCell (Basic, Left / Right Detail, Subtitle) are misplaced ios simulator screen shot 2 sep 2014 17 51 20
    • custom styles cause cells to overlap ios simulator screen shot 2 sep 2014 17 54 28

    The provided demo project covers the first case.

    Using UITableViewController and explicitly wiring it up with TLIndexPathController still works.

    opened by fatuhoku 11
  • 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0

    'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0

    Firstly, thanks for writing this, its been a great find.

    For testing purposes I am lazy loading content from sqlite. Its a very small table ...54 rows and 3 columns.

    The error below occurs after continue usage. I can expand/collapse etc and the error cannot be consistently reproduced. I may be misunderstanding how the following should be implemented properly ?

    - (void)controller:(TLTreeTableViewController *)controller willChangeNode:(TLIndexPathTreeItem *)treeItem collapsed:(BOOL)collapsed {
        if (collapsed == NO && [treeItem.childItems count] == 0)
        {
            Audit_categories *category = treeItem.data;
            NSArray *result = [[FAModel sharedInstance] subCategoriesForCategoryId: [category categoryId]];
    
            if ([result count] > 0)
            {
                NSMutableArray *childItems = [NSMutableArray arrayWithCapacity:result.count];
                [result enumerateObjectsUsingBlock:^(id subCategory, NSUInteger idx, BOOL *stop) {
                    depthCount = 0;
                    NSString *cellID = [NSString stringWithFormat:@"Level%d", [self depthLevelForCategory: subCategory]];
            //        NSLog(@" %@ -> %@ cellID: %@", [category categoryName], [subCategory categoryName], cellID);
                    TLIndexPathTreeItem *childItem = [[TLIndexPathTreeItem alloc] initWithIdentifier: [subCategory categoryName] sectionName:nil cellIdentifier: cellID data:subCategory andChildItems:[[NSArray alloc] init]];
                    [childItems addObject:childItem];
    
                }];
                TLIndexPathTreeItem *newTreeItem = [treeItem copyWithChildren:childItems];
                [self setNewVersionOfItem:newTreeItem collapsedChildNodeIdentifiers:[TLIndexPathItem identifiersForIndexPathItems:treeItem.childItems]];
            }
        }
    }
    

    *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (46) must be equal to the number of rows contained in that section before the update (36), plus or minus the number of rows inserted or deleted from that section (2 inserted, 2 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

    image

    opened by rgunn 9
  • BOOL collapsed on

    BOOL collapsed on "controller:willChangeNode:collapsed:" set incorrectly

    When clicking a synchronously and lazily loaded child in the tree view for the first time, YES is passed in for collapsed. NO is passed in on the second click. Since the children have yet to be loaded, should pass NO.

    opened by ebgraham 9
  • Exception when moving item into new section.

    Exception when moving item into new section.

    When in single performBatchUpdates call new section added and old item moved into this section exception raises. I guess it's easy to fix this by checking movedPaths for new section and do not move them.

    opened by mikhailmulyar 7
  • Is there anyway to arbitrarily prevent collapsing / expanding of rows in a tree table?

    Is there anyway to arbitrarily prevent collapsing / expanding of rows in a tree table?

    Is there anyway to arbitrarily prevent collapsing / expanding of rows? I would love to be able to have a callback or a variable of some kind, to indicate that a specific cell cannot be collapsed or expanded.

    opened by csotiriou 5
  • NSFetchedResultsChangeUpdate not counted

    NSFetchedResultsChangeUpdate not counted

    When using TLIndexPathController with NSFetchRequest, TLIndexPathUpdates internally not looking for NSFetchedResultsChangeUpdate and therefore simple item updates do not update corresponding cells.

    opened by mikhailmulyar 5
  • TLTableViewController with multiple table views

    TLTableViewController with multiple table views

    Is it possible to have multiple UITableViews with one controller? It seems everything is pretty baked into UITableViewController. Would this require re-writing TLTableViewController basically?

    Thanks!

    opened by rnystrom 5
  • Unable to get dynamic height working

    Unable to get dynamic height working

    I have to say it works fine in the example app but I'm unable to get it to resize a cell when put into my own environment. Feels like I'm missing something obvious and I've brought it back to a single label to get that working first.

    Xcode 7.2.1, target of IOS 9.2.

    I've checked:

    • Label has Lines=0 and Line breaks=Word Wrap
    • Cell is set to the right class
    • Cell says it will use the protocol
    • Constraints for label seem to pin it in to the superview in all four directions

    Differences:

    • You have explicit width for the label. Made no difference when I set a width.
    • You are using Plain tableview - made no difference when I changed mine over
    • I'm pushing my view controller through a navigationController - made no difference when I showed it modally.

    Suggestions welcome.

    opened by sumowesley 4
  • Modified Comparator

    Modified Comparator

    A relatively minor modification which allows for the TLIndexPathUpdates object to be initialized with a block which can be used to determine if two objects are to be considered modified. This allows for comparisons to be made by a user supplied block which can perform more detailed modification analysis than isEqual:, if desired.

    opened by levigroker 4
  • loading time

    loading time

    With a data set of 10k items, the TLIndexPathDataModel takes 45 seconds to load. With 5k items, it takes 11 seconds. With 1k items it loads in under a second.

    What is it doing which is so non-linear?

    opened by ghazel 4
  • *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil' crash when deleting non-persisted Core Data entities

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil' crash when deleting non-persisted Core Data entities

    Synopsis

    TPIPT's [updates performBatchUpdatesOnCollectionView:self.collectionView]; crashes because it's confused whenever unpersisted Core Data entities that use a specific identifierKeyPath gets deleted.

    Reproduction

    Have a view controller hooked up with a collection view, and implement the delegate methods as TLIPT intends.

    • Create an entity "FooBar" with MagicalRecord's MR_createEntity. Do not save the entity.
    • Delete the same entity with MR_deleteEntity (this essentially calls [context deleteObject:inContext]; behind the scenes. This is bog standard Core Data).

    The Crash

    2014-07-10 16:13:47.706 FooBarProject[11421:70b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
    *** First throw call stack:
    (
        0   CoreFoundation                      0x018a61e4 __exceptionPreprocess + 180
        1   libobjc.A.dylib                     0x03b408e5 objc_exception_throw + 44
        2   CoreFoundation                      0x01858abc -[__NSArrayM insertObject:atIndex:] + 844
        3   CoreFoundation                      0x01858760 -[__NSArrayM addObject:] + 64
        4   BDDReactiveCocoa                    0x00379655 __69-[TLIndexPathUpdates performBatchUpdatesOnCollectionView:completion:]_block_invoke + 4293
        5   UIKit                               0x031345df -[UICollectionView performBatchUpdates:completion:] + 209
        6   BDDReactiveCocoa                    0x003784a6 -[TLIndexPathUpdates performBatchUpdatesOnCollectionView:completion:] + 1126
        7   BDDReactiveCocoa                    0x0037801c -[TLIndexPathUpdates performBatchUpdatesOnCollectionView:] + 108
    

    The error is uninformative and leaves me with no leads. Perhaps TLIPT can improve on these error messages to help the user get back on their feet.

    Offending code

    The crash can be traced down into the code below in TLIndexPathUpdates.m:

            if (self.deletedItems.count) {
                NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
                for (id item in self.deletedItems) {
                    NSIndexPath *indexPath = [self.oldDataModel indexPathForItem:item]; // *** oldModel returns nil indexPath
                    [indexPaths addObject:indexPath];
                }
                [collectionView deleteItemsAtIndexPaths:indexPaths];
            }
    

    Extra information

    I've defined my indexPathController like so:

        TLIndexPathController *indexPathController = [[TLIndexPathController alloc] initWithFetchRequest:fetchedRequest
                                              managedObjectContext:[NSManagedObjectContext MR_defaultContext]
                                                sectionNameKeyPath:nil
                                                 identifierKeyPath:@"someID"  // someID is a field in FooBar.
                                                         cacheName:nil];
    

    This function is pretty unsafe, because it's making the assumption that self.oldDataModel indexPathForItem will always succeed.

    Below is some useful trace info I gathered when investigating the problem.

    // Output from controller:willUpdateDataModel:withDataModel:
    
    [DEBUG]   16:38:20.768 | > from model: Model(count=7, items=(
        "<FooBar: 0xb24b690> (entity: FooBar; id: 0xb24ce90 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p6> ; data: {...})",
        "<FooBar: 0xb24b4a0> (entity: FooBar; id: 0xb24bd30 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p8> ; data: {...})",
        "<FooBar: 0xb24b460> (entity: FooBar; id: 0xb24bd40 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p14> ; data: {...})",
        "<FooBar: 0xd0bbe80> (entity: FooBar; id: 0x144cfd20 <x-coredata:///FooBar/tE0DEE8FD-9BF1-4361-83D6-69FD3DA31B663> ; data: <fault>)",
        "<FooBar: 0xd0bcb80> (entity: FooBar; id: 0xd079380 <x-coredata:///FooBar/tE0DEE8FD-9BF1-4361-83D6-69FD3DA31B662> ; data: {...})",
        "<FooBar: 0xb24b3e0> (entity: FooBar; id: 0xb24bd60 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p7> ; data: {...})",
        "<FooBar: 0xb24b3a0> (entity: FooBar; id: 0xb24bd70 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p4> ; data: {...})"
    ))
    
    2014-07-10 16:38:20.768 BDDReactiveCocoa[11798:70b] inserted: 0, deleted: 1, moved: 3, modified: 0
    [DEBUG]   16:38:20.769 | > to model: Model(count=6, items=(
        "<FooBar: 0xb24b690> (entity: FooBar; id: 0xb24ce90 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p6> ; data: {...})",
        "<FooBar: 0xb24b4a0> (entity: FooBar; id: 0xb24bd30 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p8> ; data: {...})",
        "<FooBar: 0xb24b460> (entity: FooBar; id: 0xb24bd40 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p14> ; data: {...})",
        "<FooBar: 0xd0bcb80> (entity: FooBar; id: 0xd079380 <x-coredata:///FooBar/tE0DEE8FD-9BF1-4361-83D6-69FD3DA31B662> ; data: {...})",
        "<FooBar: 0xb24b3e0> (entity: FooBar; id: 0xb24bd60 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p7> ; data: {...})",
        "<FooBar: 0xb24b3a0> (entity: FooBar; id: 0xb24bd70 <x-coredata://55FB8417-7CBB-40CB-8633-CD9AE3447B4E/FooBar/p4> ; data: {...})"
    ))
    

    It's obvious that in the old model, there's one item that has a data fault. This would cause [self.oldDataModel indexPathForItem:item]; to return nil, because it cannot access the identifier field someID of the deleted object, and therefore cannot look up the correct indexPath and therefore returns nil. This crashes the app.

    Workaround

    Persist the object (this is not what I want to do every time I create an object!)

    opened by fatuhoku 4
  • Drawback in case of adding dynamic data from API in multilevel expandable tableview/accordion

    Drawback in case of adding dynamic data from API in multilevel expandable tableview/accordion

    Hello there!

    I really found your library very helpful but I'm facing issues to pass dynamic data to the tableview sections, child items for expandable view as shown in example i.e. Outline

    I checked that the code is being set according to static data for now. Is there any way to add dynamic data from API in form of JSON.

    If I have an dynamic length of array, how can I specify the values is similar case as it is used for static data shown below:

    TLIndexPathTreeItem *item1 = [self itemWithId:ITEM_1 level:0 children:childItems]; self.treeItems = @[item1, item2];

    Any suggestions will be appreciated :) Thanks in advance!

    opened by RakeshImpinge 1
  • Non-ideal hash calculation

    Non-ideal hash calculation

    I think you want to replace += by = here:

    https://github.com/SwiftKickMobile/TLIndexPathTools/blob/ca47649036081ec5b1220926ae072dab908301ea/TLIndexPathTools/Data%20Model/TLIndexPathItem.m#L53-57

    Right now, you're effectively calculating:

    hash = 32 * hash + [self.identifier hash];
    
    opened by futuretap 3
  • Another Suggestion Needed : Boxing Swift Structs

    Another Suggestion Needed : Boxing Swift Structs

    Tim,

    I'm slowing cutting over to Swift from a bunch of ObjC projects that use TLIndexPathTools. I'm a swift noob...

    I know you use TTIndexPathTools in a bunch of swift projects. I'm wondering what you have found to be the best/most efficient approach to wrap structs into something that can be used to init a dataModel.

    I usually structure a presenter for a viewController that prepares a viewModel...but I'm curious as to what you think would be the best practice for using IndexPathTools and CoreData in a Swift project.

    Thanx in advance

    opened by wm-j-ray 4
  • NSManagedObject changes not detected by TLIndexPathController

    NSManagedObject changes not detected by TLIndexPathController

    Hi there. I am using the latest TLIndexPathTools (0.4.3) with Core Data and came across a bug that seems to be related to TLIndexPathController (or my understanding of it.)

    In short, TLIndexPathController is not populating modifiedItems when it should be. It does call controller: didUpdateDataModel but updates has an empty modifiedItems array.

    Not being sure where the issue was, I wrote two tests, one against NSFetchedResultsController and another against TLIndexPathController to detect the same exact changes. The NSFetchedResultsController tests work as expected, while the TLIndexPathController tests fail to detect intermediate changes. I would like to have the NSFetchedResultsController behavior.

    I think this is either a serious issue, or a wild misunderstanding on my part. Either way I'd appreciate your guidance, and if it is an issue, I'd love to try to help resolve it. This library is a godsend and besides for this issue has been 100% solid for me. Thanks.

    My tests:

    opened by davetroy 19
  • TLTreeTableViewController: Keep only one expanded parent (close others)

    TLTreeTableViewController: Keep only one expanded parent (close others)

    In the case of tree table, is there any way to close other parts of the table when opening the current one?

    In short is there any way of manually closing other open sections?

    opened by csotiriou 2
Owner
SwiftKick Mobile
Mobile app design and development agency
SwiftKick Mobile
This component allows for the transfer of data items between collection views through drag and drop

Drag and Drop Collection Views Written for Swift 4.0, it is an implementation of Dragging and Dropping data across multiple UICollectionViews. Try it

Michael Michailidis 508 Dec 19, 2022
Modern Collection Views

The goal is to showcase different compositional layouts and how to achieve them. Feel free to use any code you can find and if you have interesting layout idea - open PR!

Filip Němeček 536 Dec 28, 2022
PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a consistent user interface for common content states (i.e. loading, loaded, empty, and error).

PJFDataSource PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a co

Square 88 Jun 30, 2022
Dwifft is a small Swift library that tells you what the "diff" is between two collections

Dwifft! In 10 seconds Dwifft is a small Swift library that tells you what the "diff" is between two collections, namely, the series of "edit operation

Jack Flintermann 1.8k Dec 12, 2022
A generic small reusable components for data source implementation for UITableView/UICollectionView in Swift.

GenericDataSource A generic small reusable components for data source implementation for UITableView/UICollectionView written in Swift. Features Basic

null 132 Sep 8, 2021
A Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)

Reusable A Swift mixin to use UITableViewCells, UICollectionViewCells and UIViewControllers in a type-safe way, without the need to manipulate their S

Olivier Halligon 2.9k Jan 3, 2023
Easy way to integrate pagination with dummy views in CollectionView, make Instagram "Discover" within minutes.

AZCollectionView Controller Features Automatic pagination handling No more awkward empty CollectionView screen AZ CollectionVIew controller give you a

Afroz Zaheer 95 May 11, 2022
A SwiftUI collection view with support for custom layouts, preloading, and more.

ASCollectionView A SwiftUI implementation of UICollectionView & UITableView. Here's some of its useful features: supports preloading and onAppear/onDi

Apptek Studios 1.3k Dec 24, 2022
A custom paging behavior that peeks the previous and next items in a collection view

MSPeekCollectionViewDelegateImplementation Version 3.0.0 is here! ?? The peeking logic is now done using a custom UICollectionViewLayout which makes i

Maher Santina 353 Dec 16, 2022
Custom transition between two collection view layouts

Display Switcher We designed a UI that allows users to switch between list and grid views on the fly and choose the most convenient display type. List

Yalantis 2.3k Dec 14, 2022
Generic collection view controller with external data processing

FlexibleCollectionViewController Swift library of generic collection view controller with external data processing of functionality, like determine ce

Dmytro Pylypenko 3 Jul 16, 2018
Lightweight custom collection view inspired by Airbnb.

ASCollectionView Lightweight custom collection view inspired by Airbnb. Screenshots Requirements ASCollectionView Version Minimum iOS Target Swift Ver

Abdullah Selek 364 Nov 24, 2022
A component to quickly scroll between collection view sections

SectionScrubber The scrubber will move along when scrolling the UICollectionView it has been added to. When you pan the scrubber you 'scrub' over the

Elvis 190 Aug 17, 2022
💾 🔜📱 Type-safe data-driven CollectionView, TableView Framework. (We can also use ASCollectionNode)

⚠️ The latest updates is this PR. It changes the difference algorithm to DifferenceKit. DataSources ?? ?? ?? Type-safe data-driven List-UI Framework.

Muukii 563 Dec 16, 2022
An iOS drop-in UITableView, UICollectionView and UIScrollView superclass category for showing a customizable floating button on top of it.

MEVFloatingButton An iOS drop-in UITableView, UICollectionView, UIScrollView superclass category for showing a customizable floating button on top of

Manuel Escrig 298 Jul 17, 2022
Automates prefetching of content in UITableView and UICollectionView

Automates preheating (prefetching) of content in UITableView and UICollectionView. Deprecated on iOS 10. This library is similar to UITableViewDataSou

Alexander Grebenyuk 633 Sep 16, 2022
A data-driven UICollectionView framework for building fast and flexible lists.

A data-driven UICollectionView framework for building fast and flexible lists. Main Features ?? Never call performBatchUpdates(_:, completion:) or rel

Instagram 12.5k Jan 1, 2023
Netflix and App Store like UITableView with UICollectionView, written in pure Swift 4.2

GLTableCollectionView Branch Status master develop What it is GLTableCollectionView is a ready to use UITableViewController with a UICollectionView fo

Giulio 708 Nov 17, 2022
Incremental update tool to UITableView and UICollectionView

EditDistance is one of the incremental update tool for UITableView and UICollectionView. The followings show how this library update UI. They generate

Kazuhiro Hayashi 90 Jun 9, 2022