Saving and loading state of tableview - iphone

I have a tableview where the user can make sections of people using a slider. So each section can have any number of people. I want to save the state of that tableview and then reload it when they come back.
I figured that since I'm using core data I can give each person a row and section attribute. So I'm able to save that but I don't know the best way to use those values to fill the tableview when it reappears.
I don't think that NSUserDefaults would work the best because I have many groups that can be broken into sections. I've been struggling with the best way to do this for a few days now and I'm still not sure what way to go.
More (per mihir mehta):
// Set core data values
int sec = 0;
int row = 0;
for (NSArray *section in groupsArray) {
for (People *person in section) {
[person setSubgroupSection:[NSNumber numberWithInt:sec]];
[person setSubgroupRow:[NSNumber numberWithInt:row]];
row++;
}
sec++;
row = 0; // new section so restart the row count
}

If you are already familiar with CoreData then perhaps you should stick with the plan you describe. The way I see it you should make some kind of TableViewInfoManagedObject:NSManagedObject. This TableViewInfoManagedObject should have members like #dynamic numberOfSections for example that describe what you need for your table view to work.
If you use CoreData to manage the people already consider using relationships to map numberOfSections to numberOfGroups or whatever you have in your People:NSManagedObject.
Also you need to consider when the appropriate time to "save" your state, which seems to be completely determined by the slider. In that case you may want to implement an IBAction for valueChanged.
EDIT 0: Based on the snippet you have provided it seems like at the end of the loop you would have the requisite info you need. The final value of sec should correspond to the UITableViewDataSource delegate method -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView and I am not really sure why you are setting the row number of the People object unless you are trying to achieve some sort order, which should be accomplished anyway by an NSSortDescriptor. So tableView:numberOfRowsInSection should return something like [[peopleinSection: section] count] and your tableView:cellForRowAtIndexPath should be set up so that it returns a cell like cell.textLabel.text = [[[peopleInSection:indexPath.section] objectAtIndex:indexPath.row] getPersonName]. Makes sense?

How about creating a class (subclass of NSObject) for each object that you need to save, and in that class you can add properties for each object. Then, you can use NSKeyedArchiver/Unarchiver to save and load your objects back to reuse.

Make a function that will take row and Section as argument and Returns that particular person by searching the array .... Got my point ?

Related

constant tableviewcell at the beginning of the tableview

I would like to know if there is any sneaky way of getting a UITableViewCell to appear at the beginning of a UITableView no matter what the array that is going to populate the tableview contains?
I am having issues where I would like to have a "select all" cell at the top of the tableview but currently having issues trying to adjust the arrays I have going into the tableView:cellForRowAtIndexPath:
Not sure of your end goal, but there are many ways some easier (sneaky ways) than others depending on your needs. It sounds like your items are fairly static, so you can just insert them before you display or update the table row.
say you have a datasource called self.items and you did something to get data. Maybe in your app you are round tripping to some data source based upon input, like sort selectors or from the search bar delegate.
try something like this in the area where you load your datasource.
NSString *myCustomObject = #"Jump To Songs";
self.items = [self getGetMovieList];
[self.items insertObject:myCustomObject atIndex:0];
[self.tableView reloadData];
Another easy way would be to add sections to your table and just make the first section your navigation items.
Note: you may need to handle the actions in didSelectRowAtIndexPath......
there you go, not so sneaky, but pretty simple.
be well
You can make two types of UITableViewCell, then return the "select all" one if indexPath.row == 0.
On the other hand, how about just make a UIView for the "select all" functionality, and set it as the tableHeaderView of your table view?

Duplicating a UITableView with a different data source

I have a UITableView in a ViewController with a custom UITableClass implemented. The table displays different songs that the user can play. The table is populated by a method that pulls data from a server. This method is called in ViewDidLoad.
The user can also tag songs as a 'favorite'. I'd like the user to be able to view all of their 'favorite' tracks in a new `UITableView'. This table should be exactly the same, only with a different data source (only favorited tracks from the server).
How should I implement this? Should I create another method that loads new data to the table with only 'favorited' tracks? Should initialize a new UITableView with the same class and somehow set a different data source or a new ViewController? If so, how?
There will be a slight difference between the two ViewControllers that contain the UITableViews. The original ViewController with all of the tracks will have a button that either changes the datasource or initializes a new UITableView (depending on how it's implemented). The 'favorited' ViewController will have a back button.
I would create a segmented control that has options for "Favorites | All" and when it is switched a BOOL called favoritesOnly or something like that is switched from YES to NO or vice versa. My songs would be kept in an NSArray of NSDictionarys called songsArray and I would use this as my DataSource methods:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(favoritesOnly)
{
NSInteger count = 0;
for(int n=0; n<[songsArray count]; n++)
if([[[songsArray objectAtIndex:n] objectForKey:#"Favorite"] isEqualToString:#"YES"])
count++;
return count;
}
else
{
return [songsArray count];
}
}
and then for the cells:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *theCell = [tableView dequeueReusableCellWithIdentifier:#"Proto Cell"];
if(favoritesOnly)
{
NSInteger count = -1;
for(int n=0; n<[songsArray count]; n++)
{
if([[[songsArray objectAtIndex:n] objectForKey:#"Favorite"] isEqualToString:#"YES"])
{
count++;
if(count==[indexPath row])
{
//Configure the Cell using [songsArray objectAtIndex:n]
return theCell;
}
}
}
//If you got here there was an error; Error cell?
return theCell;
}
else
{
//Configure cell using [songsArray objectAtIndex:[indexPath row]]
return theCell;
}
}
This way you are using the same set of Data and the same UITableView, you are just using your control to properly delegate how the DataSource displays the information on the UITableView
Now, if you are using CoreData and and NSFetchedResultsController, this is all much much easier.
You can always go the route of just updating a central playlist UITableView, in which case you would just swap the data in your dataSource (in this case, perhaps NSMutableArray *playlist?) and then call [UITableView reloadData]. In this scheme, you avoid the overhead of having multiple views and the trouble of passing data around.
If you're planning to create additional functionality for your favorited song list, a secondary, customized UIViewController might be in order. In this sense, it can be re-used if you decide to have additional song lists. This would be a good solution if you intend to let them do anything additional that you wouldn't want cluttering your main interface with your list(s) such as editing title, song order, etc.
If these two views would be more or less identical, you can just set up a new UIViewController, either pass the new data via property or load it in your init, and then push it onto the view stack. So long as your app is navigation-based, the back button will appear on its own once you push your secondary UIViewController onto the stack. That isn't functionality you need to add on your own. The perks of this include code-reusability, which is a good skill to have as a UI designer and an engineer.
If you just want a read-only view, you can also look into a UIPopoverController with the data that would dismiss once they click away. This solution is not robust in the least and shouldn't be used if you intend the user to be doing anything more than tapping an entry, or if you expect your datasets to get big.
When planning your UI and flow, just make sure you think of what directions you might take it in the future. As mentioned in another answer, how you store your data makes a difference, as well as how you intend to make calls to your server (button clicks? after a set time?)
how do you store your data from the server? if you use CoreData (or MagicalRecord, which I can recommend) that having a fetched results controller with a different argument would be the only change you need.....
Ah, link to MagicalRecord: MagicalRecord
when you are tagging song as a favorite send one flag as a favorite to the web-service and then call another web service in the favorite view controller and make new table view with the same custom class and view the new source coming from server... and if you are storing in sq-lite or using core data make one column extra as favorite and call it in favorite view controller and load it with different data-source.
I would have a refresh method, and a button that switches between "show all" and "show favorites".
Basically, if the button is clicked, switch to the opposite group of objects, and update the text on the button accordingly. The table will always load an array called "tableDataArray" in my example, and you'll get row counts and such from the length of it.
Like...
-(IBAction)refresh {
if ([faveButton.text isEqualToString:#"Show All"]){
tableDataArray = favoriteArray;
[faveButton setText:#"Show Favorites"];
}
else {
tableDataArray = allSongsArray;
[faveButton setText:#"Show All"];
}
[tableView reloadData];
}
Simplest way to do it would be to have two instances of the same view controller. Each instance will have its own data source, one with all the songs, another with only the favorites.

How to build an Array or Nested Array to display in UITableView and then sort by certain objectAtIndex?

Sorry for such a confusing title but it's hard to explain in a few words what I'm trying to accomplish. I'll try to explain the best I can. Ok, I'm parsing data from a xml file. It's constructed like so:
<item1>
<subitem1>text</subitem1>
<subitem2>text</subitem2>
<subitem3>text</subitem3>
<subitem4>text</subitem4>
<subitem5>text</subitem5>
</item1>
<item2>
<subitem1>text</subitem1>
<subitem2>text</subitem2>
<subitem3>text</subitem3>
<subitem4>text</subitem4>
<subitem5>text</subitem5>
</item2>
<item3>
<subitem1>text</subitem1>
<subitem2>text</subitem2>
<subitem3>text</subitem3>
<subitem4>text</subitem4>
<subitem5>text</subitem5>
</item3>
so on and so on...
So basically I want to display each item into a separate row in a UITableView and put each subitem into it's parent's row/cell as a label to display the info about it's parent "item".
Also, I need to be able to sort each item by one of it's subitems i.e. let's say subitem4. If subitem4 is equal to some string then it would display that item into the UITableView however if subitem4 isn't equal to that string I compare it to then that item wouldn't get displayed in the UITableView. As of right now I really don't have any working code because I'm not sure how to go about making this work. I don't know how I would do this because I have 1 array right now with all of the subitems together and I'm just separating each subitem and putting them into separate arrays so I can distinguish between each item row, I'm do it with the following code:
int totalNames = [Names count];
id name = [Names objectAtIndex:1];
[listOfItems addObject:name];
I'm pretty sure I'm going about this the wrong way. There must be a better way to do this logically. Any help or advice would be much appreciated. I'm mentally exhausted with this. Thanks.
Use classes.
Make each Item an instance of a class. make each subitem a property of the class. then have one array of the objects. then can sort based on a particular property or whatever.

How do I get a Row count from a UITableView

I have a tableview I'd like to customize based on how many rows it has.
If it has no rows, I'd like the background image to prompt the user to add content.
If it has 1 or more rows, I'd like it to have a different background image, in order to display the content.
I'm using a fetched results controller to populate my tableview, by the way.
Any ideas?
Well this is generally very easy to accomplish as you need only to have UITableView properly delegated to your ViewController with appropriate delegate methods included in your .m file.
Then you can anywhere get row count like this:
[tablePropertyName numberOfRowsInSection:SECTION_NUMBER];
where section number is 0 for first section, 1 for second, etc.
In Swift, you can get the UITableView rows count by
yourTableViewName.numberOfRows(inSection: Int)
example:
yourTableViewName.numberOfRows(inSection: 0) // returns rows count in section 0
I agree with Sixten Otto's answer.
[myFetchedResultsController.fetchedObjects count];
However there's more. Above line would return the number of objects regardless the sections. However if you would want to perform this on a table with multiple sections, you would have to call it the following way.
[[myFetchedResultsController.sections objectAtIndex:<section>] numberOfObjects];
You can get the objects for the section like this.
[[myFetchedResultsController.sections objectAtIndex:<section>] objects];
** You have to replace with the number representing the section.
Hope this helps.
I'd recommend taking a look at the documentation for NSFetchedResultsController. It has example code for implementing the methods of UITableViewDataSource (including the ones that say how many sections the table has, and how many rows in each section), as well as documenting the property fetchedObjects, which you could use to see how many raw results you fetched.
[myFetchedResultsController.fetchedObjects count];
tableView.numberOfRows(inSection:) returns numbers of row in section, this method may not trigger fetched results controller's core data query.
let numberOfItems = (0..<tableView.numberOfSections).reduce(into: 0) { partialResult, sectionIndex in
partialResult += tableView.numberOfRows(inSection: sectionIndex)
}

Indexpath.row value not updating

in iphone application.
I'm trying to get indexPath.row value (out of didselectedrowatindexpath method) to do something on the basis of row selected in programmatically created tableview.
i need to access indexpath.row out of didselectedrowatindexpath method where if/else will define the action on the basis of indexpath.row.
there are 7 cards images in application and one [menu list]table view. whenever user will click on row of table view,then need to touch the image
I'm trying this code to get the IndexPath.row value. The problem is indexPath.row value is not updating everytime. It's just taking the old value. Please sugggest how to solve this issue.
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
NSUInteger nSection =[myTableView numberOfSections]-1 ;
NSUInteger nRow = [myTableView numberOfRowsInSection:nSection];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:nRow inSection:nSection];
NSLog(#"No of sections in my table view %d",nSection);
NSLog(#"No of rows in my table view %d",nRow);
NSLog(#"Value of selected indexPath Row %d", [indexPath.row]);
NSLog(#"VAlue of Array arrOperationChk %d",[arrOperationChk count]);
}
This code appears to respond to something (the table?) being touched. You then ask the table how many rows it has in its last section and create an indexpath to that.
The table caches the number of rows in each section. If you have changed the number of rows, you need to tell the table, either by calling -insert/deleteRowsAtIndexPaths:withRowAnimation:, or by calling -reloadData. Otherwise the table has no way to know that it needs to re-query its datasource (which you provide).
Unless I'm reading this code wrong, aren't you just getting the index path to the last cell+1 of the last section? I wouldn't expect that to change.
If you want to get the selected cell, use the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
method in your UITableViewController object.
As other people have said, there's nothing in your code that would change the indexPath variable you've just created.
Also, the syntax [indexPath.row] looks wrong - you don't need the square brackets there unless you're calling a method. When you use the dot syntax like that on a pointer in Objective-C, you don't think of it as a method call (even though there is one, implicitly), but rather as a pseudo-instance variable as of a struct.
What is your big picture goal? If we understood what you are trying to achieve / what is the desired behavior, maybe a more useful answer will arise.