ViewController.h
1 2 3 |
@interface TestViewController { } |
ViewController.m
The concept of delegate is that we can delegate messages to another object, as long as that object conforms to the delegate.
Once an object conforms to a delegate, it just simply needs to implement the delegate methods and that’s it.
Hence in our example, everything starts at tableView. tableView has 2 delegates:
- UITableViewDelegate
- UITableViewDataSource
Our ViewController class matches up with the ViewController box in the image. Then notice its 2 delegates coming out of it.
One is called “delegate”.
The other is called “dataSource”.
In code, looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@interface TestViewController () @property (weak, nonatomic, readwrite) IBOutlet UITableView *tableView; @property (strong, nonatomic) TestDelegate *tableViewDelegate; @property (strong, nonatomic) TestDataSource *tableViewDataSource; @end @implementation TestViewController ... ... - (void)viewDidLoad { [super viewDidLoad]; self.tableViewDataSource = [[TestDataSource alloc] initWithController:self]; self.tableViewDelegate = [[TestDelegate alloc] initWithController:self]; self.tableView.delegate = self.tableViewDelegate; self.tableView.dataSource = self.tableViewDataSource; } |
Those delegates must point to objects that conforms to them.
Therefore, we then have object TestDelegate that conforms to UITableViewDelegate, which means it implements UITableViewDelegate’s method(s).
Hence the “delegate” delegate can point to it.
Same thing for UITableViewDataSource’s method(s). Hence the “dataSource” delegate can point to it.
TestDataSource.h
TestDataSource conforms to the UITableViewDataSource,
so that TestViewController’s tableView’s dataSource delegate can point to it.
1 2 3 |
@interface TestDataSource : NSObject <UITableViewDataSource> @property (strong, nonatomic, readonly) NSMutableDictionary *items; @end |
In the DataSource object, we have a data structure that represent the data we are going to represent on the table.
Initializing the cell object for data source’s cellForRowAtIndexPath
Naturally, when we implement cellForRowAtIndexPath in the data source, we ask the tableView to dequeue a reusable cell for us. Then simply
configure the cell with an “item” object. The reason for this is that we don’t want to initialize an cell object by using multiple parameters and pass lots of data into it.
1 2 3 4 5 6 7 8 9 10 |
// not what we want TestCell *cell = [self.testController.tableView dequeueReusableCellWithIdentifier:[TestCell cellIdentifier] forIndexPath:indexPath]; cell.propertyA = "this"; cell.propertyB = 234; cell.propertyC = "wow"; // cell... // cell... // goes on forever return cell; |
Rather, we create a configureWithItem that takes in an object type called say testItem. This is how we pass large amount of data into the cell.
Hence in your custom cell class, do something like this:
TestCell
1 2 3 4 5 |
@interface TestCell : UITableViewCell { } - (void)configureWithItem:(TestCellItem *)item; |
1 2 3 4 5 6 7 8 9 |
@interface JNOneFieldCell () { } @property (strong, nonatomic) TestCellItem *item; @end - (void)configureWithItem:(TestCellItem *)item { // this way, you can get all the data from an item object self.item = item; } |
In your data source object, you have the item objects in a structure array. Using the row index, pluck the items from the array and initialize your cell objects with it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@interface TestDataSource () @property (weak, nonatomic) TestViewController *testNowController; @property (strong, nonatomic, readwrite) NSMutableDictionary *items; @end @implementation TestDataSource - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TestCell *cell = [self.testController.tableView dequeueReusableCellWithIdentifier:[TestCell cellIdentifier] forIndexPath:indexPath]; cell.delegate = self.testController; [cell configureWithItem:self.items[@(indexPath.row)]]; return cell; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return IHGJNCellsCount; } @end |
TestDelegate
Say, in your delegate, you need to access the data that is being displayed on your table.
For example, you need to return 0 for the height on index 2’s row depending on index 2’s data.
So how do you get that data?
The concept is to access the tableView’s dataSource. Only the viewController has the reference to the tableView.
Thus that is why we have a reference to the viewController as a property, and initialized it via dependency injection like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@interface TestDelegate @property (weak, nonatomic) TestViewController *joinNowController; @end @implementation TestDelegate - (instancetype)initWithController:(TestViewController *)controller { self = [super init]; if (self) { self.joinNowController = controller; } return self; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } @end |
Now that you have reference to testNowController, you can then get the data source like this:
1 |
TestDataSource *dataSource = self.testNowController.tableView.dataSource; |
Then all you need to do is access the items array in the dataSource, and you can get the data from the row:
1 2 3 4 5 |
- (id)dataItemForIndexPath:(NSIndexPath *)indexPath { TestDataSource *dataSource = self.testNowController.tableView.dataSource; return [dataSource.items objectForKey:@(indexPath.row)]; } |
Make sure your return is id, because the array contains different kind of “item” objects. i.e.
- TestAItem
- TestBItem
- TestCItem
Now you can access the item object for that index. Check for a property in the item. Then return the height accordingly.
1 2 3 4 5 6 7 8 9 10 |
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { TestAItem * testItem = [self p_itemFromDataSourceForIndexPath:indexPath]; if(testItem.isVisible==NO) { return 0; } else { return 60.0f; } } |