how to not return a UITableviewCell - ios

I have a NSArray of NSDictionaries, in this array there are several values which I do not want to show in the UITableView, I would like to know how to avoid returning these cells in the tableView:cellForRowAtIndexPath:
I have tried to return nil; but this has caused me errors.
This is what my code looks like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomInstallCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomInstallCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.selectionStyle = UITableViewCellSelectionStyleGray;
currentInstallDictionary = [sortedItemsArray objectAtIndex:indexPath.row];
NSNumber *tempDP = [currentInstallDictionary objectForKey:#"dp"];
NSInteger myInteger = [tempDP integerValue];
if (myInteger == 0) {
return cell;
}
return nil; // gives error
}
any help would be appreciated.

This method must return a cell. It cannot return nil. The best thing to do is filter your list before you load your table and use the filtered array when dequeueing cells.

The UITableView is only asking for a cell because you told it to ask. Your implementation of the UITableViewDataSource protocol implements the two methods:
numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
In those methods you determine how many cells should appear on screen. As Brian Shamblen answered, if you don't want that data to appear in the table view, some how ignore (filter, delete, whatever) that data when you calculate the number of sections and rows. If you do so, no "extra" cells will be requested.

Related

Obj-c - Return two custom cells at the same time

I have two different custom TableView Cells. That said, when the first cell type is returned, I want the other returned immediately after. Technically, the below code is what I want to occur:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
UniversalAlertTableViewCell *cellUni = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
return cell;
return cellUni;
}
However, as we know, code stops executing after the first return cell. How can I accomplish this?
You can only return one specific type at a time. Use the indexPath parameter to decide which one to return.
The following code is a rough idea of what you need. It's up to you to adapt the if statement based on your actual needs.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
// configure cell based on data from your data model for this indexPath
return cell;
} else {
UniversalAlertTableViewCell *cell = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
// configure cell based on data from your data model for this indexPath
return cell;
}
}
Your question is very interesting. First clarify your needs.
I guess your demand is probably like this, and once the layout is displayed, switch to another layout. Let me talk about my habit of using tables. I am used to using data to control display and animation behavior. For your needs, I can use two data sources, corresponding to two kinds of cells. After one data source is displayed, immediately switch to another data source, and finally refresh the table.
The code is probably like this, you need a data source array models, and then each cell corresponds to a data source.
#property (strong, nonatomic) NSArray *models;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
ChatModel *model = ChatModel.new;
self.models = #[model];
[self.tableView reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UniversalAlertModel *universalModel = UniversalAlertModel.new;
self.models = #[universalModel];
[self.tableView reloadData];
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.models.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
id model = self.models[indexPath.row];
if ([model isMemberOfClass:ChatModel.class]) {
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
// Set UI display according to model
return cell;
}else if ([model isMemberOfClass:UniversalAlertModel.class]){
UniversalAlertTableViewCell *cellUni = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
// Set UI display according to model
return cellUni;
}
}
You cannot return two custom cells at the same time. It is not possible, because the delegate only runs one time per row. You can return a different cell on a different row or section.

How can I reuse UITableViewCell only when needed?

I have a tableview that has a UISegmentedControl as the tableHeaderView for my tableViewController. I have two arrays as properties that will populate the cells based on which index is selected in the segmentedController. When the two arrays have a different number of items in them, I get an out of bounds exception, which I would expect. When the number of items is the same in each array, I can reload the tableView data and everything works fine. How can I prevent the controller from trying to reuse a cell when the cell isn't needed anymore?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Downloads Cell" forIndexPath:indexPath];
// 1st option selected
if (self.downloadTypeSelector.selectedSegmentIndex == 0) {
if (indexPath.row < self.talkDownloads.count) {
// Configure the cell...
Talk *talk = [self.talkDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = talk.title;
cell.detailTextLabel.text = talk.speaker;
}
// 2nd option selected
} else if (self.downloadTypeSelector.selectedSegmentIndex == 1) {
if (indexPath.row < self.speechesDownloads.count) {
Speech *speech = [self.speechesDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = speech.title;
cell.detailTextLabel.text = speech.speaker;
}
}
return cell;
}
I see that you have a condition that checks downloadTypeSelector.selectedSegmentIndex and uses the respective array accordingly, which is fine.
Do you have the same if else condition in - tableView:numberOfRowsInSection: that checks the selectedSegmentIndex and returns the correct count of the respective array?
If that works then I think checking if indexPath.row < array.count will be obsolete too.
Cheers!

Manage tableview Cells from already stored data

Hi I am trying to make table view that has 5 cell and each cell automatically fills from the data or data type I have stored. here is the an example of what I have in mind, Please let me know if my question wasn't clear enough
cheers.
Maybe I asked my question in a wrong way, I'll try to explain it more.
So the Application I am making works this way:
I have 5 different Drill type that each has different condition, and I need to make a different drill recording stats (UI) and tableView for each drill type,  
So in the first table view that call Drills I have a cell that should show drill type and date that user made also one Create drill button that takes the user  to create drill (UI ), that has some attributes in it for example name, date , and DrillType(only 5 option),  
After this I have to make 5 different table and scoringViewControl for each drill type specifically that they be able to insert their stats as many times as they want.
And here I have problem, I don’t know what is the best way to manage navigating my cells in to the correct [drilltype] tableview..
(if I didn’t have to have some condition in create drill I would just made 5 statics Cell and navigate them into different direction)
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self.drill.drillType isEqual: #"Grouping"] ){
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = drill.drillType;
cell.detailTextLabel.text = drill.drillDate;
return cell;
}
else if([self.drill.drillType isEqual: #"Normal"] ){
static NSString *CellIdentifier = #"Cell2";
UITableViewCell *cell2 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell2.textLabel.text = drill.drillType;
cell2.detailTextLabel.text = drill.drillDate;
return cell2;
}
return 0;
}
If you need want to have many cells you should use dynamic dispatch. Use dictionary to map type of cell to the key (if statement). Here is code
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *cellIds = #{#"Grouping" : #"GroupingCellId", #"Normal" : #"NormalCellId"};
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *CellIdentifier = cellIds[drill.drillType];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self configureCell:cell withObject:drill];
return cell;
}
Also if you need to do some specific configuration for cells you could do it in 2 ways:
1 - Subclass a UITableViewCell and implement method that does cells configuration. different cells will implement this method in different ways. This technic is called "Polymorphism". Some explanation
[cell setDrill:drill]
2 - Make a dynamic dispatch (call a method) depending on what type is it. The same tech nick as for getting different cells for different type.
NSDictionary *configMethods = #{#"Grouping" : #"configGrouping", #"Normal" : #"configNormal"};
NSString *method = configMethods[drill.drillType];
[self performSelector:NSSelectorFromString(method) withObject:cell withObject:drill];

Is there a way to populate two or more different UITableViews using the same cellForRowAtIndexPath function?

I have a custom UITableView class that I have multiple instances of in a single ViewController. What is the most elegant way to populate these different instances with unique cell data? Thanks for any help in advance!
Right now I can think of two possible solutions:
Have different table view data sources for each table. You can create the data sources in the same file as the view controller if that is important to you.
Have your tableView:cellForRowAtIndexPath: method conditionally load the cells depending on the table. You can find out which table view is calling the method with the first argument of the method. You could also use the tag property of UITableView for differentiation.
I personally prefer the first one.
The answer I wrote here:
https://stackoverflow.com/a/19568737/480415
May help you achieve this.:
You could also put 2 separate UITableViews on your UIViewController,
then handle it in the delegates/datasource methods, ie:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableView == _leftTableView)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//fill cell data here
return cell;
}
else if(tableView == _rightTableView)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//fill cell data here
return cell;
}
return nil;
}
Notice that the method signature tableView:cellForRowAtIndexPath: includes a reference to the tableView. You can use that to determine which tableView is requesting a cell, and return the same/different data accordingly.
in your controller, you should have your tableViews defined as a properties
#property (nonatomic, strong) UITableView *myTableView
Then, in tableView:cellForRowAtIndexPath: you can check which table is making the callback using something like:
if (tableView == self.myTableView){
//return cell
} else if (tableView == someOtherTableView) {
//return some other cell
}

How to add extra cell in cellForRowAtIndexPath?

For example, I have 3 articles and when I display articles I want to display one more cell before the first one (that would be then 4 total).
I need to display that first article which is not in array and then articles which are in array.
UPDATE
I have tried next:
- (NSInteger)tableView:(
UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return ([arr count] + 1);
}
But my app then crash sometimes and I see over NSLOG then that app enters cellForRowAtIndexPath before I call [tableView reloadData].
This is something you should not really do.
Well, you can cheat the framework by returning a view (from -tableView:cellForRowAtIndexPath:) which would contain 2 subviews: your "first article" cell and the original first cell; don't forget to modify -tableView:heightForCellAtIndexPath: as well (otherwise your view would get cut).
But in general, you should change you data model behind the table view to dispay 4 cells – this is just a more valid approach.
You can do like this :
Return an additional row using this method :
// Each row array object contains the members for that section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [YouArray count]+1;
}
At the end check for this added row :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create a cell if one is not already available
UITableViewCell *cell = [self.mContactsTable dequeueReusableCellWithIdentifier:#"any-cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"any-cell"] autorelease];
}
//Identify the added row
if(indexpath.row==0)
{
NSLog(#"This is first row");
}
else{
// Write your existing code
}
}
You need to add a extra value in the array using insertObject
[arr insertObject:[NSNull null] atIndex:0];
And implement the methods like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arr count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if([arr onjectAtIndex:indexPath.row] == [NSNull null])
{
// 1st cell extra cell do your stuff
}
return cell;
}
Do you keep all your articles in an array? You should add the new article to the array too. The array is your datasource. I take it that you'd want to insert the new article as the first element in your array if you'd like it to appear in the top cell. As soon as you've updated your array you should call [mytableView reloadData] which triggers all the datasource methods to be called and reload your table's data.

Resources