├── .gitattributes ├── .gitignore ├── Classes ├── LRCollectionTableModel.h ├── LRCollectionTableModel.m ├── LRSelfNotifyingTableModel.h ├── LRSelfNotifyingTableModel.m ├── LRTableModel.h ├── LRTableModelEvent.h ├── LRTableModelEvent.m └── LRTableModelEventListener.h ├── Examples ├── Examples.plist ├── ExamplesIndexViewController.h ├── ExamplesIndexViewController.m ├── GithubRepositories.h ├── GithubRepositories.m ├── GroupedTableModel │ ├── GroupedTableModel.h │ ├── GroupedTableModel.m │ ├── GroupedTableViewController.h │ └── GroupedTableViewController.m ├── MainWindow.xib ├── SearchTableModel │ ├── SearchTableModel.h │ ├── SearchTableModel.m │ ├── SearchTableViewController.h │ ├── SearchTableViewController.m │ └── SearchTableViewController.xib ├── SimpleTableModel │ ├── SimpleObject.h │ ├── SimpleObject.m │ ├── SimpleTableViewController.h │ ├── SimpleTableViewController.m │ ├── SortableTableModel.h │ └── SortableTableModel.m ├── TableViewModelAppDelegate.h ├── TableViewModelAppDelegate.m └── main.m ├── LRTableModel-Info.plist ├── LRTableModel.podspec ├── LRTableModel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── luke.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── Example App.xcscheme │ │ └── LRTableModel.xcscheme └── xcuserdata │ └── luke.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── LRTableModel.xcworkspace └── contents.xcworkspacedata ├── LRTableModel_Prefix.pch ├── MIT-LICENSE ├── Podfile ├── Podfile.lock ├── README.markdown ├── Resources └── repositories.plist ├── Tests-Info.plist └── Tests ├── Support ├── HCPassesBlock.h ├── HCPassesBlock.m ├── TestHelper.h └── TestHelper.m └── Unit └── SimpleTableViewModelTest.m /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -crlf -diff -merge 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder and whatnot 2 | .DS_Store 3 | 4 | # XCode (and ancestors) per-user config (very noisy, and not relevant) 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | 12 | 13 | # Generated files 14 | VersionX-revision.h 15 | 16 | 17 | # build products 18 | build/ 19 | *.[oa] 20 | 21 | # Other source repository archive directories (protects when importing) 22 | .hg 23 | .svn 24 | CVS 25 | 26 | 27 | # automatic backup files 28 | *~.nib 29 | *.swp 30 | *~ 31 | *(Autosaved).rtfd/ 32 | Backup[ ]of[ ]*.pages/ 33 | Backup[ ]of[ ]*.key/ 34 | Backup[ ]of[ ]*.numbers/ 35 | Pods 36 | xcuserdata -------------------------------------------------------------------------------- /Classes/LRCollectionTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LRCollectionTableModel.h 3 | // LRTableModel 4 | // 5 | // Created by Luke Redpath on 12/12/2012. 6 | // 7 | // 8 | 9 | #import 10 | #import "LRSelfNotifyingTableModel.h" 11 | 12 | /* This is about the simplest possible implementation of a table model, and 13 | can be used when all you need to bind to your table view is a collection of objects 14 | in a single section. 15 | */ 16 | @interface LRCollectionTableModel : LRSelfNotifyingTableModel 17 | 18 | /* This property is mainly aimed at sub-classes who wish to get access to the 19 | objects for use within their own implementation. 20 | */ 21 | @property (nonatomic, readonly) NSArray *objects; 22 | 23 | /* 24 | Set the title that will be used for the single section represented by the model. 25 | 26 | Default is nil. 27 | */ 28 | @property (nonatomic, copy) NSString *sectionTitle; 29 | 30 | - (id)initWithObjects:(NSArray *)objects; 31 | 32 | /* 33 | Updates the objects in the model. 34 | 35 | Calling this will trigger a LRTableModelRefreshDataEvent event. 36 | */ 37 | - (void)setObjects:(NSArray *)objects; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/LRCollectionTableModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LRCollectionTableModel.m 3 | // LRTableModel 4 | // 5 | // Created by Luke Redpath on 12/12/2012. 6 | // 7 | // 8 | 9 | #import "LRCollectionTableModel.h" 10 | 11 | @implementation LRCollectionTableModel { 12 | NSArray *_objects; 13 | } 14 | 15 | - (id)initWithObjects:(NSArray *)objects 16 | { 17 | if ((self = [super init])) { 18 | _objects = [objects copy]; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)setObjects:(NSArray *)objects 24 | { 25 | [self beginUpdates]; 26 | 27 | _objects = [objects copy]; 28 | 29 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 30 | [self endUpdates]; 31 | } 32 | 33 | #pragma mark - LRTableModel 34 | 35 | - (NSInteger)numberOfSections 36 | { 37 | return 1; 38 | } 39 | 40 | - (NSInteger)numberOfRowsInSection:(NSInteger)sectionIndex 41 | { 42 | return _objects.count; 43 | } 44 | 45 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath 46 | { 47 | return [_objects objectAtIndex:indexPath.row]; 48 | } 49 | 50 | - (NSString *)titleforSection:(NSInteger)section 51 | { 52 | return self.sectionTitle; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Classes/LRSelfNotifyingTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LRAbstractTableModel.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRTableModel.h" 11 | 12 | /* This is intended as an abstract base class that implementers of the 13 | LRTableModel protocol can use to create table models that support background 14 | updates. 15 | 16 | Despite the name, this class doesn't actually implement the LRTableModel 17 | protocol itself - it doesn't have the information it needs to do so. 18 | */ 19 | @interface LRSelfNotifyingTableModel : NSObject 20 | 21 | - (void)addTableModelListener:(id)listener; 22 | - (void)removeTableModelListener:(id)listener; 23 | 24 | /* The following methods are intended to be called by sub-classes. */ 25 | 26 | - (void)notifyListeners:(LRTableModelEvent *)event; 27 | - (void)beginUpdates; 28 | - (void)endUpdates; 29 | - (void)performUpdatesWithBlock:(dispatch_block_t)updateBlock; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Classes/LRSelfNotifyingTableModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LRAbstractTableModel.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "LRSelfNotifyingTableModel.h" 10 | 11 | @implementation LRSelfNotifyingTableModel { 12 | NSMutableArray *_eventListeners; 13 | } 14 | 15 | - (id)init 16 | { 17 | if (self = [super init]) { 18 | _eventListeners = [[NSMutableArray alloc] init]; 19 | } 20 | return self; 21 | } 22 | 23 | 24 | - (void)notifyListeners:(LRTableModelEvent *)event; 25 | { 26 | for (id listener in _eventListeners) { 27 | [listener tableModelChanged:event]; 28 | } 29 | } 30 | 31 | - (void)beginUpdates 32 | { 33 | for (id listener in _eventListeners) { 34 | if ([listener respondsToSelector:@selector(tableModelWillBeginUpdates)]) { 35 | [listener tableModelWillBeginUpdates]; 36 | } 37 | } 38 | } 39 | 40 | - (void)endUpdates 41 | { 42 | for (id listener in _eventListeners) { 43 | if ([listener respondsToSelector:@selector(tableModelDidEndUpdates)]) { 44 | [listener tableModelDidEndUpdates]; 45 | } 46 | } 47 | } 48 | 49 | - (void)performUpdatesWithBlock:(dispatch_block_t)updateBlock 50 | { 51 | [self beginUpdates]; 52 | 53 | updateBlock(); 54 | 55 | [self endUpdates]; 56 | } 57 | 58 | - (void)addTableModelListener:(id)listener; 59 | { 60 | [_eventListeners addObject:listener]; 61 | } 62 | 63 | - (void)removeTableModelListener:(id)listener; 64 | { 65 | [_eventListeners removeObject:listener]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Classes/LRTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LRTableViewModel.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRTableModelEventListener.h" 11 | 12 | @protocol LRTableModel 13 | 14 | - (NSInteger)numberOfSections; 15 | - (NSInteger)numberOfRowsInSection:(NSInteger)sectionIndex; 16 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath; 17 | - (NSString *)titleforSection:(NSInteger)section; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Classes/LRTableModelEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // LRTableModelEvent.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | LRTableModelInsertRowEvent = 0, 13 | LRTableModelUpdateRowEvent, 14 | LRTableModelDeleteRowEvent, 15 | LRTableModelRefreshDataEvent 16 | } LRTableModelEventType; 17 | 18 | @interface LRTableModelEvent : NSObject { 19 | LRTableModelEventType type; 20 | NSIndexPath *__strong indexPath; 21 | } 22 | @property (nonatomic, readonly) LRTableModelEventType type; 23 | @property (nonatomic, readonly) NSIndexPath *indexPath; 24 | 25 | - (id)initWithEventType:(LRTableModelEventType)eventType indexPath:(NSIndexPath *)theIndexPath; 26 | - (BOOL)isEqualToEvent:(LRTableModelEvent *)otherEvent; 27 | - (NSArray *)indexPaths; 28 | + (id)insertionAtRow:(NSInteger)row section:(NSInteger)section; 29 | + (id)updatedRow:(NSInteger)row section:(NSInteger)section; 30 | + (id)deletedRow:(NSInteger)row section:(NSInteger)section; 31 | + (id)refreshedData; 32 | @end 33 | -------------------------------------------------------------------------------- /Classes/LRTableModelEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // LRTableModelEvent.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "LRTableModelEvent.h" 10 | 11 | 12 | @implementation LRTableModelEvent 13 | 14 | @synthesize type; 15 | @synthesize indexPath; 16 | 17 | - (id)initWithEventType:(LRTableModelEventType)eventType indexPath:(NSIndexPath *)theIndexPath; 18 | { 19 | if (self = [super init]) { 20 | type = eventType; 21 | indexPath = theIndexPath; 22 | } 23 | return self; 24 | } 25 | 26 | 27 | - (NSArray *)indexPaths; 28 | { 29 | return [NSArray arrayWithObject:indexPath]; 30 | } 31 | 32 | - (NSString *)description 33 | { 34 | NSString *eventType = nil; 35 | switch (self.type) { 36 | case LRTableModelInsertRowEvent: 37 | eventType = @"LRTableModelInsertRowEvent"; 38 | break; 39 | case LRTableModelUpdateRowEvent: 40 | eventType = @"LRTableModelUpdateRowEvent"; 41 | break; 42 | case LRTableModelDeleteRowEvent: 43 | eventType = @"LRTableModelDeleteRowEvent"; 44 | break; 45 | case LRTableModelRefreshDataEvent: 46 | eventType = @"LRTableModelRefreshDataEvent"; 47 | break; 48 | default: 49 | eventType = @"UnknownEventType"; 50 | break; 51 | } 52 | return [NSString stringWithFormat:@"%@ atIndexPath:{%d, %d}", eventType, self.indexPath.section, self.indexPath.row]; 53 | } 54 | 55 | - (BOOL)isEqual:(id)object 56 | { 57 | if (![object isKindOfClass:[self class]]) { 58 | return NO; 59 | } 60 | return [self isEqualToEvent:object]; 61 | } 62 | 63 | - (BOOL)isEqualToEvent:(LRTableModelEvent *)otherEvent 64 | { 65 | if (self.indexPath == nil) { 66 | return self.type == otherEvent.type; 67 | } 68 | return [otherEvent.indexPath isEqual:self.indexPath] && 69 | otherEvent.type == self.type; 70 | } 71 | 72 | + (id)insertionAtRow:(NSInteger)row section:(NSInteger)section; 73 | { 74 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 75 | return [[self alloc] initWithEventType:LRTableModelInsertRowEvent indexPath:indexPath]; 76 | } 77 | 78 | + (id)updatedRow:(NSInteger)row section:(NSInteger)section; 79 | { 80 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 81 | return [[self alloc] initWithEventType:LRTableModelUpdateRowEvent indexPath:indexPath]; 82 | } 83 | 84 | + (id)deletedRow:(NSInteger)row section:(NSInteger)section; 85 | { 86 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 87 | return [[self alloc] initWithEventType:LRTableModelDeleteRowEvent indexPath:indexPath]; 88 | } 89 | 90 | + (id)refreshedData; 91 | { 92 | return [[self alloc] initWithEventType:LRTableModelRefreshDataEvent indexPath:nil]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Classes/LRTableModelEventListener.h: -------------------------------------------------------------------------------- 1 | // 2 | // LRTableViewModelEventListener.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRTableModelEvent.h" 11 | 12 | @protocol LRTableModelEventListener 13 | 14 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent; 15 | 16 | @optional 17 | - (void)tableModelWillBeginUpdates; 18 | - (void)tableModelDidEndUpdates; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Examples/Examples.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | name 7 | SimpleTableModel 8 | description 9 | A basic, array-backed table model 10 | controller 11 | SimpleTableViewController 12 | 13 | 14 | name 15 | SearchTableModel 16 | description 17 | Using a table model to implement search 18 | controller 19 | SearchTableViewController 20 | 21 | 22 | name 23 | GroupedTableModel 24 | description 25 | Working with sections 26 | controller 27 | GroupedTableViewController 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Examples/ExamplesIndexViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesIndexViewController.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRAbstractTableModel.h" 11 | 12 | @interface ExamplesTableModel : LRAbstractTableModel 13 | { 14 | NSArray *examples; 15 | } 16 | - (void)loadExamplesFromPlistNamed:(NSString *)plistName inBundle:(NSBundle *)bundle; 17 | @end 18 | 19 | @interface ExamplesIndexViewController : UITableViewController { 20 | ExamplesTableModel *examplesTableModel; 21 | } 22 | @property (unsafe_unretained, nonatomic, readonly) ExamplesTableModel *examplesTableModel; 23 | @end 24 | -------------------------------------------------------------------------------- /Examples/ExamplesIndexViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesIndexViewController.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "ExamplesIndexViewController.h" 10 | #import "LRTableModelEvent.h" 11 | 12 | @implementation ExamplesTableModel 13 | 14 | - (id)initWithCellProvider:(id )theCellProvider 15 | { 16 | if (self = [super initWithCellProvider:theCellProvider]) { 17 | examples = [[NSMutableArray alloc] init]; 18 | } 19 | return self; 20 | } 21 | 22 | 23 | - (void)loadExamplesFromPlistNamed:(NSString *)plistName inBundle:(NSBundle *)bundle 24 | { 25 | examples = [NSArray arrayWithContentsOfFile:[bundle pathForResource:plistName ofType:@"plist"]]; 26 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 27 | } 28 | 29 | - (NSInteger)numberOfSections; 30 | { 31 | return 1; 32 | } 33 | 34 | - (NSInteger)numberOfRowsInSection:(NSInteger)sectionIndex; 35 | { 36 | return [examples count]; 37 | } 38 | 39 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath; 40 | { 41 | return [examples objectAtIndex:indexPath.row]; 42 | } 43 | 44 | @end 45 | 46 | @implementation ExamplesIndexViewController 47 | 48 | 49 | - (ExamplesTableModel *)examplesTableModel 50 | { 51 | if (examplesTableModel == nil) { 52 | examplesTableModel = [[ExamplesTableModel alloc] initWithCellProvider:self]; 53 | [examplesTableModel addTableModelListener:self]; 54 | } 55 | return examplesTableModel; 56 | } 57 | 58 | - (void)viewDidLoad 59 | { 60 | [super viewDidLoad]; 61 | 62 | self.tableView.rowHeight = 65; 63 | self.tableView.dataSource = self.examplesTableModel; 64 | 65 | [self.examplesTableModel loadExamplesFromPlistNamed:@"Examples" inBundle:[NSBundle mainBundle]]; 66 | } 67 | 68 | #pragma mark - 69 | #pragma mark LRTableModel methods 70 | 71 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent 72 | { 73 | [self.tableView reloadData]; 74 | } 75 | 76 | - (UITableViewCell *)cellForObjectAtIndexPath:(NSIndexPath *)indexPath reuseIdentifier:(NSString *)reuseIdentifier 77 | { 78 | return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; 79 | } 80 | 81 | - (void)configureCell:(UITableViewCell *)cell forObject:(id)object atIndexPath:(NSIndexPath *)indexPath 82 | { 83 | cell.textLabel.text = [object valueForKey:@"name"]; 84 | cell.detailTextLabel.text = [object valueForKey:@"description"]; 85 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 86 | } 87 | 88 | #pragma mark - 89 | #pragma mark UITableViewDelegate 90 | 91 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 92 | { 93 | NSDictionary *exampleData = [self.examplesTableModel objectAtIndexPath:indexPath]; 94 | 95 | UIViewController *exampleViewController = [[NSClassFromString([exampleData valueForKey:@"controller"]) alloc] init]; 96 | exampleViewController.title = [exampleData valueForKey:@"name"]; 97 | [self.navigationController pushViewController:exampleViewController animated:YES]; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Examples/GithubRepositories.h: -------------------------------------------------------------------------------- 1 | // 2 | // GithubRepositories.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface GithubRepositories : NSObject { 13 | 14 | } 15 | + (NSArray *)exampleRepositories; 16 | + (NSArray *)repositoryNamesInGroupsOf:(NSInteger)numberOfItemsInSection; 17 | @end 18 | -------------------------------------------------------------------------------- /Examples/GithubRepositories.m: -------------------------------------------------------------------------------- 1 | // 2 | // GithubRepositories.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "GithubRepositories.h" 10 | 11 | @implementation GithubRepositories 12 | 13 | + (NSArray *)exampleRepositories 14 | { 15 | return [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"repositories" ofType:@"plist"]]; 16 | } 17 | 18 | + (NSArray *)repositoryNamesInGroupsOf:(NSInteger)numberOfItemsInSection 19 | { 20 | NSMutableArray *objectsInSections = [NSMutableArray arrayWithObject:[NSMutableArray array]]; 21 | 22 | __block NSMutableArray *currentSection = [objectsInSections objectAtIndex:0]; 23 | 24 | [[[self exampleRepositories] valueForKeyPath:@"@unionOfObjects.name"] enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) { 25 | if (idx > 0 && (idx % numberOfItemsInSection) == 0) { 26 | currentSection = [NSMutableArray arrayWithObject:object]; 27 | [objectsInSections addObject:currentSection]; 28 | } else { 29 | [currentSection addObject:object]; 30 | } 31 | }]; 32 | 33 | return objectsInSections; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Examples/GroupedTableModel/GroupedTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedTableModel.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 11/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SortableTableModel.h" 11 | 12 | @interface GroupedTableModel : SortableTableModel 13 | { 14 | NSMutableArray *sections; 15 | NSMutableArray *sectionTitles; 16 | } 17 | - (void)setSections:(NSArray *)arrayOfSectionArrays sectionTitles:(NSArray *)titles; 18 | - (void)rotateLastItem; 19 | @end 20 | -------------------------------------------------------------------------------- /Examples/GroupedTableModel/GroupedTableModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedTableModel.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 11/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "GroupedTableModel.h" 10 | 11 | 12 | @implementation GroupedTableModel 13 | 14 | - (id)initWithCellProvider:(id )theCellProvider 15 | { 16 | if (self = [super initWithCellProvider:theCellProvider]) { 17 | sections = [[NSMutableArray alloc] initWithObjects:[NSArray array], nil]; 18 | sectionTitles = [[NSMutableArray alloc] init]; 19 | } 20 | return self; 21 | } 22 | 23 | 24 | - (NSArray *)objectsInSection:(NSInteger)sectionIndex; 25 | { 26 | return [sections objectAtIndex:sectionIndex]; 27 | } 28 | 29 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath 30 | { 31 | return [[self objectsInSection:indexPath.section] objectAtIndex:indexPath.row]; 32 | } 33 | 34 | - (void)setSections:(NSArray *)arrayOfSectionArrays sectionTitles:(NSArray *)titles; 35 | { 36 | [sections setArray:arrayOfSectionArrays]; 37 | [sectionTitles setArray:titles]; 38 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 39 | } 40 | 41 | - (NSString *)headerforSection:(NSInteger)section; 42 | { 43 | return [sectionTitles objectAtIndex:section]; 44 | } 45 | 46 | /* 47 | * this takes the last object from the list, pushes it to the top 48 | * and moves everything down one (which means the last object in 49 | * each section moves to the top of the next section 50 | */ 51 | - (void)rotateLastItem; 52 | { 53 | NSMutableArray *lastSection = [sections lastObject]; 54 | 55 | [self beginUpdates]; 56 | 57 | id lastObject = [lastSection lastObject]; 58 | NSInteger indexOfLastObject = [lastSection indexOfObject:lastObject]; 59 | [lastSection removeLastObject]; 60 | [self notifyListeners:[LRTableModelEvent deletedRow:indexOfLastObject section:[sections indexOfObject:lastSection]]]; 61 | 62 | for (NSMutableArray *section in sections) { 63 | [section insertObject:lastObject atIndex:0]; 64 | [self notifyListeners:[LRTableModelEvent insertionAtRow:0 section:[sections indexOfObject:section]]]; 65 | 66 | [self endUpdates]; 67 | 68 | if (section != [sections lastObject]) { 69 | [self beginUpdates]; 70 | 71 | lastObject = [section lastObject]; 72 | NSInteger indexOfLastObject = [section indexOfObject:lastObject]; 73 | [section removeLastObject]; 74 | [self notifyListeners:[LRTableModelEvent deletedRow:indexOfLastObject section:[sections indexOfObject:section]]]; 75 | } 76 | } 77 | } 78 | 79 | #pragma mark - 80 | #pragma mark LRTableModel 81 | 82 | - (NSInteger)numberOfSections 83 | { 84 | return [sections count]; 85 | } 86 | 87 | - (NSInteger)numberOfRowsInSection:(NSInteger)sectionIndex; 88 | { 89 | return [[self objectsInSection:sectionIndex] count]; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /Examples/GroupedTableModel/GroupedTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedTableViewController.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRTableModelCellProvider.h" 11 | #import "LRTableModelEventListener.h" 12 | 13 | @class GroupedTableModel; 14 | 15 | @interface GroupedTableViewController : UITableViewController { 16 | GroupedTableModel *tableModel; 17 | } 18 | @property (unsafe_unretained, nonatomic, readonly) GroupedTableModel *tableModel; 19 | @end 20 | -------------------------------------------------------------------------------- /Examples/GroupedTableModel/GroupedTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedTableViewController.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "GroupedTableViewController.h" 10 | #import "GithubRepositories.h" 11 | #import "GroupedTableModel.h" 12 | 13 | @implementation GroupedTableViewController 14 | 15 | - (GroupedTableModel *)tableModel 16 | { 17 | if (tableModel == nil) { 18 | tableModel = [[GroupedTableModel alloc] initWithCellProvider:self]; 19 | [tableModel addTableModelListener:self]; 20 | } 21 | return tableModel; 22 | } 23 | 24 | #define kNumberOfObjectsInSection 5 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | 30 | self.tableView.dataSource = self.tableModel; 31 | 32 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(rotateButtonTapped:)]; 33 | 34 | NSArray *sections = [GithubRepositories repositoryNamesInGroupsOf:kNumberOfObjectsInSection]; 35 | 36 | NSMutableArray *sectionTitles = [NSMutableArray array]; 37 | for (int i = 0; i < sections.count; i++) { 38 | NSInteger firstItemIndex = (i * kNumberOfObjectsInSection); 39 | [sectionTitles addObject:[NSString stringWithFormat:@"Item %d to %d", firstItemIndex + 1, firstItemIndex + kNumberOfObjectsInSection]]; 40 | } 41 | 42 | [self.tableModel setSections:sections sectionTitles:sectionTitles]; 43 | } 44 | 45 | - (void)tableModelWillBeginUpdates 46 | { 47 | [self.tableView beginUpdates]; 48 | } 49 | 50 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent 51 | { 52 | switch (changeEvent.type) { 53 | case LRTableModelInsertRowEvent: 54 | [self.tableView insertRowsAtIndexPaths:changeEvent.indexPaths withRowAnimation:UITableViewRowAnimationTop]; 55 | break; 56 | case LRTableModelDeleteRowEvent: 57 | [self.tableView deleteRowsAtIndexPaths:changeEvent.indexPaths withRowAnimation:UITableViewRowAnimationBottom]; 58 | break; 59 | default: 60 | [self.tableView reloadData]; 61 | break; 62 | } 63 | } 64 | 65 | - (void)tableModelDidEndUpdates 66 | { 67 | [self.tableView endUpdates]; 68 | } 69 | 70 | - (void)configureCell:(UITableViewCell *)cell forObject:(id)object atIndexPath:(NSIndexPath *)indexPath 71 | { 72 | cell.textLabel.text = object; 73 | } 74 | 75 | - (void)rotateButtonTapped:(id)sender 76 | { 77 | [self.tableModel rotateLastItem]; 78 | } 79 | 80 | @end 81 | 82 | -------------------------------------------------------------------------------- /Examples/MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10F569 6 | 788 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 117 12 | 13 | 14 | YES 15 | 16 | 17 | YES 18 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 19 | 20 | 21 | YES 22 | 23 | YES 24 | 25 | 26 | YES 27 | 28 | 29 | 30 | YES 31 | 32 | IBFilesOwner 33 | IBCocoaTouchFramework 34 | 35 | 36 | IBFirstResponder 37 | IBCocoaTouchFramework 38 | 39 | 40 | IBCocoaTouchFramework 41 | 42 | 43 | 44 | 1316 45 | 46 | {320, 480} 47 | 48 | 1 49 | MSAxIDEAA 50 | 51 | NO 52 | NO 53 | 54 | IBCocoaTouchFramework 55 | YES 56 | 57 | 58 | 59 | 60 | 1 61 | 62 | IBCocoaTouchFramework 63 | NO 64 | 65 | 66 | 256 67 | {0, 0} 68 | NO 69 | YES 70 | YES 71 | IBCocoaTouchFramework 72 | 73 | 74 | YES 75 | 76 | 77 | Example List 78 | IBCocoaTouchFramework 79 | 80 | 81 | 82 | 1 83 | 84 | IBCocoaTouchFramework 85 | NO 86 | 87 | 88 | 89 | 90 | 91 | 92 | YES 93 | 94 | 95 | delegate 96 | 97 | 98 | 99 | 4 100 | 101 | 102 | 103 | window 104 | 105 | 106 | 107 | 5 108 | 109 | 110 | 111 | navigationController 112 | 113 | 114 | 115 | 14 116 | 117 | 118 | 119 | rootViewController 120 | 121 | 122 | 123 | 15 124 | 125 | 126 | 127 | 128 | YES 129 | 130 | 0 131 | 132 | 133 | 134 | 135 | 136 | 2 137 | 138 | 139 | YES 140 | 141 | 142 | 143 | 144 | -1 145 | 146 | 147 | File's Owner 148 | 149 | 150 | 3 151 | 152 | 153 | 154 | 155 | -2 156 | 157 | 158 | 159 | 160 | 10 161 | 162 | 163 | YES 164 | 165 | 166 | 167 | 168 | 169 | 170 | 11 171 | 172 | 173 | YES 174 | 175 | 176 | 177 | 178 | 179 | 12 180 | 181 | 182 | 183 | 184 | 13 185 | 186 | 187 | 188 | 189 | 190 | 191 | YES 192 | 193 | YES 194 | -1.CustomClassName 195 | -2.CustomClassName 196 | 10.IBEditorWindowLastContentRect 197 | 10.IBPluginDependency 198 | 11.CustomClassName 199 | 11.IBPluginDependency 200 | 12.IBPluginDependency 201 | 13.IBPluginDependency 202 | 2.IBAttributePlaceholdersKey 203 | 2.IBEditorWindowLastContentRect 204 | 2.IBPluginDependency 205 | 3.CustomClassName 206 | 3.IBPluginDependency 207 | 208 | 209 | YES 210 | UIApplication 211 | UIResponder 212 | {{0, 665}, {320, 480}} 213 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 214 | ExamplesIndexViewController 215 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 216 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 217 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 218 | 219 | YES 220 | 221 | 222 | YES 223 | 224 | 225 | {{198, 376}, {320, 480}} 226 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 227 | TableViewModelAppDelegate 228 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 229 | 230 | 231 | 232 | YES 233 | 234 | 235 | YES 236 | 237 | 238 | 239 | 240 | YES 241 | 242 | 243 | YES 244 | 245 | 246 | 247 | 15 248 | 249 | 250 | 251 | YES 252 | 253 | ExamplesIndexViewController 254 | UITableViewController 255 | 256 | IBProjectSource 257 | Classes/ExamplesIndexViewController.h 258 | 259 | 260 | 261 | TableViewModelAppDelegate 262 | NSObject 263 | 264 | YES 265 | 266 | YES 267 | navigationController 268 | window 269 | 270 | 271 | YES 272 | UINavigationController 273 | UIWindow 274 | 275 | 276 | 277 | YES 278 | 279 | YES 280 | navigationController 281 | window 282 | 283 | 284 | YES 285 | 286 | navigationController 287 | UINavigationController 288 | 289 | 290 | window 291 | UIWindow 292 | 293 | 294 | 295 | 296 | IBProjectSource 297 | Classes/TableViewModelAppDelegate.h 298 | 299 | 300 | 301 | TableViewModelAppDelegate 302 | NSObject 303 | 304 | IBUserSource 305 | 306 | 307 | 308 | 309 | 310 | YES 311 | 312 | NSObject 313 | 314 | IBFrameworkSource 315 | Foundation.framework/Headers/NSError.h 316 | 317 | 318 | 319 | NSObject 320 | 321 | IBFrameworkSource 322 | Foundation.framework/Headers/NSFileManager.h 323 | 324 | 325 | 326 | NSObject 327 | 328 | IBFrameworkSource 329 | Foundation.framework/Headers/NSKeyValueCoding.h 330 | 331 | 332 | 333 | NSObject 334 | 335 | IBFrameworkSource 336 | Foundation.framework/Headers/NSKeyValueObserving.h 337 | 338 | 339 | 340 | NSObject 341 | 342 | IBFrameworkSource 343 | Foundation.framework/Headers/NSKeyedArchiver.h 344 | 345 | 346 | 347 | NSObject 348 | 349 | IBFrameworkSource 350 | Foundation.framework/Headers/NSObject.h 351 | 352 | 353 | 354 | NSObject 355 | 356 | IBFrameworkSource 357 | Foundation.framework/Headers/NSRunLoop.h 358 | 359 | 360 | 361 | NSObject 362 | 363 | IBFrameworkSource 364 | Foundation.framework/Headers/NSThread.h 365 | 366 | 367 | 368 | NSObject 369 | 370 | IBFrameworkSource 371 | Foundation.framework/Headers/NSURL.h 372 | 373 | 374 | 375 | NSObject 376 | 377 | IBFrameworkSource 378 | Foundation.framework/Headers/NSURLConnection.h 379 | 380 | 381 | 382 | NSObject 383 | 384 | IBFrameworkSource 385 | UIKit.framework/Headers/UIAccessibility.h 386 | 387 | 388 | 389 | NSObject 390 | 391 | IBFrameworkSource 392 | UIKit.framework/Headers/UINibLoading.h 393 | 394 | 395 | 396 | NSObject 397 | 398 | IBFrameworkSource 399 | UIKit.framework/Headers/UIResponder.h 400 | 401 | 402 | 403 | UIApplication 404 | UIResponder 405 | 406 | IBFrameworkSource 407 | UIKit.framework/Headers/UIApplication.h 408 | 409 | 410 | 411 | UIBarButtonItem 412 | UIBarItem 413 | 414 | IBFrameworkSource 415 | UIKit.framework/Headers/UIBarButtonItem.h 416 | 417 | 418 | 419 | UIBarItem 420 | NSObject 421 | 422 | IBFrameworkSource 423 | UIKit.framework/Headers/UIBarItem.h 424 | 425 | 426 | 427 | UINavigationBar 428 | UIView 429 | 430 | IBFrameworkSource 431 | UIKit.framework/Headers/UINavigationBar.h 432 | 433 | 434 | 435 | UINavigationController 436 | UIViewController 437 | 438 | IBFrameworkSource 439 | UIKit.framework/Headers/UINavigationController.h 440 | 441 | 442 | 443 | UINavigationItem 444 | NSObject 445 | 446 | 447 | 448 | UIResponder 449 | NSObject 450 | 451 | 452 | 453 | UISearchBar 454 | UIView 455 | 456 | IBFrameworkSource 457 | UIKit.framework/Headers/UISearchBar.h 458 | 459 | 460 | 461 | UISearchDisplayController 462 | NSObject 463 | 464 | IBFrameworkSource 465 | UIKit.framework/Headers/UISearchDisplayController.h 466 | 467 | 468 | 469 | UITableViewController 470 | UIViewController 471 | 472 | IBFrameworkSource 473 | UIKit.framework/Headers/UITableViewController.h 474 | 475 | 476 | 477 | UIView 478 | 479 | IBFrameworkSource 480 | UIKit.framework/Headers/UITextField.h 481 | 482 | 483 | 484 | UIView 485 | UIResponder 486 | 487 | IBFrameworkSource 488 | UIKit.framework/Headers/UIView.h 489 | 490 | 491 | 492 | UIViewController 493 | 494 | 495 | 496 | UIViewController 497 | 498 | IBFrameworkSource 499 | UIKit.framework/Headers/UIPopoverController.h 500 | 501 | 502 | 503 | UIViewController 504 | 505 | IBFrameworkSource 506 | UIKit.framework/Headers/UISplitViewController.h 507 | 508 | 509 | 510 | UIViewController 511 | 512 | IBFrameworkSource 513 | UIKit.framework/Headers/UITabBarController.h 514 | 515 | 516 | 517 | UIViewController 518 | UIResponder 519 | 520 | IBFrameworkSource 521 | UIKit.framework/Headers/UIViewController.h 522 | 523 | 524 | 525 | UIWindow 526 | UIView 527 | 528 | IBFrameworkSource 529 | UIKit.framework/Headers/UIWindow.h 530 | 531 | 532 | 533 | 534 | 0 535 | IBCocoaTouchFramework 536 | 537 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 538 | 539 | 540 | 541 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 542 | 543 | 544 | YES 545 | TableViewModel.xcodeproj 546 | 3 547 | 117 548 | 549 | 550 | -------------------------------------------------------------------------------- /Examples/SearchTableModel/SearchTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableModel.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SortableTableModel.h" 11 | 12 | @interface SearchTableModel : SortableTableModel { 13 | NSArray *filteredObjects; 14 | } 15 | - (void)filterObjectsWithPrefix:(NSString *)prefix; 16 | - (void)clearSearchFilter; 17 | @end 18 | -------------------------------------------------------------------------------- /Examples/SearchTableModel/SearchTableModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableModel.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "SearchTableModel.h" 10 | #import "SimpleObject.h" 11 | 12 | @interface SearchTableModel () 13 | - (NSArray *)activeCollection; 14 | @end 15 | 16 | @implementation SearchTableModel 17 | 18 | - (id)init 19 | { 20 | if (self = [super init]) { 21 | filteredObjects = nil; 22 | } 23 | return self; 24 | } 25 | 26 | 27 | - (NSArray *)activeCollection; 28 | { 29 | if (filteredObjects) { 30 | return filteredObjects; 31 | } 32 | return objects; 33 | } 34 | 35 | - (NSInteger)numberOfRowsInSection:(NSInteger)section; 36 | { 37 | return [[self activeCollection] count]; 38 | } 39 | 40 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath; 41 | { 42 | return [[self activeCollection] objectAtIndex:indexPath.row]; 43 | } 44 | 45 | NSPredicate *predicateForPrefix(NSString *prefix) 46 | { 47 | return [NSPredicate predicateWithBlock:^(id evaluatedObject, NSDictionary *bindings) { 48 | return [[(SimpleObject *)evaluatedObject title] hasPrefix:prefix]; 49 | }]; 50 | } 51 | 52 | - (void)filterObjectsWithPrefix:(NSString *)prefix; 53 | { 54 | filteredObjects = [objects filteredArrayUsingPredicate:predicateForPrefix(prefix)]; 55 | } 56 | 57 | - (void)clearSearchFilter; 58 | { 59 | filteredObjects = nil; 60 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Examples/SearchTableModel/SearchTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableViewController.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRTableModel.h" 11 | 12 | @class SearchTableModel; 13 | 14 | @interface SearchTableViewController : UITableViewController { 15 | SearchTableModel *searchTableModel; 16 | } 17 | @property (nonatomic, strong) IBOutlet SearchTableModel *searchTableModel; 18 | @end 19 | -------------------------------------------------------------------------------- /Examples/SearchTableModel/SearchTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableViewController.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 10/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "SearchTableViewController.h" 10 | #import "SimpleObject.h" 11 | #import "SearchTableModel.h" 12 | #import "GithubRepositories.h" 13 | 14 | @implementation SearchTableViewController 15 | 16 | @synthesize searchTableModel; 17 | 18 | - (id)init 19 | { 20 | return [self initWithNibName:@"SearchTableViewController" bundle:nil]; 21 | } 22 | 23 | 24 | - (void)awakeFromNib 25 | { 26 | [self.searchTableModel addTableModelListener:self]; 27 | } 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | 33 | self.tableView.rowHeight = 65; 34 | self.searchDisplayController.searchResultsTableView.rowHeight = self.tableView.rowHeight; 35 | 36 | NSMutableArray *objects = [NSMutableArray array]; 37 | [[GithubRepositories exampleRepositories] enumerateObjectsUsingBlock:^(id repository, NSUInteger idx, BOOL *stop) { 38 | SimpleObject *object = [[SimpleObject alloc] initWithTitle:[repository objectForKey:@"name"] description:[repository objectForKey:@"description"]]; 39 | [objects addObject:object]; 40 | }]; 41 | 42 | [self.searchTableModel setObjects:objects]; 43 | } 44 | 45 | #pragma mark - 46 | #pragma mark LRTableModel methods 47 | 48 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent 49 | { 50 | [self.tableView reloadData]; 51 | } 52 | 53 | - (UITableViewCell *)cellForObjectAtIndexPath:(NSIndexPath *)indexPath reuseIdentifier:(NSString *)reuseIdentifier 54 | { 55 | return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; 56 | } 57 | 58 | - (void)configureCell:(UITableViewCell *)cell forObject:(id)object atIndexPath:(NSIndexPath *)indexPath 59 | { 60 | SimpleObject *simpleObject = object; 61 | 62 | cell.detailTextLabel.numberOfLines = 2; 63 | cell.textLabel.text = simpleObject.title; 64 | cell.detailTextLabel.text = simpleObject.description; 65 | } 66 | 67 | #pragma mark - 68 | #pragma mark UISearchDisplayDelegate methods 69 | 70 | - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString 71 | { 72 | [self.searchTableModel filterObjectsWithPrefix:searchString]; 73 | return YES; 74 | } 75 | 76 | - (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller 77 | { 78 | [self.searchTableModel clearSearchFilter]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Examples/SearchTableModel/SearchTableViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10F569 6 | 788 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 117 12 | 13 | 14 | YES 15 | 16 | 17 | YES 18 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 19 | 20 | 21 | YES 22 | 23 | YES 24 | 25 | 26 | YES 27 | 28 | 29 | 30 | YES 31 | 32 | IBFilesOwner 33 | IBCocoaTouchFramework 34 | 35 | 36 | IBFirstResponder 37 | IBCocoaTouchFramework 38 | 39 | 40 | 41 | 274 42 | 43 | YES 44 | 45 | 46 | 290 47 | {320, 44} 48 | 49 | 50 | 3 51 | IBCocoaTouchFramework 52 | 53 | IBCocoaTouchFramework 54 | 55 | 56 | 57 | {320, 460} 58 | 59 | 60 | 61 | 3 62 | MQA 63 | 64 | YES 65 | IBCocoaTouchFramework 66 | YES 67 | 1 68 | 0 69 | YES 70 | 44 71 | 22 72 | 22 73 | 74 | 75 | 76 | IBCocoaTouchFramework 77 | 78 | 79 | IBCocoaTouchFramework 80 | 81 | 82 | 83 | 84 | YES 85 | 86 | 87 | delegate 88 | 89 | 90 | 91 | 5 92 | 93 | 94 | 95 | cellProvider 96 | 97 | 98 | 99 | 7 100 | 101 | 102 | 103 | searchTableModel 104 | 105 | 106 | 107 | 8 108 | 109 | 110 | 111 | dataSource 112 | 113 | 114 | 115 | 9 116 | 117 | 118 | 119 | view 120 | 121 | 122 | 123 | 10 124 | 125 | 126 | 127 | searchBar 128 | 129 | 130 | 131 | 13 132 | 133 | 134 | 135 | searchDisplayController 136 | 137 | 138 | 139 | 14 140 | 141 | 142 | 143 | searchContentsController 144 | 145 | 146 | 147 | 15 148 | 149 | 150 | 151 | searchResultsDelegate 152 | 153 | 154 | 155 | 17 156 | 157 | 158 | 159 | delegate 160 | 161 | 162 | 163 | 18 164 | 165 | 166 | 167 | delegate 168 | 169 | 170 | 171 | 19 172 | 173 | 174 | 175 | searchResultsDataSource 176 | 177 | 178 | 179 | 20 180 | 181 | 182 | 183 | 184 | YES 185 | 186 | 0 187 | 188 | 189 | 190 | 191 | 192 | -1 193 | 194 | 195 | File's Owner 196 | 197 | 198 | -2 199 | 200 | 201 | 202 | 203 | 4 204 | 205 | 206 | YES 207 | 208 | 209 | 210 | 211 | 212 | 6 213 | 214 | 215 | 216 | 217 | 12 218 | 219 | 220 | 221 | 222 | 11 223 | 224 | 225 | 226 | 227 | 228 | 229 | YES 230 | 231 | YES 232 | -1.CustomClassName 233 | -2.CustomClassName 234 | 11.IBPluginDependency 235 | 4.IBEditorWindowLastContentRect 236 | 4.IBPluginDependency 237 | 6.CustomClassName 238 | 6.IBPluginDependency 239 | 240 | 241 | YES 242 | SearchTableViewController 243 | UIResponder 244 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 245 | {{21, 662}, {320, 460}} 246 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 247 | SearchTableModel 248 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 249 | 250 | 251 | 252 | YES 253 | 254 | 255 | YES 256 | 257 | 258 | 259 | 260 | YES 261 | 262 | 263 | YES 264 | 265 | 266 | 267 | 20 268 | 269 | 270 | 271 | YES 272 | 273 | LRAbstractTableModel 274 | NSObject 275 | 276 | cellProvider 277 | id 278 | 279 | 280 | cellProvider 281 | 282 | cellProvider 283 | id 284 | 285 | 286 | 287 | IBProjectSource 288 | Classes/LRTableModel/LRAbstractTableModel.h 289 | 290 | 291 | 292 | SearchTableModel 293 | SimpleTableModel 294 | 295 | IBProjectSource 296 | Classes/SearchTableModel/SearchTableModel.h 297 | 298 | 299 | 300 | SearchTableViewController 301 | UITableViewController 302 | 303 | searchTableModel 304 | SearchTableModel 305 | 306 | 307 | searchTableModel 308 | 309 | searchTableModel 310 | SearchTableModel 311 | 312 | 313 | 314 | IBProjectSource 315 | Classes/SearchTableModel/SearchTableViewController.h 316 | 317 | 318 | 319 | SimpleTableModel 320 | LRAbstractTableModel 321 | 322 | IBProjectSource 323 | Classes/SimpleTableModel/SimpleTableModel.h 324 | 325 | 326 | 327 | 328 | YES 329 | 330 | NSObject 331 | 332 | IBFrameworkSource 333 | Foundation.framework/Headers/NSError.h 334 | 335 | 336 | 337 | NSObject 338 | 339 | IBFrameworkSource 340 | Foundation.framework/Headers/NSFileManager.h 341 | 342 | 343 | 344 | NSObject 345 | 346 | IBFrameworkSource 347 | Foundation.framework/Headers/NSKeyValueCoding.h 348 | 349 | 350 | 351 | NSObject 352 | 353 | IBFrameworkSource 354 | Foundation.framework/Headers/NSKeyValueObserving.h 355 | 356 | 357 | 358 | NSObject 359 | 360 | IBFrameworkSource 361 | Foundation.framework/Headers/NSKeyedArchiver.h 362 | 363 | 364 | 365 | NSObject 366 | 367 | IBFrameworkSource 368 | Foundation.framework/Headers/NSObject.h 369 | 370 | 371 | 372 | NSObject 373 | 374 | IBFrameworkSource 375 | Foundation.framework/Headers/NSRunLoop.h 376 | 377 | 378 | 379 | NSObject 380 | 381 | IBFrameworkSource 382 | Foundation.framework/Headers/NSThread.h 383 | 384 | 385 | 386 | NSObject 387 | 388 | IBFrameworkSource 389 | Foundation.framework/Headers/NSURL.h 390 | 391 | 392 | 393 | NSObject 394 | 395 | IBFrameworkSource 396 | Foundation.framework/Headers/NSURLConnection.h 397 | 398 | 399 | 400 | NSObject 401 | 402 | IBFrameworkSource 403 | UIKit.framework/Headers/UIAccessibility.h 404 | 405 | 406 | 407 | NSObject 408 | 409 | IBFrameworkSource 410 | UIKit.framework/Headers/UINibLoading.h 411 | 412 | 413 | 414 | NSObject 415 | 416 | IBFrameworkSource 417 | UIKit.framework/Headers/UIResponder.h 418 | 419 | 420 | 421 | UIResponder 422 | NSObject 423 | 424 | 425 | 426 | UIScrollView 427 | UIView 428 | 429 | IBFrameworkSource 430 | UIKit.framework/Headers/UIScrollView.h 431 | 432 | 433 | 434 | UISearchBar 435 | UIView 436 | 437 | IBFrameworkSource 438 | UIKit.framework/Headers/UISearchBar.h 439 | 440 | 441 | 442 | UISearchDisplayController 443 | NSObject 444 | 445 | IBFrameworkSource 446 | UIKit.framework/Headers/UISearchDisplayController.h 447 | 448 | 449 | 450 | UITableView 451 | UIScrollView 452 | 453 | IBFrameworkSource 454 | UIKit.framework/Headers/UITableView.h 455 | 456 | 457 | 458 | UITableViewController 459 | UIViewController 460 | 461 | IBFrameworkSource 462 | UIKit.framework/Headers/UITableViewController.h 463 | 464 | 465 | 466 | UIView 467 | 468 | IBFrameworkSource 469 | UIKit.framework/Headers/UITextField.h 470 | 471 | 472 | 473 | UIView 474 | UIResponder 475 | 476 | IBFrameworkSource 477 | UIKit.framework/Headers/UIView.h 478 | 479 | 480 | 481 | UIViewController 482 | 483 | IBFrameworkSource 484 | UIKit.framework/Headers/UINavigationController.h 485 | 486 | 487 | 488 | UIViewController 489 | 490 | IBFrameworkSource 491 | UIKit.framework/Headers/UIPopoverController.h 492 | 493 | 494 | 495 | UIViewController 496 | 497 | IBFrameworkSource 498 | UIKit.framework/Headers/UISplitViewController.h 499 | 500 | 501 | 502 | UIViewController 503 | 504 | IBFrameworkSource 505 | UIKit.framework/Headers/UITabBarController.h 506 | 507 | 508 | 509 | UIViewController 510 | UIResponder 511 | 512 | IBFrameworkSource 513 | UIKit.framework/Headers/UIViewController.h 514 | 515 | 516 | 517 | 518 | 0 519 | IBCocoaTouchFramework 520 | 521 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 522 | 523 | 524 | 525 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 526 | 527 | 528 | YES 529 | ../../TableViewModel.xcodeproj 530 | 3 531 | 117 532 | 533 | 534 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SimpleObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleObject.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface SimpleObject : NSObject { 13 | NSString *title; 14 | NSString *description; 15 | } 16 | @property (nonatomic, readonly) NSString *title; 17 | @property (nonatomic, readonly) NSString *description; 18 | 19 | - (id)initWithTitle:(NSString *)aTitle description:(NSString *)aDescription; 20 | @end 21 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SimpleObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleObject.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "SimpleObject.h" 10 | 11 | 12 | @implementation SimpleObject 13 | 14 | @synthesize title, description; 15 | 16 | - (id)initWithTitle:(NSString *)aTitle description:(NSString *)aDescription; 17 | { 18 | if (self = [super init]) { 19 | title = [aTitle copy]; 20 | description = [aDescription copy]; 21 | } 22 | return self; 23 | } 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SimpleTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleTableViewController.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "LRTableModelEventListener.h" 12 | 13 | @class SortableTableModel; 14 | 15 | @interface SimpleTableViewController : UITableViewController { 16 | SortableTableModel *tableModel; 17 | UISegmentedControl *sortOrderControl; 18 | } 19 | @property (unsafe_unretained, nonatomic, readonly) SortableTableModel *tableModel; 20 | 21 | - (void)configureToolbarItems; 22 | @end 23 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SimpleTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleTableViewController.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "SimpleTableViewController.h" 10 | #import "SimpleObject.h" 11 | #import "SortableTableModel.h" 12 | #import "GithubRepositories.h" 13 | 14 | @implementation SimpleTableViewController 15 | 16 | 17 | - (SortableTableModel *)tableModel 18 | { 19 | if (tableModel == nil) { 20 | tableModel = [[SortableTableModel alloc] init]; 21 | 22 | [tableModel addTableModelListener:self]; 23 | } 24 | return tableModel; 25 | } 26 | 27 | - (void)viewDidLoad 28 | { 29 | [super viewDidLoad]; 30 | 31 | [self configureToolbarItems]; 32 | 33 | self.tableView.rowHeight = 65; 34 | 35 | UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonTapped:)]; 36 | [self.navigationItem setRightBarButtonItem:addButton]; 37 | 38 | NSMutableArray *objects = [NSMutableArray array]; 39 | 40 | [[GithubRepositories exampleRepositories] enumerateObjectsUsingBlock:^(id repository, NSUInteger idx, BOOL *stop) { 41 | SimpleObject *object = [[SimpleObject alloc] initWithTitle:[repository objectForKey:@"name"] description:[repository objectForKey:@"description"]]; 42 | [objects addObject:object]; 43 | }]; 44 | 45 | [self.tableModel setObjects:objects]; 46 | } 47 | 48 | - (void)viewWillAppear:(BOOL)animated 49 | { 50 | [super viewWillAppear:animated]; 51 | [self.navigationController setToolbarHidden:NO animated:YES]; 52 | } 53 | 54 | - (void)viewWillDisappear:(BOOL)animated 55 | { 56 | [super viewWillDisappear:animated]; 57 | [self.navigationController setToolbarHidden:YES animated:YES]; 58 | } 59 | 60 | - (void)configureToolbarItems; 61 | { 62 | NSMutableArray *items = [NSMutableArray array]; 63 | 64 | if (sortOrderControl == nil) { 65 | sortOrderControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Unsorted", @"Sort Asc", @"Sort Desc", nil]]; 66 | sortOrderControl.segmentedControlStyle = UISegmentedControlStyleBar; 67 | sortOrderControl.selectedSegmentIndex = self.tableModel.sortOrder; 68 | [sortOrderControl addTarget:self action:@selector(sortOrderControlChanged:) forControlEvents:UIControlEventValueChanged]; 69 | } 70 | [items addObject:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]]; 71 | [items addObject:[[UIBarButtonItem alloc] initWithCustomView:sortOrderControl]]; 72 | [items addObject:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]]; 73 | 74 | self.toolbarItems = items; 75 | } 76 | 77 | - (void)addButtonTapped:(id)sender 78 | { 79 | SimpleObject *object = [[SimpleObject alloc] initWithTitle:@"A new object" description:[NSString stringWithFormat:@"This was created at %@", [NSDate date]]]; 80 | [self.tableModel insertObject:object atIndex:0]; 81 | } 82 | 83 | - (void)sortOrderControlChanged:(UISegmentedControl *)control 84 | { 85 | switch (control.selectedSegmentIndex) { 86 | case 0: 87 | [self.tableModel setSortDescriptors:nil]; 88 | break; 89 | case 1: 90 | [self.tableModel setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES]]]; 91 | break; 92 | case 2: 93 | [self.tableModel setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:NO]]]; 94 | break; 95 | default: 96 | break; 97 | } 98 | } 99 | 100 | #pragma mark - 101 | #pragma mark LRTableModelEventListener methods 102 | 103 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent 104 | { 105 | switch (changeEvent.type) { 106 | case LRTableModelRefreshDataEvent: 107 | [self.tableView reloadData]; 108 | break; 109 | case LRTableModelInsertRowEvent: 110 | [self.tableView insertRowsAtIndexPaths:changeEvent.indexPaths withRowAnimation:UITableViewRowAnimationTop]; 111 | break; 112 | default: 113 | [self.tableView reloadData]; 114 | break; 115 | } 116 | } 117 | 118 | #pragma mark - UITableViewDataSource 119 | 120 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 121 | { 122 | static NSString *CellIdentifier = @"SimpleCell"; 123 | 124 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 125 | 126 | if (cell == nil) { 127 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; 128 | } 129 | return cell; 130 | } 131 | 132 | #pragma mark - UITableViewDelegate 133 | 134 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 135 | { 136 | SimpleObject *simpleObject = object; 137 | 138 | cell.detailTextLabel.numberOfLines = 2; 139 | cell.textLabel.text = simpleObject.title; 140 | cell.detailTextLabel.text = simpleObject.description; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SortableTableModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleTableViewModel.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LRCollectionTableModel.h" 11 | 12 | /* 13 | SimpleTableModel extends the built-in LRCollectionTableModel to provide 14 | more fine-grained mutation methods (which in turn generate more fine-grained 15 | change events. 16 | 17 | It also supports sorting with sort descriptors. 18 | */ 19 | @interface SortableTableModel : LRCollectionTableModel 20 | 21 | @property (nonatomic, strong) NSArray *sortDescriptors; 22 | 23 | - (void)addObject:(id)anObject; 24 | - (void)removeObject:(id)anObject; 25 | - (void)insertObject:(id)anObject atIndex:(NSInteger)index; 26 | - (void)replaceObjectAtIndex:(NSInteger)index withObject:(id)anObject; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Examples/SimpleTableModel/SortableTableModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleTableViewModel.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "SortableTableModel.h" 10 | #import "LRTableModelEvent.h" 11 | 12 | @interface SortableTableModel () 13 | - (NSArray *)sortedObjects; 14 | @end 15 | 16 | @implementation SortableTableModel { 17 | NSMutableArray *_mutableObjects; 18 | NSArray *_sortedObjects; 19 | } 20 | 21 | - (NSArray *)objects 22 | { 23 | return [_mutableObjects copy]; 24 | } 25 | 26 | - (NSArray *)sortedObjects 27 | { 28 | if (self.sortDescriptors.count == 0) { 29 | return self.objects; 30 | } 31 | if (_sortedObjects == nil) { 32 | _sortedObjects = [self.objects sortedArrayUsingDescriptors:self.sortDescriptors]; 33 | } 34 | 35 | return _sortedObjects; 36 | } 37 | 38 | - (void)setSortDescriptors:(NSArray *)sortDescriptors 39 | { 40 | [self performUpdatesWithBlock:^{ 41 | _sortedObjects = nil; 42 | _sortDescriptors = [sortDescriptors copy]; 43 | 44 | // changing the sort descriptors always triggers a refresh 45 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 46 | }]; 47 | } 48 | 49 | - (void)addObject:(id)anObject; 50 | { 51 | [self performUpdatesWithBlock:^{ 52 | _sortedObjects = nil; 53 | 54 | [_mutableObjects addObject:anObject]; 55 | 56 | NSInteger indexOfNewObject = [_mutableObjects indexOfObject:anObject]; 57 | [self notifyListeners:[LRTableModelEvent insertionAtRow:indexOfNewObject section:0]]; 58 | }]; 59 | } 60 | 61 | - (void)removeObject:(id)anObject; 62 | { 63 | [self performUpdatesWithBlock:^{ 64 | NSInteger indexOfObject = [_mutableObjects indexOfObject:anObject]; 65 | 66 | _sortedObjects = nil; 67 | 68 | [_mutableObjects removeObject:anObject]; 69 | [self notifyListeners:[LRTableModelEvent deletedRow:indexOfObject section:0]]; 70 | }]; 71 | } 72 | 73 | - (void)replaceObjectAtIndex:(NSInteger)index withObject:(id)anObject; 74 | { 75 | [self performUpdatesWithBlock:^{ 76 | _sortedObjects = nil; 77 | 78 | [_mutableObjects replaceObjectAtIndex:index withObject:anObject]; 79 | [self notifyListeners:[LRTableModelEvent updatedRow:index section:0]]; 80 | }]; 81 | } 82 | 83 | - (void)setObjects:(NSArray *)newObjects; 84 | { 85 | [self performUpdatesWithBlock:^{ 86 | _sortedObjects = nil; 87 | 88 | [_mutableObjects setArray:newObjects]; 89 | [self notifyListeners:[LRTableModelEvent refreshedData]]; 90 | }]; 91 | } 92 | 93 | - (void)insertObject:(id)anObject atIndex:(NSInteger)index; 94 | { 95 | [self performUpdatesWithBlock:^{ 96 | _sortedObjects = nil; 97 | 98 | [_mutableObjects insertObject:anObject atIndex:index]; 99 | [self notifyListeners:[LRTableModelEvent insertionAtRow:index section:0]]; 100 | }]; 101 | } 102 | 103 | #pragma mark - LRTableModel overrides 104 | 105 | - (id)objectAtIndexPath:(NSIndexPath *)indexPath; 106 | { 107 | return [[self sortedObjects] objectAtIndex:indexPath.row]; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /Examples/TableViewModelAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewModelAppDelegate.h 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright LJR Software Limited 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewModelAppDelegate : NSObject 12 | { 13 | UIWindow *window; 14 | UINavigationController *navigationController; 15 | } 16 | @property (nonatomic, strong) IBOutlet UIWindow *window; 17 | @property (nonatomic, strong) IBOutlet UINavigationController *navigationController; 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /Examples/TableViewModelAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewModelAppDelegate.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright LJR Software Limited 2010. All rights reserved. 7 | // 8 | 9 | #import "TableViewModelAppDelegate.h" 10 | #import "SimpleTableViewController.h" 11 | 12 | @implementation TableViewModelAppDelegate 13 | 14 | @synthesize window; 15 | @synthesize navigationController; 16 | 17 | #pragma mark - 18 | #pragma mark Application lifecycle 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 21 | { 22 | [window makeKeyAndVisible]; 23 | 24 | return YES; 25 | } 26 | 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | /* 30 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 31 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 32 | */ 33 | } 34 | 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application { 37 | /* 38 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 39 | If your application supports background execution, called instead of applicationWillTerminate: when the user quits. 40 | */ 41 | } 42 | 43 | 44 | - (void)applicationWillEnterForeground:(UIApplication *)application { 45 | /* 46 | Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background. 47 | */ 48 | } 49 | 50 | 51 | - (void)applicationDidBecomeActive:(UIApplication *)application { 52 | /* 53 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 54 | */ 55 | } 56 | 57 | 58 | - (void)applicationWillTerminate:(UIApplication *)application { 59 | /* 60 | Called when the application is about to terminate. 61 | See also applicationDidEnterBackground:. 62 | */ 63 | } 64 | 65 | 66 | #pragma mark - 67 | #pragma mark Memory management 68 | 69 | - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { 70 | /* 71 | Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later. 72 | */ 73 | } 74 | 75 | 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Examples/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright LJR Software Limited 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) { 12 | 13 | @autoreleasepool { 14 | int retVal = UIApplicationMain(argc, argv, nil, nil); 15 | return retVal; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LRTableModel-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | MainWindow 29 | 30 | 31 | -------------------------------------------------------------------------------- /LRTableModel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'LRTableModel' 3 | s.version = '1.0' 4 | s.license = 'MIT' 5 | s.summary = 'A JTable-inspired alternative way of modelling your table view data.' 6 | s.homepage = 'https://github.com/lukeredpath/LRTableModel' 7 | s.author = 'Luke Redpath' 8 | s.source = { :git => 'git://github.com/lukeredpath/LRTableModel.git', :tag => 'v1.0' } 9 | s.source_files = 'Classes' 10 | s.clean_paths = %w{Examples LRTableModel.xcworkspace LRTableModel.xcodeproj Reources Support Tests} 11 | end 12 | -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 11 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 12 | 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; }; 13 | A38666961678B52C0099BD04 /* LRCollectionTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A38666941678B00E0099BD04 /* LRCollectionTableModel.m */; }; 14 | A3A03BBC14E17AE000EF322F /* Examples.plist in Resources */ = {isa = PBXBuildFile; fileRef = A3A03BA114E17AE000EF322F /* Examples.plist */; }; 15 | A3A03BBD14E17AE000EF322F /* ExamplesIndexViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BA314E17AE000EF322F /* ExamplesIndexViewController.m */; }; 16 | A3A03BBE14E17AE000EF322F /* GithubRepositories.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BA514E17AE000EF322F /* GithubRepositories.m */; }; 17 | A3A03BBF14E17AE000EF322F /* GroupedTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BA814E17AE000EF322F /* GroupedTableModel.m */; }; 18 | A3A03BC014E17AE000EF322F /* GroupedTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BAA14E17AE000EF322F /* GroupedTableViewController.m */; }; 19 | A3A03BC114E17AE000EF322F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BAB14E17AE000EF322F /* main.m */; }; 20 | A3A03BC214E17AE000EF322F /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A3A03BAC14E17AE000EF322F /* MainWindow.xib */; }; 21 | A3A03BC314E17AE000EF322F /* SearchTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BAF14E17AE000EF322F /* SearchTableModel.m */; }; 22 | A3A03BC414E17AE000EF322F /* SearchTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BB114E17AE000EF322F /* SearchTableViewController.m */; }; 23 | A3A03BC514E17AE000EF322F /* SearchTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A3A03BB214E17AE000EF322F /* SearchTableViewController.xib */; }; 24 | A3A03BC614E17AE000EF322F /* SimpleObject.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BB514E17AE000EF322F /* SimpleObject.m */; }; 25 | A3A03BC714E17AE000EF322F /* SortableTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BB714E17AE000EF322F /* SortableTableModel.m */; }; 26 | A3A03BC814E17AE000EF322F /* SimpleTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BB914E17AE000EF322F /* SimpleTableViewController.m */; }; 27 | A3A03BC914E17AE000EF322F /* TableViewModelAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BBB14E17AE000EF322F /* TableViewModelAppDelegate.m */; }; 28 | A3A03BD614E17B1100EF322F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 29 | A3A03BE014E17B2200EF322F /* LRSelfNotifyingTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BCB14E17AF500EF322F /* LRSelfNotifyingTableModel.m */; }; 30 | A3A03BE114E17B2200EF322F /* LRTableModelEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BCF14E17AF500EF322F /* LRTableModelEvent.m */; }; 31 | A3A03BE214E17B2900EF322F /* LRSelfNotifyingTableModel.h in Headers */ = {isa = PBXBuildFile; fileRef = A3A03BCA14E17AF500EF322F /* LRSelfNotifyingTableModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | A3A03BE414E17B2900EF322F /* LRTableModelEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = A3A03BCE14E17AF500EF322F /* LRTableModelEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | A3A03BE514E17B2900EF322F /* LRTableModelEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = A3A03BD014E17AF500EF322F /* LRTableModelEventListener.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34 | A3A03BE614E17B3E00EF322F /* libLRTableModel.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A3A03BD514E17B1100EF322F /* libLRTableModel.a */; }; 35 | A3A03BE714E17B4800EF322F /* libLRTableModel.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A3A03BD514E17B1100EF322F /* libLRTableModel.a */; }; 36 | A3A03BE914E17B4800EF322F /* libPods-specs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A3A03BE814E17B4800EF322F /* libPods-specs.a */; }; 37 | A3A03BFF14E181E100EF322F /* SortableTableModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3A03BB714E17AE000EF322F /* SortableTableModel.m */; }; 38 | A3D492831210AB0C0076940C /* TestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D492821210AB0C0076940C /* TestHelper.m */; }; 39 | A3D494E91210BE2E0076940C /* SimpleTableViewModelTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D494E41210BE130076940C /* SimpleTableViewModelTest.m */; }; 40 | A3D4959E1210C6190076940C /* HCPassesBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D4959D1210C6190076940C /* HCPassesBlock.m */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 45 | 1D6058910D05DD3D006BFB54 /* LRTableModel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LRTableModel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 47 | 288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 48 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 32CA4F630368D1EE00C91783 /* LRTableModel_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LRTableModel_Prefix.pch; sourceTree = ""; }; 50 | 662FE6D91D8E41DBB58A189B /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 8D1107310486CEB800E47090 /* LRTableModel-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "LRTableModel-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; 52 | A38666931678B00E0099BD04 /* LRCollectionTableModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LRCollectionTableModel.h; sourceTree = ""; }; 53 | A38666941678B00E0099BD04 /* LRCollectionTableModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LRCollectionTableModel.m; sourceTree = ""; }; 54 | A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Pods-specs.xcconfig"; path = "Pods/Pods-specs.xcconfig"; sourceTree = ""; }; 55 | A3A03BA114E17AE000EF322F /* Examples.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Examples.plist; sourceTree = ""; }; 56 | A3A03BA214E17AE000EF322F /* ExamplesIndexViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExamplesIndexViewController.h; sourceTree = ""; }; 57 | A3A03BA314E17AE000EF322F /* ExamplesIndexViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExamplesIndexViewController.m; sourceTree = ""; }; 58 | A3A03BA414E17AE000EF322F /* GithubRepositories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GithubRepositories.h; sourceTree = ""; }; 59 | A3A03BA514E17AE000EF322F /* GithubRepositories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GithubRepositories.m; sourceTree = ""; }; 60 | A3A03BA714E17AE000EF322F /* GroupedTableModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupedTableModel.h; sourceTree = ""; }; 61 | A3A03BA814E17AE000EF322F /* GroupedTableModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GroupedTableModel.m; sourceTree = ""; }; 62 | A3A03BA914E17AE000EF322F /* GroupedTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupedTableViewController.h; sourceTree = ""; }; 63 | A3A03BAA14E17AE000EF322F /* GroupedTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GroupedTableViewController.m; sourceTree = ""; }; 64 | A3A03BAB14E17AE000EF322F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | A3A03BAC14E17AE000EF322F /* MainWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 66 | A3A03BAE14E17AE000EF322F /* SearchTableModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchTableModel.h; sourceTree = ""; }; 67 | A3A03BAF14E17AE000EF322F /* SearchTableModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchTableModel.m; sourceTree = ""; }; 68 | A3A03BB014E17AE000EF322F /* SearchTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchTableViewController.h; sourceTree = ""; }; 69 | A3A03BB114E17AE000EF322F /* SearchTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchTableViewController.m; sourceTree = ""; }; 70 | A3A03BB214E17AE000EF322F /* SearchTableViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SearchTableViewController.xib; sourceTree = ""; }; 71 | A3A03BB414E17AE000EF322F /* SimpleObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleObject.h; sourceTree = ""; }; 72 | A3A03BB514E17AE000EF322F /* SimpleObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleObject.m; sourceTree = ""; }; 73 | A3A03BB614E17AE000EF322F /* SortableTableModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SortableTableModel.h; sourceTree = ""; }; 74 | A3A03BB714E17AE000EF322F /* SortableTableModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SortableTableModel.m; sourceTree = ""; }; 75 | A3A03BB814E17AE000EF322F /* SimpleTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleTableViewController.h; sourceTree = ""; }; 76 | A3A03BB914E17AE000EF322F /* SimpleTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleTableViewController.m; sourceTree = ""; }; 77 | A3A03BBA14E17AE000EF322F /* TableViewModelAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewModelAppDelegate.h; sourceTree = ""; }; 78 | A3A03BBB14E17AE000EF322F /* TableViewModelAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewModelAppDelegate.m; sourceTree = ""; }; 79 | A3A03BCA14E17AF500EF322F /* LRSelfNotifyingTableModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LRSelfNotifyingTableModel.h; sourceTree = ""; }; 80 | A3A03BCB14E17AF500EF322F /* LRSelfNotifyingTableModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LRSelfNotifyingTableModel.m; sourceTree = ""; }; 81 | A3A03BCC14E17AF500EF322F /* LRTableModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LRTableModel.h; sourceTree = ""; }; 82 | A3A03BCE14E17AF500EF322F /* LRTableModelEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LRTableModelEvent.h; sourceTree = ""; }; 83 | A3A03BCF14E17AF500EF322F /* LRTableModelEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LRTableModelEvent.m; sourceTree = ""; }; 84 | A3A03BD014E17AF500EF322F /* LRTableModelEventListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LRTableModelEventListener.h; sourceTree = ""; }; 85 | A3A03BD514E17B1100EF322F /* libLRTableModel.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLRTableModel.a; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | A3A03BE814E17B4800EF322F /* libPods-specs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-specs.a"; path = "Pods/build/Release-iphoneos/libPods-specs.a"; sourceTree = ""; }; 87 | A3D492691210AA210076940C /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | A3D4926A1210AA210076940C /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 89 | A3D492791210AA7E0076940C /* TestHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestHelper.h; sourceTree = ""; }; 90 | A3D492821210AB0C0076940C /* TestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestHelper.m; sourceTree = ""; }; 91 | A3D494E41210BE130076940C /* SimpleTableViewModelTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleTableViewModelTest.m; sourceTree = ""; }; 92 | A3D4959C1210C6190076940C /* HCPassesBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HCPassesBlock.h; sourceTree = ""; }; 93 | A3D4959D1210C6190076940C /* HCPassesBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HCPassesBlock.m; sourceTree = ""; }; 94 | /* End PBXFileReference section */ 95 | 96 | /* Begin PBXFrameworksBuildPhase section */ 97 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | A3A03BE614E17B3E00EF322F /* libLRTableModel.a in Frameworks */, 102 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 103 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 104 | 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */, 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | A3A03BD214E17B1100EF322F /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | A3A03BD614E17B1100EF322F /* Foundation.framework in Frameworks */, 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | A3D492661210AA210076940C /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | A3A03BE914E17B4800EF322F /* libPods-specs.a in Frameworks */, 121 | A3A03BE714E17B4800EF322F /* libLRTableModel.a in Frameworks */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXFrameworksBuildPhase section */ 126 | 127 | /* Begin PBXGroup section */ 128 | 080E96DDFE201D6D7F000001 /* Classes */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | A38666931678B00E0099BD04 /* LRCollectionTableModel.h */, 132 | A38666941678B00E0099BD04 /* LRCollectionTableModel.m */, 133 | A3A03BCA14E17AF500EF322F /* LRSelfNotifyingTableModel.h */, 134 | A3A03BCB14E17AF500EF322F /* LRSelfNotifyingTableModel.m */, 135 | A3A03BCC14E17AF500EF322F /* LRTableModel.h */, 136 | A3A03BCE14E17AF500EF322F /* LRTableModelEvent.h */, 137 | A3A03BCF14E17AF500EF322F /* LRTableModelEvent.m */, 138 | A3A03BD014E17AF500EF322F /* LRTableModelEventListener.h */, 139 | ); 140 | path = Classes; 141 | sourceTree = ""; 142 | }; 143 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 1D6058910D05DD3D006BFB54 /* LRTableModel.app */, 147 | A3D492691210AA210076940C /* Tests.octest */, 148 | A3A03BD514E17B1100EF322F /* libLRTableModel.a */, 149 | ); 150 | name = Products; 151 | sourceTree = ""; 152 | }; 153 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | A3A03BA014E17AE000EF322F /* Examples */, 157 | A3D492721210AA590076940C /* Tests */, 158 | 080E96DDFE201D6D7F000001 /* Classes */, 159 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 160 | 29B97317FDCFA39411CA2CEA /* Resources */, 161 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 162 | 19C28FACFE9D520D11CA2CBB /* Products */, 163 | A3A03BE814E17B4800EF322F /* libPods-specs.a */, 164 | A3D4926A1210AA210076940C /* Tests-Info.plist */, 165 | A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */, 166 | ); 167 | name = CustomTemplate; 168 | sourceTree = ""; 169 | }; 170 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 32CA4F630368D1EE00C91783 /* LRTableModel_Prefix.pch */, 174 | 29B97316FDCFA39411CA2CEA /* main.m */, 175 | ); 176 | name = "Other Sources"; 177 | sourceTree = ""; 178 | }; 179 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 8D1107310486CEB800E47090 /* LRTableModel-Info.plist */, 183 | ); 184 | name = Resources; 185 | sourceTree = ""; 186 | }; 187 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 191 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 192 | 288765FC0DF74451002DB57D /* CoreGraphics.framework */, 193 | 662FE6D91D8E41DBB58A189B /* libPods.a */, 194 | ); 195 | name = Frameworks; 196 | sourceTree = ""; 197 | }; 198 | A3A03BA014E17AE000EF322F /* Examples */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | A3A03BA114E17AE000EF322F /* Examples.plist */, 202 | A3A03BA214E17AE000EF322F /* ExamplesIndexViewController.h */, 203 | A3A03BA314E17AE000EF322F /* ExamplesIndexViewController.m */, 204 | A3A03BA414E17AE000EF322F /* GithubRepositories.h */, 205 | A3A03BA514E17AE000EF322F /* GithubRepositories.m */, 206 | A3A03BA614E17AE000EF322F /* GroupedTableModel */, 207 | A3A03BAB14E17AE000EF322F /* main.m */, 208 | A3A03BAC14E17AE000EF322F /* MainWindow.xib */, 209 | A3A03BAD14E17AE000EF322F /* SearchTableModel */, 210 | A3A03BB314E17AE000EF322F /* SortableTableModel */, 211 | A3A03BBA14E17AE000EF322F /* TableViewModelAppDelegate.h */, 212 | A3A03BBB14E17AE000EF322F /* TableViewModelAppDelegate.m */, 213 | ); 214 | path = Examples; 215 | sourceTree = ""; 216 | }; 217 | A3A03BA614E17AE000EF322F /* GroupedTableModel */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | A3A03BA714E17AE000EF322F /* GroupedTableModel.h */, 221 | A3A03BA814E17AE000EF322F /* GroupedTableModel.m */, 222 | A3A03BA914E17AE000EF322F /* GroupedTableViewController.h */, 223 | A3A03BAA14E17AE000EF322F /* GroupedTableViewController.m */, 224 | ); 225 | path = GroupedTableModel; 226 | sourceTree = ""; 227 | }; 228 | A3A03BAD14E17AE000EF322F /* SearchTableModel */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | A3A03BAE14E17AE000EF322F /* SearchTableModel.h */, 232 | A3A03BAF14E17AE000EF322F /* SearchTableModel.m */, 233 | A3A03BB014E17AE000EF322F /* SearchTableViewController.h */, 234 | A3A03BB114E17AE000EF322F /* SearchTableViewController.m */, 235 | A3A03BB214E17AE000EF322F /* SearchTableViewController.xib */, 236 | ); 237 | path = SearchTableModel; 238 | sourceTree = ""; 239 | }; 240 | A3A03BB314E17AE000EF322F /* SortableTableModel */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | A3A03BB414E17AE000EF322F /* SimpleObject.h */, 244 | A3A03BB514E17AE000EF322F /* SimpleObject.m */, 245 | A3A03BB614E17AE000EF322F /* SortableTableModel.h */, 246 | A3A03BB714E17AE000EF322F /* SortableTableModel.m */, 247 | A3A03BB814E17AE000EF322F /* SimpleTableViewController.h */, 248 | A3A03BB914E17AE000EF322F /* SimpleTableViewController.m */, 249 | ); 250 | name = SortableTableModel; 251 | path = SimpleTableModel; 252 | sourceTree = ""; 253 | }; 254 | A3D492721210AA590076940C /* Tests */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | A3D492751210AA590076940C /* Support */, 258 | A3D492761210AA590076940C /* Unit */, 259 | ); 260 | path = Tests; 261 | sourceTree = ""; 262 | }; 263 | A3D492751210AA590076940C /* Support */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | A3D4959C1210C6190076940C /* HCPassesBlock.h */, 267 | A3D4959D1210C6190076940C /* HCPassesBlock.m */, 268 | A3D492791210AA7E0076940C /* TestHelper.h */, 269 | A3D492821210AB0C0076940C /* TestHelper.m */, 270 | ); 271 | path = Support; 272 | sourceTree = ""; 273 | }; 274 | A3D492761210AA590076940C /* Unit */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | A3D494E41210BE130076940C /* SimpleTableViewModelTest.m */, 278 | ); 279 | path = Unit; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXGroup section */ 283 | 284 | /* Begin PBXHeadersBuildPhase section */ 285 | A3A03BD314E17B1100EF322F /* Headers */ = { 286 | isa = PBXHeadersBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | A3A03BE214E17B2900EF322F /* LRSelfNotifyingTableModel.h in Headers */, 290 | A3A03BE414E17B2900EF322F /* LRTableModelEvent.h in Headers */, 291 | A3A03BE514E17B2900EF322F /* LRTableModelEventListener.h in Headers */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXHeadersBuildPhase section */ 296 | 297 | /* Begin PBXNativeTarget section */ 298 | 1D6058900D05DD3D006BFB54 /* Example App */ = { 299 | isa = PBXNativeTarget; 300 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Example App" */; 301 | buildPhases = ( 302 | 1D60588D0D05DD3D006BFB54 /* Resources */, 303 | 1D60588E0D05DD3D006BFB54 /* Sources */, 304 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 305 | ); 306 | buildRules = ( 307 | ); 308 | dependencies = ( 309 | ); 310 | name = "Example App"; 311 | productName = TableViewModel; 312 | productReference = 1D6058910D05DD3D006BFB54 /* LRTableModel.app */; 313 | productType = "com.apple.product-type.application"; 314 | }; 315 | A3A03BD414E17B1100EF322F /* LRTableModel */ = { 316 | isa = PBXNativeTarget; 317 | buildConfigurationList = A3A03BDD14E17B1100EF322F /* Build configuration list for PBXNativeTarget "LRTableModel" */; 318 | buildPhases = ( 319 | A3A03BD114E17B1100EF322F /* Sources */, 320 | A3A03BD214E17B1100EF322F /* Frameworks */, 321 | A3A03BD314E17B1100EF322F /* Headers */, 322 | ); 323 | buildRules = ( 324 | ); 325 | dependencies = ( 326 | ); 327 | name = LRTableModel; 328 | productName = LRTableModel; 329 | productReference = A3A03BD514E17B1100EF322F /* libLRTableModel.a */; 330 | productType = "com.apple.product-type.library.static"; 331 | }; 332 | A3D492681210AA210076940C /* Tests */ = { 333 | isa = PBXNativeTarget; 334 | buildConfigurationList = A3D4926D1210AA210076940C /* Build configuration list for PBXNativeTarget "Tests" */; 335 | buildPhases = ( 336 | A3D492641210AA210076940C /* Resources */, 337 | A3D492651210AA210076940C /* Sources */, 338 | A3D492661210AA210076940C /* Frameworks */, 339 | A3D492671210AA210076940C /* ShellScript */, 340 | ); 341 | buildRules = ( 342 | ); 343 | dependencies = ( 344 | ); 345 | name = Tests; 346 | productName = Tests; 347 | productReference = A3D492691210AA210076940C /* Tests.octest */; 348 | productType = "com.apple.product-type.bundle"; 349 | }; 350 | /* End PBXNativeTarget section */ 351 | 352 | /* Begin PBXProject section */ 353 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 354 | isa = PBXProject; 355 | attributes = { 356 | LastUpgradeCheck = 0420; 357 | }; 358 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LRTableModel" */; 359 | compatibilityVersion = "Xcode 3.2"; 360 | developmentRegion = English; 361 | hasScannedForEncodings = 1; 362 | knownRegions = ( 363 | en, 364 | ); 365 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 366 | projectDirPath = ""; 367 | projectRoot = ""; 368 | targets = ( 369 | 1D6058900D05DD3D006BFB54 /* Example App */, 370 | A3A03BD414E17B1100EF322F /* LRTableModel */, 371 | A3D492681210AA210076940C /* Tests */, 372 | ); 373 | }; 374 | /* End PBXProject section */ 375 | 376 | /* Begin PBXResourcesBuildPhase section */ 377 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 378 | isa = PBXResourcesBuildPhase; 379 | buildActionMask = 2147483647; 380 | files = ( 381 | A3A03BBC14E17AE000EF322F /* Examples.plist in Resources */, 382 | A3A03BC214E17AE000EF322F /* MainWindow.xib in Resources */, 383 | A3A03BC514E17AE000EF322F /* SearchTableViewController.xib in Resources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | A3D492641210AA210076940C /* Resources */ = { 388 | isa = PBXResourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | /* End PBXResourcesBuildPhase section */ 395 | 396 | /* Begin PBXShellScriptBuildPhase section */ 397 | A3D492671210AA210076940C /* ShellScript */ = { 398 | isa = PBXShellScriptBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | ); 402 | inputPaths = ( 403 | ); 404 | outputPaths = ( 405 | ); 406 | runOnlyForDeploymentPostprocessing = 0; 407 | shellPath = /bin/sh; 408 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 409 | }; 410 | /* End PBXShellScriptBuildPhase section */ 411 | 412 | /* Begin PBXSourcesBuildPhase section */ 413 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 414 | isa = PBXSourcesBuildPhase; 415 | buildActionMask = 2147483647; 416 | files = ( 417 | A3A03BBD14E17AE000EF322F /* ExamplesIndexViewController.m in Sources */, 418 | A3A03BBE14E17AE000EF322F /* GithubRepositories.m in Sources */, 419 | A3A03BBF14E17AE000EF322F /* GroupedTableModel.m in Sources */, 420 | A3A03BC014E17AE000EF322F /* GroupedTableViewController.m in Sources */, 421 | A3A03BC114E17AE000EF322F /* main.m in Sources */, 422 | A3A03BC314E17AE000EF322F /* SearchTableModel.m in Sources */, 423 | A3A03BC414E17AE000EF322F /* SearchTableViewController.m in Sources */, 424 | A3A03BC614E17AE000EF322F /* SimpleObject.m in Sources */, 425 | A3A03BC714E17AE000EF322F /* SortableTableModel.m in Sources */, 426 | A3A03BC814E17AE000EF322F /* SimpleTableViewController.m in Sources */, 427 | A3A03BC914E17AE000EF322F /* TableViewModelAppDelegate.m in Sources */, 428 | ); 429 | runOnlyForDeploymentPostprocessing = 0; 430 | }; 431 | A3A03BD114E17B1100EF322F /* Sources */ = { 432 | isa = PBXSourcesBuildPhase; 433 | buildActionMask = 2147483647; 434 | files = ( 435 | A3A03BE014E17B2200EF322F /* LRSelfNotifyingTableModel.m in Sources */, 436 | A3A03BE114E17B2200EF322F /* LRTableModelEvent.m in Sources */, 437 | A38666961678B52C0099BD04 /* LRCollectionTableModel.m in Sources */, 438 | ); 439 | runOnlyForDeploymentPostprocessing = 0; 440 | }; 441 | A3D492651210AA210076940C /* Sources */ = { 442 | isa = PBXSourcesBuildPhase; 443 | buildActionMask = 2147483647; 444 | files = ( 445 | A3D492831210AB0C0076940C /* TestHelper.m in Sources */, 446 | A3D494E91210BE2E0076940C /* SimpleTableViewModelTest.m in Sources */, 447 | A3D4959E1210C6190076940C /* HCPassesBlock.m in Sources */, 448 | A3A03BFF14E181E100EF322F /* SortableTableModel.m in Sources */, 449 | ); 450 | runOnlyForDeploymentPostprocessing = 0; 451 | }; 452 | /* End PBXSourcesBuildPhase section */ 453 | 454 | /* Begin XCBuildConfiguration section */ 455 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ENABLE_OBJC_ARC = YES; 460 | COPY_PHASE_STRIP = NO; 461 | DSTROOT = /tmp/xcodeproj.dst; 462 | GCC_DYNAMIC_NO_PIC = NO; 463 | GCC_OPTIMIZATION_LEVEL = 0; 464 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 465 | GCC_PREFIX_HEADER = LRTableModel_Prefix.pch; 466 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 467 | INFOPLIST_FILE = "LRTableModel-Info.plist"; 468 | PRODUCT_NAME = LRTableModel; 469 | SKIP_INSTALL = YES; 470 | }; 471 | name = Debug; 472 | }; 473 | 1D6058950D05DD3E006BFB54 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ALWAYS_SEARCH_USER_PATHS = NO; 477 | CLANG_ENABLE_OBJC_ARC = YES; 478 | COPY_PHASE_STRIP = YES; 479 | DSTROOT = /tmp/xcodeproj.dst; 480 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 481 | GCC_PREFIX_HEADER = LRTableModel_Prefix.pch; 482 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 483 | INFOPLIST_FILE = "LRTableModel-Info.plist"; 484 | PRODUCT_NAME = LRTableModel; 485 | SKIP_INSTALL = YES; 486 | VALIDATE_PRODUCT = YES; 487 | }; 488 | name = Release; 489 | }; 490 | A3A03BDE14E17B1100EF322F /* Debug */ = { 491 | isa = XCBuildConfiguration; 492 | baseConfigurationReference = A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */; 493 | buildSettings = { 494 | ALWAYS_SEARCH_USER_PATHS = NO; 495 | CLANG_ENABLE_OBJC_ARC = YES; 496 | COPY_PHASE_STRIP = NO; 497 | DSTROOT = /tmp/LRTableModel.dst; 498 | GCC_C_LANGUAGE_STANDARD = gnu99; 499 | GCC_DYNAMIC_NO_PIC = NO; 500 | GCC_OPTIMIZATION_LEVEL = 0; 501 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 502 | GCC_PREFIX_HEADER = LRTableModel_Prefix.pch; 503 | GCC_PREPROCESSOR_DEFINITIONS = ( 504 | "DEBUG=1", 505 | "$(inherited)", 506 | ); 507 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 508 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 509 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 510 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 511 | OTHER_LDFLAGS = "-ObjC"; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SKIP_INSTALL = YES; 514 | }; 515 | name = Debug; 516 | }; 517 | A3A03BDF14E17B1100EF322F /* Release */ = { 518 | isa = XCBuildConfiguration; 519 | baseConfigurationReference = A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */; 520 | buildSettings = { 521 | ALWAYS_SEARCH_USER_PATHS = NO; 522 | CLANG_ENABLE_OBJC_ARC = YES; 523 | COPY_PHASE_STRIP = YES; 524 | DSTROOT = /tmp/LRTableModel.dst; 525 | GCC_C_LANGUAGE_STANDARD = gnu99; 526 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 527 | GCC_PREFIX_HEADER = LRTableModel_Prefix.pch; 528 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 529 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 530 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 531 | OTHER_LDFLAGS = "-ObjC"; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | SKIP_INSTALL = YES; 534 | VALIDATE_PRODUCT = YES; 535 | }; 536 | name = Release; 537 | }; 538 | A3D4926B1210AA210076940C /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | baseConfigurationReference = A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */; 541 | buildSettings = { 542 | ALWAYS_SEARCH_USER_PATHS = NO; 543 | CLANG_ENABLE_OBJC_ARC = YES; 544 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 545 | COPY_PHASE_STRIP = NO; 546 | DSTROOT = /tmp/xcodeproj.dst; 547 | FRAMEWORK_SEARCH_PATHS = ( 548 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 549 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 550 | ); 551 | GCC_DYNAMIC_NO_PIC = NO; 552 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 553 | GCC_OPTIMIZATION_LEVEL = 0; 554 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 555 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 556 | INFOPLIST_FILE = "Tests-Info.plist"; 557 | OTHER_LDFLAGS = ( 558 | "-framework", 559 | Foundation, 560 | "-framework", 561 | SenTestingKit, 562 | "-framework", 563 | UIKit, 564 | "-all_load", 565 | "-lstdc++", 566 | ); 567 | PRODUCT_NAME = Tests; 568 | SKIP_INSTALL = YES; 569 | USER_HEADER_SEARCH_PATHS = "Support/**"; 570 | WRAPPER_EXTENSION = octest; 571 | }; 572 | name = Debug; 573 | }; 574 | A3D4926C1210AA210076940C /* Release */ = { 575 | isa = XCBuildConfiguration; 576 | baseConfigurationReference = A3A03B9D14E17A0F00EF322F /* Pods-specs.xcconfig */; 577 | buildSettings = { 578 | ALWAYS_SEARCH_USER_PATHS = NO; 579 | CLANG_ENABLE_OBJC_ARC = YES; 580 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 581 | COPY_PHASE_STRIP = YES; 582 | DSTROOT = /tmp/xcodeproj.dst; 583 | FRAMEWORK_SEARCH_PATHS = ( 584 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 585 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 586 | ); 587 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 588 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 589 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 590 | INFOPLIST_FILE = "Tests-Info.plist"; 591 | OTHER_LDFLAGS = ( 592 | "-framework", 593 | Foundation, 594 | "-framework", 595 | SenTestingKit, 596 | "-framework", 597 | UIKit, 598 | "-all_load", 599 | "-lstdc++", 600 | ); 601 | PRODUCT_NAME = Tests; 602 | SKIP_INSTALL = YES; 603 | USER_HEADER_SEARCH_PATHS = "Support/**"; 604 | WRAPPER_EXTENSION = octest; 605 | ZERO_LINK = NO; 606 | }; 607 | name = Release; 608 | }; 609 | C01FCF4F08A954540054247B /* Debug */ = { 610 | isa = XCBuildConfiguration; 611 | buildSettings = { 612 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 613 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 614 | GCC_C_LANGUAGE_STANDARD = c99; 615 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 616 | GCC_WARN_UNUSED_VARIABLE = YES; 617 | HEADER_SEARCH_PATHS = "$(inherited)"; 618 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 619 | SDKROOT = iphoneos; 620 | }; 621 | name = Debug; 622 | }; 623 | C01FCF5008A954540054247B /* Release */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 627 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 628 | GCC_C_LANGUAGE_STANDARD = c99; 629 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 630 | GCC_WARN_UNUSED_VARIABLE = YES; 631 | HEADER_SEARCH_PATHS = "$(inherited)"; 632 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 633 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 634 | SDKROOT = iphoneos; 635 | }; 636 | name = Release; 637 | }; 638 | /* End XCBuildConfiguration section */ 639 | 640 | /* Begin XCConfigurationList section */ 641 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Example App" */ = { 642 | isa = XCConfigurationList; 643 | buildConfigurations = ( 644 | 1D6058940D05DD3E006BFB54 /* Debug */, 645 | 1D6058950D05DD3E006BFB54 /* Release */, 646 | ); 647 | defaultConfigurationIsVisible = 0; 648 | defaultConfigurationName = Release; 649 | }; 650 | A3A03BDD14E17B1100EF322F /* Build configuration list for PBXNativeTarget "LRTableModel" */ = { 651 | isa = XCConfigurationList; 652 | buildConfigurations = ( 653 | A3A03BDE14E17B1100EF322F /* Debug */, 654 | A3A03BDF14E17B1100EF322F /* Release */, 655 | ); 656 | defaultConfigurationIsVisible = 0; 657 | defaultConfigurationName = Release; 658 | }; 659 | A3D4926D1210AA210076940C /* Build configuration list for PBXNativeTarget "Tests" */ = { 660 | isa = XCConfigurationList; 661 | buildConfigurations = ( 662 | A3D4926B1210AA210076940C /* Debug */, 663 | A3D4926C1210AA210076940C /* Release */, 664 | ); 665 | defaultConfigurationIsVisible = 0; 666 | defaultConfigurationName = Release; 667 | }; 668 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LRTableModel" */ = { 669 | isa = XCConfigurationList; 670 | buildConfigurations = ( 671 | C01FCF4F08A954540054247B /* Debug */, 672 | C01FCF5008A954540054247B /* Release */, 673 | ); 674 | defaultConfigurationIsVisible = 0; 675 | defaultConfigurationName = Release; 676 | }; 677 | /* End XCConfigurationList section */ 678 | }; 679 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 680 | } 681 | -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/project.xcworkspace/xcuserdata/luke.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeredpath/LRTableModel/c3209214ec2250c4f78da6212fa313d77b1bcd0e/LRTableModel.xcodeproj/project.xcworkspace/xcuserdata/luke.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/xcshareddata/xcschemes/Example App.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/xcshareddata/xcschemes/LRTableModel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 32 | 38 | 39 | 40 | 41 | 42 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /LRTableModel.xcodeproj/xcuserdata/luke.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example App.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | LRTableModel.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 1D6058900D05DD3D006BFB54 21 | 22 | primary 23 | 24 | 25 | A3A03BD414E17B1100EF322F 26 | 27 | primary 28 | 29 | 30 | A3D492681210AA210076940C 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LRTableModel.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LRTableModel_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'TableViewModel' target in the 'TableViewModel' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Luke Redpath 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios 2 | 3 | target :specs, :exclusive => true do 4 | dependency 'Kiwi', git: "git://github.com/allending/Kiwi.git" 5 | dependency 'LRMocky', git: "git://github.com/lukeredpath/LRMocky.git" 6 | dependency 'OCHamcrest' 7 | end 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kiwi (1.0.1) 3 | - LRMocky (0.9.1) 4 | - OCHamcrest (1.6) 5 | 6 | DEPENDENCIES: 7 | - Kiwi (from `git://github.com/allending/Kiwi.git') 8 | - LRMocky (from `git://github.com/lukeredpath/LRMocky.git') 9 | - OCHamcrest 10 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # LRTableModel, because implementing UITableViewDataSource sucks! 2 | 3 | UITableViews are the ubiquitous building blocks of many iPhone and iPad applications. They are easy to add to your application - throw them in a XIB file, hook up your delegate and datasource outlets, and you're set...well, almost. You will be once you've gone through the tedious process of implementing those data source methods. 4 | 5 | How many times have you written out something like this? 6 | 7 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 8 | { 9 | static NSString *identifier = @"Cell"; 10 | 11 | // yawn... 12 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 13 | if (cell == nil) { 14 | // don't forget that reuse identifier! 15 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease]; 16 | } 17 | 18 | // at least extracting this keeps the method tidy... 19 | [self configureCell:cell atIndexPath:indexPath]; 20 | 21 | return cell; 22 | } 23 | 24 | OK, you may not have written it; it may have been generated for you (with some convenient placeholders for you to fill in) but the reality is that as your application develops, this method will grow and then you have to duplicate it for every table view controller in your application. 25 | 26 | And then you have to decide where your table view should get it's data from. Should you store it in an NSArray instance variable in your view controller? Or an NSDictionary? How about pulling it straight out of a collection on one of your domain objects? There has to be a better way. 27 | 28 | ### A note on building the project 29 | 30 | If you want to check the project out and build it, you'll need to make sure you initialise the project's git submodules first; it has two, Kiwi and Mocky. 31 | 32 | * Kiwi: http://www.kiwi-lib.info/ 33 | * Mocky: http://github.com/lukeredpath/LRMocky 34 | 35 | ## A missing abstraction? 36 | 37 | Having implemented far too many UITableViewControllers in the same repetitive fashion, I started to wonder if there was a missing abstraction that should sit between your domain model and the UITableView's data source. Something that responds to events in your domain's language (e.g. receive a new tweet, delete a contact), which may or may not be the result of user input (perhaps the Tweet arrived from the Twitter streaming API), and publishes events in response to these that speak the language of UITableView: row has been inserted, rows have been deleted etc. 38 | 39 | ## Implementing the concept of a table model 40 | 41 | All of the work here is based on the TableModel interface that powers the Java Swing JTable API, but don't let the words Java or Swing put you off. The JTable component isn't quite the same as UITableView. It's a more traditional table component with rows representing objects and columns representing properties, with cells displaying a single value but the more I looked at it the more I felt it could be made to work with UITableView. 42 | 43 | This project is an exploration of that concept with the hope that it might evolve into something generally useful. 44 | 45 | ## Further benefits: easier unit testing 46 | 47 | By implementing most of your table update logic in the TableModel, with the controller simply responding to these changes by reloading the table view one way or another, it becomes possible to unit test your table model code independently of the UITableView. The SimpleTableViewModelTest test shows how easy this is, using a mock event listener. 48 | 49 | ## A brief overview of the different components 50 | 51 | The core of LRTableModel is a series of protocols which your application needs to implement. They are: 52 | 53 | ### LRTableModel (inherits from UITableViewDataSource) 54 | 55 | The core protocol, the missing link between UITableView and your domain. The intention is that your implementation of the LRTableModel protocol will also act as the data source for your table view. Implementations can be generic (see the SimpleTableModel example, which is based on a generic array of objects) -- in fact, it may be possible to provide an abstract implementation of the protocol that users can sub-class in the majority of cases -- although I would recommend implementing a table model that speaks that language of your domain and indeed, forms part of your domain model. 56 | 57 | ### LRTableModelCellProvider 58 | 59 | The general pattern for providing table view cells is the same; dequeue a reusable cell, create a new one if one is not available, configure it with properties from your domain object then return it. An abstract implementation of LRTableModel should be able to encapsulate this pattern by delegating to an instance of LRTableModelProvider to fill in the blanks - creating new cells, specifying a reuse identifier and configuring a cell's properties. 60 | 61 | ### LRTableModelEventListener 62 | 63 | This will generally be implemented by the view controller that manages the UITableView and will be used to respond to events triggered by the table model; these will be inserts, updates, deletions, refreshes (where the whole structure or data set has changed). The simplest thing the event listener could do is simply reload the UITableView but it may choose to handle different types of events with more granular updates, like animated insertions/deletions. 64 | 65 | ## Examples 66 | 67 | The project contains the following examples: 68 | 69 | ### SimpleTableModel 70 | 71 | A generic implementation based around an array of domain objects; this implementation is provided with unit tests and may provide the basis for some kind of abstract table model class. 72 | 73 | ## License 74 | 75 | All code is copyright Luke Redpath 2010 and is provided under the terms of the MIT license. 76 | 77 | 78 | -------------------------------------------------------------------------------- /Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /Tests/Support/HCPassesBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // HCPassesBlock.h 3 | // LRResty 4 | // 5 | // Created by Luke Redpath on 03/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HCBaseMatcher.h" 11 | 12 | typedef BOOL (^HCPassesBlockBlock)(id object); 13 | 14 | @interface HCPassesBlock : HCBaseMatcher { 15 | HCPassesBlockBlock block; 16 | NSString *message; 17 | } 18 | + (id)passesBlock:(HCPassesBlockBlock)block withMessage:(NSString *)message; 19 | - (id)initWithBlock:(HCPassesBlockBlock)theBlock expectationMessage:(NSString *)expectationMessage; 20 | @end 21 | 22 | id HC_passesBlock(HCPassesBlockBlock theBlock, NSString *expectationMessage); 23 | 24 | #ifdef HC_SHORTHAND 25 | #define passesBlock(block, message) HC_passesBlock(block, message) 26 | #endif 27 | 28 | -------------------------------------------------------------------------------- /Tests/Support/HCPassesBlock.m: -------------------------------------------------------------------------------- 1 | // 2 | // HCPassesBlock.m 3 | // LRResty 4 | // 5 | // Created by Luke Redpath on 03/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "HCPassesBlock.h" 10 | #import "HCDescription.h" 11 | 12 | @implementation HCPassesBlock 13 | 14 | + (id)passesBlock:(HCPassesBlockBlock)block withMessage:(NSString *)message; 15 | { 16 | return [[self alloc] initWithBlock:block expectationMessage:message]; 17 | } 18 | 19 | - (id)initWithBlock:(HCPassesBlockBlock)theBlock expectationMessage:(NSString *)expectationMessage; 20 | { 21 | if (self = [super init]) { 22 | block = theBlock; 23 | message = [expectationMessage copy]; 24 | } 25 | return self; 26 | } 27 | 28 | - (BOOL)matches:(id)item 29 | { 30 | return block(item); 31 | } 32 | 33 | - (void)describeTo:(id )description 34 | { 35 | [description appendText:message]; 36 | } 37 | 38 | @end 39 | 40 | id HC_passesBlock(HCPassesBlockBlock theBlock, NSString *expectationMessage) 41 | { 42 | return [HCPassesBlock passesBlock:theBlock withMessage:expectationMessage]; 43 | } 44 | -------------------------------------------------------------------------------- /Tests/Support/TestHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestHelper.h 3 | // 4 | // Created by Luke Redpath on 09/08/2010. 5 | // Copyright 2010 LJR Software Limited. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | #import "Kiwi.h" 12 | #define LRMOCKY_KIWI_COMPATIBILITY_MODE 13 | #import "LRMocky.h" 14 | #define HC_SHORTHAND 15 | #import "OCHamcrest.h" 16 | 17 | #import "HCPassesBlock.h" 18 | 19 | #import "LRTableModelEvent.h" 20 | #import "LRTableModelEventListener.h" 21 | 22 | // this is to work around lack of protocol mock support in mocky 23 | @interface LRMockEventListener : NSObject 24 | {} 25 | @end 26 | 27 | id anyEvent(); 28 | id insertEventAtRow(int rowIndex); 29 | id updateEventAtRow(int rowIndex); 30 | id deleteEventAtRow(int rowIndex); 31 | id refreshEvent(); 32 | -------------------------------------------------------------------------------- /Tests/Support/TestHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // AardvarkTest.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "TestHelper.h" 10 | 11 | SPEC_BEGIN(DemoSpec) 12 | 13 | describe(@"A simple test", ^{ 14 | 15 | LRMockery *mockery = [LRMockery mockeryForTestCase:self]; 16 | 17 | afterEach(^{ 18 | [mockery assertSatisfied]; 19 | }); 20 | 21 | it(@"should work", ^{ 22 | [[@"foo" should] equal:@"foo"]; 23 | }); 24 | 25 | it(@"should integrate with Mocky", ^{ 26 | id mockObject = [mockery mock:[NSString class]]; 27 | 28 | [mockery checking:^(LRExpectationBuilder *expects) { 29 | [[expects oneOf:mockObject] uppercaseString]; 30 | }]; 31 | 32 | [mockObject uppercaseString]; 33 | }); 34 | 35 | }); 36 | 37 | SPEC_END 38 | 39 | @implementation LRMockEventListener 40 | - (void)tableModelChanged:(LRTableModelEvent *)changeEvent {} 41 | @end 42 | 43 | id insertEventAtRow(int rowIndex) { 44 | return equalTo([LRTableModelEvent insertionAtRow:rowIndex section:0]); 45 | } 46 | 47 | id anyEvent() { 48 | return instanceOf([LRTableModelEvent class]); 49 | } 50 | 51 | id updateEventAtRow(int rowIndex) { 52 | return equalTo([LRTableModelEvent updatedRow:rowIndex section:0]); 53 | } 54 | 55 | id deleteEventAtRow(int rowIndex) { 56 | return equalTo([LRTableModelEvent deletedRow:rowIndex section:0]); 57 | } 58 | 59 | id refreshEvent() { 60 | return equalTo([LRTableModelEvent refreshedData]); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Tests/Unit/SimpleTableViewModelTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleTableViewModelTest.m 3 | // TableViewModel 4 | // 5 | // Created by Luke Redpath on 09/08/2010. 6 | // Copyright 2010 LJR Software Limited. All rights reserved. 7 | // 8 | 9 | #import "TestHelper.h" 10 | #import "SortableTableModel.h" 11 | #import "LRTableModelEventListener.h" 12 | #import "LRTableModelEvent.h" 13 | 14 | 15 | SPEC_BEGIN(SimpleTableViewModelSpec) 16 | 17 | describe(@"SimpleTableModel", ^{ 18 | 19 | __block SortableTableModel *model = nil; 20 | __block __strong LRMockery *mockery = [LRMockery mockeryForTestCase:self]; 21 | 22 | beforeEach(^{ 23 | model = [[SortableTableModel alloc] initWithCellProvider:nil]; 24 | }); 25 | 26 | context(@"with a single object", ^{ 27 | beforeEach(^{ 28 | [model addObject:@"an example object"]; 29 | }); 30 | 31 | it(@"should have one section", ^{ 32 | assertThatInt([model numberOfSections], equalToInt(1)); 33 | }); 34 | 35 | it(@"should have one row", ^{ 36 | assertThatInt([model numberOfRowsInSection:0], equalToInt(1)); 37 | }); 38 | 39 | it(@"should return the single object for index path {0, 0}", ^{ 40 | assertThat([model objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], 41 | equalTo(@"an example object")); 42 | }); 43 | }); 44 | 45 | context(@"with multiple objects", ^{ 46 | beforeEach(^{ 47 | [model setObjects:[NSArray arrayWithObjects:@"foo", @"bar", @"baz", nil]]; 48 | }); 49 | 50 | it(@"should have a row for each object", ^{ 51 | assertThatInt([model numberOfRowsInSection:0], equalToInt(3)); 52 | }); 53 | }); 54 | 55 | context(@"with an event listener", ^{ 56 | __block id mockListener = [mockery mock:[LRMockEventListener class]]; 57 | 58 | beforeEach(^{ 59 | [model addTableModelListener:mockListener]; 60 | }); 61 | 62 | afterEach(^{ 63 | [mockery assertSatisfied]; 64 | [mockery reset]; 65 | }); 66 | 67 | it(@"notifies the listener of an insertion when the object is added", ^{ 68 | [mockery checking:^(LRExpectationBuilder *expects) { 69 | [[expects oneOf:mockListener] tableModelChanged:insertEventAtRow(0)]; 70 | }]; 71 | 72 | [model addObject:@"an object"]; 73 | }); 74 | 75 | it(@"notifies the listener of an update when an existing object is replaced", ^{ 76 | [mockery checking:^(LRExpectationBuilder *expects) { 77 | [[expects oneOf:mockListener] tableModelChanged:insertEventAtRow(0)]; 78 | [[expects oneOf:mockListener] tableModelChanged:insertEventAtRow(1)]; 79 | }]; 80 | 81 | [model addObject:@"an object"]; 82 | [model addObject:@"another object"]; 83 | 84 | [mockery checking:^(LRExpectationBuilder *expects) { 85 | [[expects oneOf:mockListener] tableModelChanged:updateEventAtRow(1)]; 86 | }]; 87 | 88 | [model replaceObjectAtIndex:1 withObject:@"a different object"]; 89 | }); 90 | 91 | it(@"notifies the listener of a deletion when an existing object is removed", ^{ 92 | [mockery checking:^(LRExpectationBuilder *expects) { 93 | [[expects oneOf:mockListener] tableModelChanged:insertEventAtRow(0)]; 94 | }]; 95 | 96 | [model addObject:@"an object"]; 97 | 98 | [mockery checking:^(LRExpectationBuilder *expects) { 99 | [[expects oneOf:mockListener] tableModelChanged:deleteEventAtRow(0)]; 100 | }]; 101 | 102 | [model removeObject:@"an object"]; 103 | }); 104 | 105 | it(@"notifies the listener of a refresh when the whole data set is replaced", ^{ 106 | [mockery checking:^(LRExpectationBuilder *expects) { 107 | [[expects oneOf:mockListener] tableModelChanged:refreshEvent()]; 108 | }]; 109 | 110 | [model setObjects:[NSArray arrayWithObjects:@"foo", @"bar", @"baz", nil]]; 111 | }); 112 | 113 | it(@"does not notify listener when it has been removed", ^{ 114 | [mockery checking:^(LRExpectationBuilder *expects) { 115 | [[expects never:mockListener] tableModelChanged:insertEventAtRow(0)]; 116 | }]; 117 | 118 | [model removeTableModelListener:mockListener]; 119 | [model addObject:@"an object"]; 120 | }); 121 | }); 122 | 123 | }); 124 | 125 | SPEC_END 126 | --------------------------------------------------------------------------------