Reputation: 19969
I have a UITableView and have seen this effect and would like to implement it for our the followind data:
menu_header
menu_subheader
* item
* item
menu_subheader
* item
* item
* item
Basically, I would like to show just the header and subeaders and then when the user clicks one of the subheaders, it displays the items (preferably in an animation block) AND adjusts the other cells down or up appropriately. Like this:
Is there a prebuilt component that does this? Thinking about it, it seems like I would like to set these item cells to be hidden. I have seen this https://github.com/peterpaulis/StaticDataTableViewController but it looks like it doesn't work with dynamic data. It seems like this should be really simple. Any ideas on how to get this done? Ideally, I'd like it to be able to when you click it insert the data and then if you click another sub-header, close the other one and add to that sub-header.
Upvotes: 8
Views: 2647
Reputation: 10938
You have to remove and insert cells into your table view instead of hacking their height.
We use a controller for table views that lets you "hide/show" cells while actually handling the removal/reinsertion of rows in the table view.
The way "Toggle Details" works on the Demo is pretty similar to what you are trying to achieve:
- (IBAction)toggleDetails:(id)sender
{
// Hide if all hiddeable rows are hidden, show all otherwise
[self.topSection setObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)]
hidden:(![self.topSection isObjectAtIndexHidden:1] &&
![self.topSection isObjectAtIndexHidden:2] &&
![self.topSection isObjectAtIndexHidden:3])];
}
Changing the height of cells to "hide" them is not optimal as the delegate will be asked for their sizes many times and cells will still be instantiated and configured yet not visible.
The sample library keeps the row data on memory (one object per row) while reusing cells. This should be ok for most projects, but maybe in your case not only all objects shouldn't be on memory but also you shouldn't fetch all of them at once.
Upvotes: 2
Reputation: 91
I actually wanted to just add a comment, but reputation issues...
Anyway, my personal favorite way of expanding/collapsing UITableView sections is described in this post: https://stackoverflow.com/a/1941766/2440562
If I am understanding the issue correctly, the menu_headers
and menu_subheaders
would always be visible and only the items would be shown/hidden.
So here it is my idea (let's see if I can explain it well enough):
You probably have an idea how many menu_subheaders
you would have for each menu_header
(static count or the number of elements of an array), so you can add one section for each menu_header (which would actually contain only one row or header) and in-between those you can add the expandable sections (menu_subheaders
), which can be managed as shown in the answer I mentioned above. And as you want to collapse the previously expanded menu_subheader
when tapping on another, you could just reset its boolean value and reload both with the reloadSections
method. You would have to do some calculating for the placement of the menu_headers
and menu_subheaders
, but basically you wouldn't have to deal with cell heights and row insertions and deletions (that actually is my favorite part).
Here it is a quick code snippet of the calculations I've mentioned (not tested, totally improvised):
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return <number_of_menu_headers> + <number_of_menu_subheaders>;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
// handle first menu_header
} else if (indexPath.section < 1 + <number_of_menu_subheaders1>) {
if (indexPath.row == 0) {
// handle the menu_subheader header row
} else {
// handle the rest of the items
}
} else if (indexPath.section == 1 + <number_of_menu_subheaders1>) {
// handle second menu_header
} else if (indexPath.section < 2 + <number_of_menu_subheaders1> + <number_of_menu_subheaders2>) {
if (indexPath.row == 0) {
// handle the menu_subheader header row for the current menu_subheader
} else {
// handle the rest of the items for the current menu_subheader
}
} etc...
}
Again, just an idea...
Upvotes: 1
Reputation: 119242
To implement "folding" in a table view you have two options:
insert
or deleteRowsAtIndexPaths:withRowAnimation:
methods on the tableView. beginUpdates
followed immediately by endUpdates
to re compute the heights and animate to the new layout. I've created a simple implementation of the second option in this GitHub repo. Please let me know if you have other questions about it.
Upvotes: 6
Reputation: 26385
There is a sample code from Apple that can help you in get this result.
The main difference is that in the Apple sample code is the header that triggers the action and shows the relative subviews, but this is not a blocking issue.
You can use normal cells to achieve that, by inserting and deleting rows while selecting one of them.
What is important is that you need to to remap datasource information or pair the info with another collection to get the state of that cell: opened or closed and subheader or item to identify them and choose the right action while selecting it.
Also important is to keep consistency between your data model(data source) and the number of cells, if you do using batch operation to ad insert and remove cells would not be a problem. If you don't you are going to see a lot of exceptions.
Take a look also here.
Upvotes: 1
Reputation: 38
Make your header will be Tableview Section and sub header will be Row... And in didSelectRow delegate method insert rows, that will be your items.
Upvotes: 1