IRCollectionTableViewModel — A powerful MVVM Tableview/CollectionView for iOS

Irons163
3 min readMar 9, 2020

--

IRCollectionTableViewModel

  • IRCollectionTableViewModel is a powerful MVVM Tableview/CollectionView for iOS, which is flexible and can easy to handle and reuse.

Features

  • MVVM structure.
  • Flexible, Reusable.

Install

Git

  • Git clone this project.
  • Copy this project into your own project.
  • Add the .xcodeproj into you project and link it as embed framework.

Options

  • You can remove the demo and ScreenShots folder.

Cocoapods

  • Add pod 'IRCollectionTableViewModel' in the Podfile
  • pod install

Introduction MVVM

  • Model — view — viewmodel (MVVM) is a software architectural pattern.
  • It has advantages more than the tranditional MVC architectural. Can improve the whole code strurcture.
  • More detail can see the MVVM wikipedia. MVVM

Usage

Basic

TableView

  • Create a new class TableViewViewModel extends TableViewBasicViewModel, and Import IRCollectionTableViewModel
#import <IRCollectionTableViewModel/IRCollectionTableViewModel.h>@interface TableViewViewModel : TableViewBasicViewModel<UITableViewDataSource>@end
  • You can add your init method and register the cell inside
- (instancetype)initWithTableView:(UITableView*)tableView;...- (instancetype)initWithTableView:(UITableView *)tableView {
if (self = [super init]) {
items = [[NSMutableArray<id<SectionModelItem>> alloc] init];
[tableView registerNib:[UINib nibWithNibName:CELL_NIB_NAME bundle:nil] forCellReuseIdentifier:CELL_IDENTIFIER];
}
return self;
}
  • Add update method
- (void)update;...- (void)update {
[items removeAllObjects];
// Setup items
// [self setupRows];
}
  • For setup items, other words, setup the sections/rows you want to show. Create TableViewSectionItem and TableViewRowItem, DemoSectionType, DemoRowType
typedef NS_ENUM(NSInteger, DemoSectionType){
DemoSection
};
typedef NS_ENUM(NSInteger, DemoRowType){
RowType_DemoRow
};
@interface TableViewRowItem : RowBasicModelItem
@property (readonly) DemoRowType type;
@end
@interface TableViewSectionItem : SectionBasicModelItem
@property (nonatomic) NSString* sectionTitle;
@property (nonatomic) SectionType type;
@end
  • Implementation TableViewSectionItem and TableViewRowItem in the TableViewViewModel.m
@implementation TableViewRowItem
@dynamic type;
@end
@implementation TableViewSectionItem
@end
  • Setup items
- (void)setupRows {
NSMutableArray *rowItems = [NSMutableArray array];
[rowItems addObject:[[TableViewRowItem alloc] initWithType:RowType_DemoRow withTitle:@"Demo Row"]];
[rowItems addObject:[[TableViewRowItem alloc] initWithType:RowType_DemoRow withTitle:@"Demo Row"]];
NSArray *demoRowItems = [NSArray arrayWithArray:rowItems];
TableViewSectionItem *item = [[TableViewSectionItem alloc] initWithRowCount:[demoRowItems count]];
item.type = DemoSection;
item.sectionTitle = @"Demo Section";
item.rows = demoRowItems;
[items addObject:item];
}
  • Override UITableViewDataSource
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return items.count;
}
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [items[section] rowCount];
}
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
id<SectionModelItem> item = [items objectAtIndex:indexPath.section];
TableViewRowItem *row = (TableViewRowItem *)[item.rows objectAtIndex:[indexPath row]];
switch (item.type) {
case DemoSection:
{
switch (row.type) {
case RowType_DemoRow:
{
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:TableViewCell.identifier forIndexPath:indexPath];
cell.titleLabel.text = [NSString stringWithFormat:@"%@%ld", row.title, row.tagRange.location];
cell.editTextField.text = [editedTexts objectAtIndex:indexPath.row];
cell.editTextField.tag = row.tagRange.location;
cell.editTextField.delegate = self;
return cell;
}
}
break;
}
default:
break;
}
return [[UITableViewCell alloc] init];
}
  • Use your view model TableViewViewModel
#import "TableViewViewModel.h"@implementation TableViewController {
TableViewViewModel *viewModel;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:HEADER_VIEW_NIB_NAME bundle:nil] forHeaderFooterViewReuseIdentifier:HEADER_VIEW_IDENTIFIER];
viewModel = [[TableViewViewModel alloc] initWithTableView:_tableView];
_tableView.dataSource = viewModel;
[viewModel update];
}
@end

CollectionView

  • Just the same way of TableViewViewModel. Create a new class CustomCollectionViewModel extends TableViewBasicViewModel<UICollectionViewDataSource>, and Import this CustomCollectionViewModel to view controller
  • You can add your init method and register the cell inside
  • For setup items, other words, setup the sections/rows you want to show. Create CustomCollectionSectionItem and CustomCollectionRowItem, CustomCollectionSectionType
  • Override UICollectionViewDataSource

Advanced settings

Methos of TableViewBasicViewModel

  • TableViewBasicViewModel provides some usage methods
- (NSInteger)getRowTypeWith:(SectionType)type row:(NSInteger)row;
- (NSString *)getSectionTitleinSection:(NSInteger)section;
- (UIImage *)getSectionLeftIconinSection:(NSInteger)section;
- (SectionType)getSectionTypeinSection:(NSInteger)section;
- (void)hideRows:(BOOL)hide inSection:(NSInteger)section;
- (BOOL)hiddenRowsinSection:(NSInteger)section;
- (NSIndexSet *)getIndexSetWithSectionType:(SectionType)sectionType;
- (NSIndexPath *)getIndexPathWithSectionType:(SectionType)sectionType rowType:(RowType)rowType;
- (void)setupRowTag;
- (NSIndexPath *)getIndexPathFromRowTag:(NSInteger)rowTag;

Tag

  • Because the cells have reuse feature, somtimes we need to tag the cell/componenst if want to recognize the specific cell/components, thus IRCollectionTableViewModel provides a tag feature
  • Setup tags by setupRowTag, it save the tag information in the tagRange which is in the RowBasicModelItem
- (void)setupRows {
...
[self setupRowTag];
}
  • Get tag
TableViewRowItem *row = (TableViewRowItem *)[item.rows objectAtIndex:[indexPath row]];
tag = row.tagRange.location;
  • Get indexPath by tag
- (NSIndexPath *)getIndexPathFromRowTag:(NSInteger)rowTag;
  • Sometimes you want to tag the UI components like UITextField, use setTagRangeLength
TableViewRowItem *row = [[TableViewRowItem alloc] initWithType:RowType_DemoRow withTitle:@"Demo Row"];
[row setTagRangeLength:2];
...[self setupRowTag];
  • Then get tags
TableViewRowItem *row = (TableViewRowItem *)[item.rows objectAtIndex:[indexPath row]];
tag1 = row.tagRange.location;
tag2 = row.tagRange.location + 1;
cell.textField1.tag = tag1;
cell.textField2.tag = tag2;
  • The tags are mapping to the same index path
[self getIndexPathFromRowTag:tag1] == [self getIndexPathFromRowTag:tag2]

Now, you can easy to tag anyhing you want.

Get Row Type

  • Hide rows for specific setion by (void)hideRows:(BOOL)hide inSection:(NSInteger)section
- (NSInteger)getRowTypeWith:(SectionType)type row:(NSInteger)row;

Get Section Title

  • Get section title which set in the SectionBasicModelItem
- (NSString *)getSectionTitleinSection:(NSInteger)section;

Get Section Left Icon

  • Get section icon which set in the SectionBasicModelItem
- (UIImage *)getSectionLeftIconinSection:(NSInteger)section;

Get Section Type

  • Get section type by section index
- (SectionType)getSectionTypeinSection:(NSInteger)section;

Hide Rows

  • Hide rows for specific setion by (void)hideRows:(BOOL)hide inSection:(NSInteger)section
  • Check hidden status by (BOOL)hiddenRowsinSection:(NSInteger)section
- (void)hideRows:(BOOL)hide inSection:(NSInteger)section;
- (BOOL)hiddenRowsinSection:(NSInteger)section;

Get Index Set

  • Get index of section by section type
- (NSIndexSet *)getIndexSetWithSectionType:(SectionType)sectionType;

Get IndexPath

  • Get index path by section type and row type
- (NSIndexPath *)getIndexPathWithSectionType:(SectionType)sectionType rowType:(RowType)rowType;

Screenshots

TableView CollectionView

--

--

No responses yet