I am new to coding and have just started on working on a new app. I have been stuck for a few days searching for answers on how to remove null headers in a table header.
This is my code at the moment:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view=[DetailGroupHeader loadInstanceFromNib];
NSDictionary *category = [self.categories objectAtIndex:section];
if ([[self.restaurant objectForKey:#"restaurant_id"] isEqual:[category objectForKey:#"restaurant_id"]]) {
DetailGroupHeader *headerView=(DetailGroupHeader *)view;
headerView.lblTitle.text=[category objectForKey:#"maincatename"];
headerView.btnReveal.indexPath=[NSIndexPath indexPathForRow:0 inSection:section];
}
return view;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return self.categories.count;
}
At the top of -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section I am initiating the names of the headers, and this part is working perfectly fine.
However when I get to the next function -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView, to count the number of headers I receive null values, how can I get rid of the null values in the header?
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view=[DetailGroupHeader loadInstanceFromNib];
NSDictionary *category = [self.categories objectAtIndex:section];
if ([[self.restaurant objectForKey:#"restaurant_id"] isEqual:[category objectForKey:#"restaurant_id"]]) {
DetailGroupHeader *headerView = (DetailGroupHeader *)view;
NSString *maincateName = [category objectForKey:#"maincatename"];
if ([maincateName isEqual:[NSNull null]]) {
maincateName = #"";
}
headerView.lblTitle.text = maincateName;
headerView.btnReveal.indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
}
return view;
}
I believe you are making some really basic mistakes. As I can see you are only showing those rows which have [[self.restaurant objectForKey:#"restaurant_id"] isEqual:[category objectForKey:#"restaurant_id"]] condition true. But you are returning numberOfSectionsInTableView, count of complete categories array as self.categories.count
I will suggest you to check:
Calculate count of header where condition [[self.restaurant objectForKey:#"restaurant_id"] isEqual:[category objectForKey:#"restaurant_id"]] satisfies
Check if the condition you are working with really have it's existence, (print log or set break point)
That's all I can say for now. If problem persist, add log print of your self.restaurant dictionary and self.categories array.
Related
I've got my cellForRowAtIndexPath delegate method defined as so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PLOTCheckinTableViewCell *cell = (PLOTCheckinTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CheckinCellIdentifier forIndexPath:indexPath];
if([self.items count] == 0){
return cell;
}
NSDictionary *checkin = self.items[indexPath.row];
// configure and return custom cell
}
I'm using a custom cell class (PLOTCheckinTableViewCell).
I faced an issue where the user would pull to refresh and then attempt to pull again before the first request had completed (on completion of the request, I reload the table data). When they did this, the app would crash and say that indexPath.row was basically out of bounds, ie. the array was empty.
By putting in this IF check above, I mitigated the crash.
However,
Why exactly does my IF check "work", I see no visual implications of returning the cell before it's been configured. This is confusing
Are there any better ways to guard against this happening (ie. the table data being reloaded with an empty array)? Surely the numberOfRowsInSection would have returned array count which would be 0? (if it was an empty array)
EDIT (further code)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
float count = [self.items count];
return count;
}
- (void)resetData {
self.items = [NSMutableArray array];
}
-(void) refreshInvoked:(id)sender forState:(UIControlState)state {
[self resetData];
[self downloadHomeTimeline];
[self.refreshControl endRefreshing];
}
- (void)downloadHomeTimeline {
[self.apiClient homeTimeline:self.page completionBlock:^(NSDictionary *data){
for (NSDictionary *obj in data[#"items"]) {
[self.items addObject:obj];
}
[self.itemsTableView reloadData];
}];
}
I couple of things that i would suggest to do. Make sure that the [self.itemsTableView reloadData] is executed on the main thread and also i would put the [self.refresControl endRefreshing] in the completion block. This way it will stop the refresh when its done and you should not let the user more then once simultaneously.
- (void)downloadHomeTimeline {
[self.apiClient homeTimeline:self.page completionBlock:^(NSDictionary *data){
for (NSDictionary *obj in data[#"items"]) {
[self.items addObject:obj];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.itemsTableView reloadData];
[self.refreshControl endRefreshing];
});
}];
}
Also in the numberOfRowsInSection just return count
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.items count];
}
To add to the answer. You should not reset the array before you receive new data. While getting new data the user can still scroll the table and that means new cells will be created but your NSMutableArray doesn't have any data. That is when you get the error and app crashes. You would have to [tableView reloadData] to clear the table so that the tableView would know that there are 0 rows, which i don't think is your intent.
Let me know if that's solves the issue.
I have 2 UITableViewControllers in my project.
The problem I am having is that I am getting blank cell entries in the tableView opposite to the tableView where the data is entered.
I can't seem to figure out why this is the case.
It's creating blank rows in this tableView even though the information is from the other UITableViewController.
Here's the main tableView part from the one of the 2 UITableViewControllers:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"number of addedSpaceObjects %lu",(unsigned long)[self.diaryoptions count]);
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"number of sections %ld",(long)section);
// Return the number of rows in the section.
return [self.diaryoptions count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentification = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentification
forIndexPath:indexPath];
Data2 *diary = [self.diaryoptions objectAtIndex:indexPath.row];
cell.textLabel.text = diary.diaryname;
cell.detailTextLabel.text = diary.diaryWeight;
return cell;
}
And from other UITableViewController:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"number of addedSpaceObjects %lu",(unsigned long)[self.addedSpaceObjects count]);
// Return the number of sections.
if ([self.addedSpaceObjects count]) {
return 2;
}
else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"number of sections %ld",(long)section);
// Return the number of rows in the section.
if (section == 1) {
return [self.addedSpaceObjects count];
}
else {
return [self.recipes count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentification = #"Josh";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentification
forIndexPath:indexPath];
if (indexPath.section == 1) {
Data *recipe = [self.addedSpaceObjects objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
else {
// Configure the cell...
Data *recipe = [self.recipes objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
return cell;
}
Here is the full project on GitHub. https://github.com/josher32/Plant-Diet
Appreciate any help anyone can offer!
Ok, so I checked out the app and I'll try my best to explain the problem as precisely as I can to cover it adequately.
Firstly, the classes in question are:
RecipesTableTableViewController
AddRecipeViewController
Data
DiaryTableViewController
AddDiaryViewController
Data2
Secondly, we'll need to look into your
#define ADDED_SPACE_OBJECTS2 #"Added Space Objects Array"
AddRecipeViewController
So... AddRecipeViewController basically creates a Data object that is kept in an array and eventually stored in NSUserDefaults under the key name Added Space Objects Array.
Great!! So you now have got recipe related stuff in some Data object.
AddDiaryViewController
Same thing here.
AddDiaryViewController creates a Data2 object that is eventually stored in NSUserDefaults under the same key name Added Space Objects Array.
But before storing this, you're taking the old value of the key Added Space Objects Array, which is an array, and adding a new object to it before placing it back into NSUserDefaults.
But now... this array will now have a combination of Data as well as Data2 objects!
RecipesTableTableViewController
When we come here, things get real.
- (void)viewDidLoad
{
//...
NSArray *myRecipeAsPropertyLists = [[NSUserDefaults standardUserDefaults] arrayForKey:ADDED_SPACE_OBJECTS_KEY];
for (NSDictionary *dictionary in myRecipeAsPropertyLists) {
Data *spaceObject = [self spaceObjectForDictionary:dictionary];
[self.addedSpaceObjects addObject:spaceObject];
}
}
Since we already realized that self.addedSpaceObjects can contain Data as well as Data2 objects, in the case whendictionary is containing stuff specific to type Data2, spaceObjectForDictionary will not be able to translate it properly to the required Data object.
We're expecting name, title, ingredients, directions but we're getting diaryentry, diaryname,diaryWeight.
So (in this scenario):
The values of name, title, ingredients, directions will be nil
The section-row count will be incorrect because it will give count of both Data as well as Data2 objects (and we don't care about Data2 objects in the RecipesTableTableViewController class... right?... well anyways, I assumed)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
if (indexPath.section == 1) {
Data *recipe = [self.addedSpaceObjects objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
//...
}
We see recipe.name is nil, for some indexPaths, ergo blank rows and vice versa in DiaryTableViewController.
Solution:
Firstly, I wouldn't recommend NSUserDefaults for your purposes but anyways...
Basically, don't use a single #"Added Space Objects Array" key for your NSUserDefaults stuff.
I'd suggest you use 2 separate keys.
//replace
//#define ADDED_SPACE_OBJECTS2 #"Added Space Objects Array"
//with
#define ADDED_SPACE_OBJECTS2 #"RecipeEntries" //in RecipesTableTableViewController
//and
#define ADDED_SPACE_OBJECTS2 #"DiaryEntries" //in DiaryTableViewController
Basically, segregate the entries instead of mixing them up under a single key name.
This seems like the quickest way to solve your problem without changing your logic.
I am creating an app where users can store an object in Core Data. I have the Objects being pulled into a UITableView and everything is working correctly there. I now want to separate the objects into a possible of 1-3 different sections based on choices in a UISegmentedControl.
Currently I have this to create the 1 section and populate the cells of that section with my objects
#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 self.fetchedDiscs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DiscCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Configure Cell
Disc *currentDisc = [self.fetchedDiscs objectAtIndex:indexPath.row];
cell.textLabel.text = currentDisc.name;
cell.detailTextLabel.text = currentDisc.brand;
return cell;
}
So the main question is how to dynamically change the number of sections and number of rows in section?
The Segmented control returns values such as Option 1, Option 2, Option 3. The only way I can think of is to loop through my fetchedDiscs array and separate that into an array for each section. Then I can return the number of arrays if they exists and I can get the count of each array to get the number of rows in each section. But then I get to the problem of How to get the CellForRowAtIndextPath to work correctly with three arrays.
Basically there has to be a better more logical way to do this. I am just not sure how.
Let, you have a dictionary named dataSource that contains 'x' number of array as value for key "key1", "key2", .. "keyX". You can do this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[dataSource allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//Get the array object form key. I am considering 'keySection' as an example
return [[dataSource objectForKey:#"keySection"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DiscCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [[dataSource objectForKey:keySection] objectAtIndex:indexPath.row]
return cell;
}
Note: I wrote the code in this editor, excuse any mistakes, I just tried to share the idea. Hope this helps.. :)
I've seen a number of people who have asked a similar thing, but answers to their questions are not the answers to mine.
1) I have created a single view application with an empty View Controller. In that, I dragged a new Table View (style Plain) with a single prototype cell of style Basic.
2) I am trying to learn about dynamically changing the behaviour of TableViews, so I have a mutable array called sectionRows, which will contain the number of rows per section. At the moment, a single section with a number of rows would be an achievement :)
3) In my ViewController.h I have set the delegates
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#end
I have also control-dragged from the TableView to the ViewController Yellow-Circle and set the datasource and delegate outlets.
4) In my ViewController.m, I have defined some global variables
#interface ViewController ()
{
NSMutableArray *sectionRows;
UITableView *myTableView;
}
The first is my data array (containing the number of rows per section and the second is a pointer to my TableView, which I have identified using a numeric View tag of '1'.
5) In my viewDidLoad, I initialize everything:
- (void)viewDidLoad
{
[super viewDidLoad];
myTableView = (UITableView *)[self.view viewWithTag:1];
sectionRows = [[NSMutableArray alloc] init]; // Create sectionarray
[sectionRows addObject:[NSNumber numberWithInteger:1]];
myTableView.dataSource = self;
myTableView.delegate = self;
[myTableView reloadData];
}
As you can see, I even make sure that I set the datasource and delegate again but this hasn't made any difference.
5) I have overloaded 3 methods.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Returning %# rows", [sectionRows objectAtIndex:section]);
return (NSInteger)[sectionRows objectAtIndex:section]; // the number referenced in the array...
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"Returning %li sections", sectionRows.count);
return sectionRows.count; // the size of the sectionRows array
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"xxx";
NSLog(#"Setting cell to %#", cell);
return cell;
}
Now, when I run this, I am getting NSLog returning confirmation that there is a single section and a single row:
2014-07-27 19:58:34.599 TableViewTests[12877:60b] Returning 1 sections
2014-07-27 19:58:34.600 TableViewTests[12877:60b] Returning 1 rows
However, as you can see cellForRowAtIndexPath is not being called.
None of the other things I have seen point to what I am doing wrong. I am doing what I thought I did successfully in another simple project (to learn) but I must be doing something else differently.
Any ideas what I am missing?
Thanks in advance
Jon
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Returning %# rows", [sectionRows objectAtIndex:section]);
return (NSInteger)[sectionRows objectAtIndex:section]; // the number referenced in the array...
}
This is incorrect. You cannot cast what you get from your array to an NSInteger. It's a pointer. Assuming you store NSNumbers into the array:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Returning %# rows", [sectionRows objectAtIndex:section]);
return [[sectionRows objectAtIndex:section] integerValue]; // the number referenced in the array...
}
I have a project which uses Storyboards. I have a UITableView with Static cells and Group style.
I need to change the section text in one section depending on which selection is made in a segmented control (in another section)
I have found some solutions which indicate that you should use override this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
and trigger an update by calling:
[[self tableView]reloadSections:[NSIndexSet indexSetWithIndex:SectionToChange] withRowAnimation:UITableViewRowAnimationFade];
The problem is when I call reloadSections then all the rows and cells in the section in question get deleted. The text updates correctly thought but with this unwanted side effect.
I think I found the answer here: Changing UITableView section header without tableView:titleForHeaderInSection
It may not be very elegant but it seams to work.
I can trigger an update to only the section header with none of the unwanted side effects by calling:
[self.tableView headerViewForSection:1].textLabel.text = [self tableView:self.tableView titleForHeaderInSection:1];
So the only thing needed is to implement:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 1){
if (self.segmentX.selectedSegmentIndex == 0) // or some condition
return #"Header one";
else
return #"Header two";
}
return nil;
}
If you have implemented these functions then removing them should fix your problem:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{ if (section == 0)
{
lbl.text = #"abc";
}
if (section == 2)
{
lbl.text = #"2ndsectionbeigns";
}
return headerview;
}