Setting sectioned table header using nsfetchedresults controller - ios

I am using NSFetchedResultsController in my project along with core data. I am able to fetch all the objects from the database. I am displaying them in a sectioned table view. I want to display the total number of rows in each section as the header of the section table view. I am not getting any kind of help for this. I have tried a lot but don't know how exactly to set it . Any kind of help is appreciable.

Something like the following should work:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSInteger numberOfRows = [[[self.frc sections] objectAtIndex:section] numberOfObjects];
UITableViewHeaderFooterView *sectionHeaderView = [[UITableViewHeaderFooterView alloc] init];
sectionHeaderView.textLabel.text = [NSString stringWithFormat:#"Total objects %d", numberOfRows];
return sectionHeaderView;
}
The trick is getting the right section out of the NSFetchedResultsController and calling the numberOfObjects method on it. The sections in an NSFetchedResultsController are proxy objects that conform to the NSFetchedResultsSectionInfo delegate.

Related

UItableviewcontroller doesnt display data in rows and creates extra row

I have created UITableViewController. I have a NSMutableArray with three records. I have created a custom UITableViewCell class and linked it with TableViewCell. But when running the program my tableview displays below output. There are only three records but table view shows 5 rows and all my data are showed in first row itself.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
callRecordsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CallRecordsCell" forIndexPath:indexPath];
callRecords *record = [self.callRecordsDataController callRecordAtIndex:indexPath.row];
NSLog(#"Indexpath row now is %d",indexPath.row);
cell.callRecordOriginalDestinationLabel.text = record.originalDestination_Alias;
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"dd-MMM"];
cell.callRecordStartTime.text = [[NSString alloc]initWithString:[df stringFromDate:record.start_Time]];
cell.callRecordCallDuration.text = record.call_Duration;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSLog(#"count is %d",[self.callRecordsDataController callRecordsCount]);
return [self.callRecordsDataController callRecordsCount];
}
DataController code
-(NSUInteger)callRecordsCount{
return [self.callRecordsList count];
}
- (callRecords *)callRecordAtIndex:(NSUInteger)index{
return [self.callRecordsList objectAtIndex:index];
}
I found the problem.it was because of rendering issues in iphone7plus simulator.i ran the same project in other simulators it was displaying it correctly.
!iphone7plus simulator
There are only three records but table view shows 5 rows and all my data are showed in first row itself.can somebody help??
First on all, I DO think the table is showing only 3 records.
Three records ARE NOT displaying in the same row.
My guess,
Back to the storyboard, I think you set the row height, separator and the layout constraints in the cell wrongly.
Please screecap the storyboard and post your cell design.
Also read this tutorial about creating custom cell with storyboard:
https://www.appcoda.com/ios-programming-customize-uitableview-storyboard/

how to create single tableview with two nested tableview cell xibs using iOS storyboard?

I am trying to implement accordion tableview with parent and child custom tableview cells. I am using below mentioned open source code.
Source code : https://github.com/singhson/Expandable-Collapsable-TableView
In that code having single tableview with single tableview cell. It will show for parent and child cells but I want to make:
Main storyboard Tableview
Parenttableviewcell separate class and xib
Childtableviewcell separate class and xib
It should apply on main controller tableview with accordion. Right now in this code there is no separate custom cells (parent and child using same tableview cell and changing data only).
You can implement it by trying to manage the sections and rows of a table view, thats what i assume to be the simplest way to implement it without using any third party code. For example
All the section headers will contain their own number of rows.
Take a array or dictionary or array that will store the state of expanded rows in sections. (Say '1' for expanded state and '0; for collapsed state)
If in the initial state, all the UI is expanded then set all the objects '1' in array or dictionary.
On tap of individual headers (I am assuming section will be collapsed on click on individual header of section) you can get which section need to be collapsed. Retain this state as '0' for collapsed section rows in the array or dictionary.
Reload the table and check in heightForRow delegate method for the '0' entity in your array or dictionary. Wherever you find it to be '0', return height as 0 in the delegate method.
Perform the opposite for reverse functionality.
UPDATE FOR CODE
- (IBAction)btnMenuViewTypeTapped:(id)sender{
UIButton *btnSender = (UIButton *)sender;
if(menuViewType == MVTCollapse){
[btnSender setImage:[UIImage imageNamed:#"MenuCollapse"] forState:UIControlStateNormal];
menuViewType = MVTExpand;
for(NSInteger intAtIndex=0; intAtIndex<[mutArrSearchMenuItems count]; intAtIndex++){
[mutArrSectionOpened replaceObjectAtIndex:intAtIndex withObject:#"1"];
}
} else{
[btnSender setImage:[UIImage imageNamed:#"MenuExpand"] forState:UIControlStateNormal];
menuViewType = MVTCollapse;
for(NSInteger intAtIndex=0; intAtIndex<[mutArrSearchMenuItems count]; intAtIndex++){
[mutArrSectionOpened replaceObjectAtIndex:intAtIndex withObject:#"0"];
}
}
[tblViewRestaurantMenu reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [mutArrSearchMenuItems count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSInteger intNumOfRow;
if([mutArrSectionOpened[section] isEqualToString:#"1"]){
intNumOfRow = [mutArrSearchMenuItems[section][strMenuType] count];
}
else{
intNumOfRow = 0;
}
return intNumOfRow;
}
You can implement it by using SKSTableview.
for that Please refer the below link:
SKSTableView
Let make two kinds of custom cell.
+Parent cell is tableview sectionHeader
+Child cell is normal cell
/*your datasource like this
arrData = #[section, section, section ....]
section.name
section.image
section.arrayChild
arrayChild = #[child, child, child....]
child.name
child.image
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return arrData.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
section = arrData[section];
child = section.child;
return child.count;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
//Cell is Custom of UITableViewHeaderFooterViewCell
//load your Parent Cell here
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//load your custom chill cell here
}

ios table with dynamic sections and two different prototype cells

I am new to ios programming so bear with me if the question is simple. I have a core data table mapped to a table view controller. The data in it currently looks as follows - there is one prototype cell:
I need to sum up the data by dates and show the details of each date in a different section with the summed up total coming up as the first row. Something like:
My question is is this doable? I am thinking I need to create sections and two prototype cells within each table cell. Would appreciate quick feedback.
Thanks all!
The easy way to do this is using section headers. You can either use a single string (#"%#: %#", date, total) or a wrapper view with a label on the left for the date and on the right for the total.
-(NSString *) tableView:(UITableView *)tv titleForHeaderInSection:(NSInteger)s
{
NSString *dateString = [self dateStringForSection:s];
float total = [self totalForSection:s];
return [NSString stringWithFormat:#"%#: %0.2f", dateString, total];
}
Or
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return [self wrappedHeaderForSection:s];
}
You'll have to implement dateStringForSection: or wrappedHeaderForSection: appropriately, of course.
The easiest way is to style your UITableView to 'UITableViewStyleGrouped'.
UITableView *tab = [[UITableView alloc] initWithFrame:rect style:UITableViewStyleGrouped];
Or you can go to interface builder and in Table View change style from plain to grouped.
The style 'Grouped' divides your table into multiple sections.
The using UITableViewDelegate methods specify all the parameters.
// Tell the number of section in table
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return numberOfSections;
}
//Tell the number of rows in each section
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0)
{
return 2;
} else if(section == 1)...
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == 0)
{
//Show Amount for Jul 02, 2013
cell.textLabel.text = #"Jul 02, 2013";
cell.detailTextLabel = #"20.35";
}
// Do the same for all rows and section in table.
}
For further reference -
http://mobisoftinfotech.com/iphone-uitableview-tutorial-grouped-table/
You should also definitely check out the Sensible TableView framework. Saves me tons of time when working with Core Data.

ios - struggling to populate a UITableView from an NSArray

I have this code:
#interface MyBusinessesController : UIViewController
{
NSDictionary *businesses;
NSArray *items_array;
}
#property (weak, nonatomic) IBOutlet UILabel *messageLabel;
- (IBAction)plan:(id)sender;
#property (weak, nonatomic) IBOutlet UITableView *itemList;
#end
and I set the UITableView and the NSArray in the header area of the .m file. Then I have a remote server call and get back JSON. I get the JSON data into an array like this:
items_array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
Then I loop through the items like this:
for (int i = 0; i<= items_array.count - 1; i++)
{
NSDictionary *dict = [items_array objectAtIndex:i];
NSString *item_title = [dict objectForKey:#"item_title"];
NSString *item_id = [dict objectForKey:#"item_id"];
...
and then I would like to add it as a row in my UITableView, but I am struggling with how to do it now.
What I would want is to display the item_title to the user, and when the user presses the title that I would be able to know how to get the item_id of that item_title.
Thank you!
You need to implement the required methods in the UITableViewDataSource protocol, and set the dataSource property of the tableView to self.
Accordingly, implement the appropriate UITableViewDelegate methods, and set the delegate property of your tableView to self.
See the documentation for details on which methods are required, and which optional methods you might want to implement.
Don't forget to advertise in your .h file that your Class conforms to both protocols:
#interface MyBusinessesController : UIViewController <UITableViewDataSource, UITableViewDelegate>
You can make the tableView refresh its content by calling [itemList reloadData].
Table views work differently to what you appear to be used. You don't loop over your data and fill the table. Instead, you set yourself as table's delegate and then the table will ask you:"How much data do you have, what data do you want at row 5" and so on.
I'd really suggest you go over this great tutorial here:
http://kurrytran.blogspot.com/2011/10/ios-5-storyboard-uitableview-tutorial.html
Instead of looping through the array and pulling out the string values you can let the data source methods for UITableView handle this. So in cellForRowAtIndexPath method you would index your items_array with the index path as such:
NSDictionary *dict = [items_array objectAtIndex:[indexPath row]];
Then you would pull the strings out of the dictionary like you did in the loop and set the title for the cell to the string. For selecting the cell, you can write your code in the didSelectRowAtIndexPath method.
Here is an example from a project I was working on:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [items_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)
reuseIdentifier:#"cell"];
NSDictionary *dict = [items_array objectAtIndex:[indexPath row]];
cell.textLabel.text = [dict objectForKey:#"item_name"];
return cell;
}
The first method specifies the number of sections you want in your table. If you want a very simple table this will be 1. The second method is the number of rows. This will be the number of items in your items_array so: [items_array count]. The third method creates a cell based on the index. It will go from section 0 to the number of sections you specify and from row 0 to number of rows per section you specify. So now instead of looping you can just index out your array. [indexPath section] gives the section number and [indexPath row] gives the row number.
*I know I should probably dequeue cells before making new ones but my array is very small.

Adding a row in TableView iOS

I´m quite new to iOS development and I´m having a terrible time by trying something that should be easy; to add an extra row in a TableView everytime the user clicks on one of the existing rows. There is no real purpose on that action, I´m just wanting to understand the behaviour of TableView.
So I did the following:
I used a Split View-based template and changed the number of rows to 30 in the RootViewController.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 30;
}
The method tableView:didSelectRowAtIndexPath looks in the following manner:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
*/
NSMutableArray* paths = [[NSMutableArray alloc] init];
NSIndexPath *indice = [NSIndexPath indexPathForRow:30 inSection:0];
[paths addObject:indice];
detailViewController.detailItem = [NSString stringWithFormat:#"Second Story Element %d with all its information and bla bla bla", indexPath.row];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
When I execute the program and click on one of the elements, I receive the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (30) must be equal to the number of rows contained in that section before the update (30), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).'
I did not change any other part of the code that the template provides.
I read quite extensively the documentation from Apple and the responses to the following questions:
Add a row dynamically in TableView of iphone
and
how to properly use insertRowsAtIndexPaths?
The second question seems to address the same problem, but I´m not capable to understand what is happening. What do they mean with dataSource? The response that I understand better says the following:
It's a two step process:
First update your data source so numberOfRowsInSection and cellForRowAtIndexPath will return the correct values for your post-insert data. You must do this before you insert or delete rows or you will see the "invalid number of rows" error that you're getting.
What does this update of the data source implies?
Sample code would be HIGHLY appreciated, because I´m totally frustrated.
By the way, all that I´m trying has nothing to do with entering the editing mode, has it?
You need to keep the count returned by tableView:numberOfRowsInSection: in sync!
So when you have 30 rows and then tell the tableview to insert a new row you need to make sure tableView:numberOfRowsInSection: will now return 31.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
{
return self.rowCount;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.rowCount++;
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
In practice you would probably use an array to track your rows return [self.rows count]; etc
The answer is quite simple. When you want to modify a table view you need to perform two simple steps:
Deal with the model
Deal with table animation
You already perform the second step. But you have missed the first one. Usually when you deal with a table you pass it a data source. In other words some data to display within it.
A simple example is using a NSMutableArray (it's dynamic as the name suggests) that contains dummy data.
For example, create a property like the following in .h
#property (nonatomic, strong) NSMutableArray* myDataSource;
and in .m synthesize it as:
#synthesize myDataSource;
Now, you can alloc-init that array and populate it as the following (for example in viewDidLoad method of your controller).
self.myDataSource = [[NSMutableArray alloc] init];
[self.myDataSource addObject:#"First"];
[self.myDataSource addObject:#"Second"];
Then, instead of hardcoding the number of rows you will display (30 in your case), you can do the following:
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [self.myDataSource count];
}
Now, in you didSelectRowAtIndexPath delegate you can add a third element.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.myDataSource addObject:#"Third"];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
It looks like one big problem is with tableView:numberOfRowsInSection:. You need to return the correct number of rows in that method.
To do that, it's usually best to maintain an NSArray or NSMutableArray of items for the table view so in that function, you can say: return [arrayOfValues count];. Keep the array as a property of your view controller class so that it's readily accessible in all methods.
The array can also be used in cellForRowAtIndexPath:. If you have an array of NSString, you can say cell.text = [arrayOfValues objectAtRow:indexPath.row];.
Then, when you want to add an item to the table view, you can just add it to the array and reload the table, e.g. [tableView reloadData];.
Try implementing this concept and let me know how it goes.
You can Also do that for dayanamic table cell
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayStationStore count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndentyfire;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentyfire];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentyfire];
}
cell.textLabel.text = [arrayStationStore objectAtIndex:indexPath.row];
return cell;
}
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Check if current row is selected
BOOL isSelected = NO;
if([tblStationName cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
isSelected = YES;
}
if(isSelected)
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
[arrayReplace removeObject:indexPath];
NSLog(#"array replace remove is %# ",arrayReplace);
}
else
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
[arrayReplace addObject:indexPath];
NSLog(#"array replace add is %# ",arrayReplace);
}
return indexPath;
}

Resources