I have a UITableView on a UIViewController.
And this table has "say" 5 visible categories to choose from. But in the Array that holds the content i have "say" 10 categories. What i've created now is a normal scrolling table that goes from category 1 on index 0 to category 10 on index 9.
But what i'd rather have is category 1 also after category 10 and the other way around.
So basically an endless loop of a static Array.
I've tryed this with the scrollViewDidScroll: method, but when i do that it doesn't scroll like you would expect a UITableView to scroll. It RACES to a random spot and moving 1 or two categories is impossible.
Here is a sample of my code. Hope somebody can help.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == categoryView){
if (categoryView.contentOffset.y > 0.0) {
loopcounter++;
NSLog(#"ScrollView Scrolled DOWN");
[categorytablecontent insertObject:[categorytablecontent objectAtIndex:0] atIndex:[categorytablecontent count]-1];
[categorytablecontent removeObjectAtIndex:0];
[categoryView reloadData];
}
if (categoryView.contentOffset.y < 0.0) {
loopcounter = [categorytablecontent count];
NSLog(#"ScrollView Scrolled UP");
[categorytablecontent insertObject:[categorytablecontent objectAtIndex:[categorytablecontent count]-1] atIndex:0];
[categorytablecontent removeObjectAtIndex:[categorytablecontent count]-1];
[categoryView reloadData];
}
a = categoryView.visibleCells;
for (UITableViewCell *cell in a){
NSLog(#"Current Visible cell: %#", cell.textLabel.text);
}
NSLog(#"Current offset: %#", categoryView.contentOffset.y);
}
}
There is more then one good way to do this. Does your controller implement the UITableViewDataSource Protocol? If so, the easiest thing would probably be to just return the modulo of your dataset size in cellForRowAtIndexPath.
Something like:
-(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];
}
// Use appropriate fields for your categorytablecontent object here...
cell.textLabel.text = [categorytablecontent objectAtIndex:(indexPath.row % [categorytablecontent count])];
return cell;
}
This should result in a nice smooth infinite scroll that loops through your static (or dynamic) content.
Related
I am adding new data to a tableView when the user scrolls to bottom like this....
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
float height = self.itemListTableView.frame.size.height;
float contentYoffset = aScrollView.contentOffset.y;
float distanceFromBottom = aScrollView.contentSize.height - contentYoffset;
if (distanceFromBottom < height) {
NSLog(#" you reached end of the table");
isTableScrolledToBottom = YES;
}else{
isTableScrolledToBottom = NO;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"CellIdentifier";
ListViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell==nil){
cell = [[ListViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Assign values to labels here..
if (isTableScrolledToBottom) {
if (indexPath.row == [apiMutableArray count]-1)
{
// Call the API
NSLog(#"Call the api");
int pgn = [self.currentPageNumber intValue];
pgn = pgn+1;
self.currentPageNumber = [NSNumber numberWithInt:pgn];
NSLog(#"incremented page number : %#",self.currentPageNumber);
NSMutableDictionary *mutableDict = [self.poPostDict mutableCopy];
[mutableDict setObject:self.currentPageNumber forKey:#"pageno"];
self.poPostDict = [mutableDict mutableCopy];
[self addLoaderViewAtBottom];
[self loadPOData:self.poPostDict];
}
}
return cell;
}
Now if the data in table view is big enough, the user will scroll to bottom and data fetching is done from api as expected.However when I filter this table and lets say I get only one row of data.
In such case even if I can see only one cell is present in the table, if I try to scroll the table, the api request is hit.
How to actually know if user has scrolled beyond its frame and upto scrollView's end so that only in that case I can fetch the data.
Note:- Using current technique if only one row is present and I try to scroll, the api call is hit continuously without stopping.
try this solution, it always work for me.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let lastElement = dataSource.count - 1
if indexPath.row == lastElement {
// logic here
}
}
If you still have doubt then ask.
I have more than 20 cells in my custom table view, in execution time 6 cells will be visible. Now i select the 4 th cell means, that 4th cell have to come in first position and 5th cell in 2nd position and so on. how to do this process, i tried like this.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MACalendarCustomCell *cell = (MACalendarCustomCell*) [tableView dequeueReusableCellWithIdentifier:[MACalendarCustomCell reuseIdentifier]];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"MACalendarCustomCell" owner:self options:nil];
cell = customCell;
customCell = nil;
}
cell.lbl_CalendarTitle.text = [arr_CalendarTitle objectAtIndex:indexPath.row];
cell.lbl_CalendarSubTitle.text = [arr_CalendarSubTitle objectAtIndex:indexPath.row];
cell.lbl_calendarEventTime.text = [arr_CalendarEventTime objectAtIndex:indexPath.row];
cell.lbl_calendarDate.text = [arr_CalendarDate objectAtIndex:indexPath.row];
cell.lbl_CalendarMonth.text = [arr_CalendarMonth objectAtIndex:indexPath.row];
cell.lbl_CalendarYear.text = [arr_CalendarYear objectAtIndex:indexPath.row];
cell.img_BackGround.image = [UIImage imageNamed:#"calendar_Cell_Up.png"];
//here click event is occurred.
cell.btn_CollapseExpand.tag = indexPath.row;
[cell.btn_CollapseExpand addTarget:self action:#selector(method_Expand:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
ButtonPressed event calls
- (void)method_Expand:(UIButton*)sender
{
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
[tbl_CalendarList moveRowAtIndexPath:[NSIndexPath indexPathForItem:indexpath.row inSection:indexpath.section] toIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexpath.section]];
int_SelectedIndex = sender.tag;
NSLog(#"Selected Button : %ld",(long)int_SelectedIndex);
if ( int_TempSelectedIndex != int_SelectedIndex)
{
int_TempSelectedIndex = int_SelectedIndex;
}
else
{
int_TempSelectedIndex = -1;
}
[tbl_CalendarList reloadData];
}
Resizing the cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == int_TempSelectedIndex )
{
cellSize = 300;
isRowSelected[indexPath.row] = YES;
}
else
{
cellSize = 100;
isRowSelected[indexPath.row] = NO;
}
return cellSize;
}
Now i got Like this in simulator.
when i pressed it comes like this.
This selected cell should come to first position.
You can scroll your table view to that cell and you can specify that you want to scroll it on top when you select the cell:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Hope this is what you are after.
In the method_Expand method after fetching the selected row you have to remove the object at the selected index and insert the removed object at 0 index.
Also you want to move the move next item to the second position
For that you have to increment the selected index and check if that index is with in the array bounds then remove that item and add it to the index 1;
- (void)method_Expand:(UIButton*)sender
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
int nextIndex=indexpath.row+1;
// first remove the object
NSString *str=[arr_CalendarTitle objectAtIndex];
[arr_CalendarTitle removeObjectAtIndex:indexpath.row];
[arr_CalendarTitle insertObject:str atIndex:0];
//do the same to arr_CalendarEventTime,arr_CalendarDate,arr_CalendarMont etc
if([arr_CalendarTitle count]-1<nextIndex)// check if nextIndex within bounds to avoid crash
{
// remove next object and addit to the index 1 to all array
}
[tbl_CalendarList reloadData];
}
As i wrote in the comments:
Upon selection, move selected arr_CalendarTitle entry to the top of array and call reloadData() on tableView. Table view displays data as is sorted in arr_CalendarTitle.
moveRowAtIndexPath is not enough, must resort the array too.
So, before reloadData() call (in button click method), do this:
id object = [[[arr_CalendarTitle objectAtIndex:int_SelectedIndex] retain] autorelease];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
For ARC you can use :
__autoreleasing id object = [arr_CalendarTitle objectAtIndex:int_SelectedIndex];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
Since you have more than one array that holds data (only noticed that now) you must do this for every array thats holds data for tableView cells.
Use below method to scroll your tableview.
[tableview setContentOffset:CGPointMake(0, button.tag*tableview.rowHeight) animated:YES];
This method will make tableview scroll to specified point.
Change value of Y in CGPointMake according to your code.
Am I able to create a UILabel that is layouted upon many UITableViewCells?
I'm trying to make something like (that is just one section of my UITableView, each section can have one or more rows):
---------------------------------------------
| Multi-lined label | row1 values |
| with some useless | row2 values |
| text | row3 values |
---------------------------------------------
I managed to create a UILabel (in the first row of a section) that is multi-lined and is not clipping to bounds. That works really well (it was a bit tricky to count each sections row heights, but doable) besides one case: when I'm scrolling UITableView from bottom to top - UITableView renders last row (without UILabel) so it has "no evidence" of having UILabel (because it is maintained in the first row of section). Can I force some kind of relayouting first cell in section? I tried reloadRowsAtIndexPaths:withRowAnimation: with first row in each section whenever I layouted not first cell in section but it gave me layouting errors that I really do not understand. Or maybe there is another idea to do so?
-- EDITED
To be clear: I have a custom UITableViewCell with an IB view, it has a few labels that each row consist of and a label named labelName that I want to be "multi-lined" along rows in that section. LabelName.text is empty for each row besides first one in each section.
I am adding somescreenshots:
Good screenshot - when I am scrolling to bottom I'm getting proper effect:
Bad screenshot - when I am scrolling up, UITableView renders last row of section firstly, and afterwards renders upper rows - that gives effect of cut label (because multi-line label is in the first row)
I am not sure if code here will add anything to question - it is rather simple and almost whole logic is in tableView:heightForRowAtIndexPath. I can only present how do I create custom UITableViewCell:
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
cell.contentView.superview.clipsToBounds = NO;
}
-- EDIT 2
Here is most of the code:
- (void) reloadData
{
NSUInteger index = 0;
for (NSDictionary *object in self.list) {
CGFloat height = [[object objectForKey:#"name"] sizeWithFont:self.labelFont constrainedToSize:self.labelSize].height;
[self.labelHeights addObject:NSNumberFloat(ceilf(height))];
index++;
}
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *object = [self.list objectAtIndex:indexPath.section];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
NSUInteger count = [[object objectForKey:#"list"] count];
CGFloat cellHeight = 30.f;
if((indexPath.row + 1) == count){
cellHeight = MAX(8.f + height - 30.f * indexPath.row, 30.f);
}
return cellHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.list count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.list objectAtIndex:section] objectForKey:#"list"] count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *person = [self.list objectAtIndex:indexPath.section];
NSDictionary *object = [[person objectForKey:#"list"] objectAtIndex:indexPath.row];
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = NO;
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
[cell.contentView.superview setClipsToBounds:NO];
}
if(indexPath.row == 0){
cell.labelName.text = [person objectForKey:#"name"];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
cell.labelName.numberOfLines = (int)(height / self.fontSizeHeight);
cell.labelName.frame = CGRectChangeHeight(cell.labelName.frame, height);
}
else{
cell.labelName.text = #"";
}
CGFloat cellHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
cell.borderTop.hidden = YES;
cell.borderBottom.hidden = YES;
cell.borderBottomSmall.hidden = NO;
if(indexPath.row == 0){
cell.borderTop.hidden = NO;
}
if(indexPath.row + 1 == [[person objectForKey:#"list"] count]){
cell.borderBottom.hidden = NO;
cell.borderBottom.frame = CGRectChangeY(cell.borderBottom.frame, cellHeight - 1.f);
cell.borderBottomSmall.hidden = YES;
}
cell.labelDate.text = [object objectForKey:#"date"];
cell.labelPremium.text = [[object objectForKey:#"premium"];
return cell;
}
-- PARTIAL ANSWER
I managed to create a hack, that makes multi-line UILabel visibile when scrolling bottom to up at some point:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *cells = [self.tableView visibleCells];
UITableViewCell *cell = [cells objectAtIndex:0];
[cell.superview bringSubviewToFront:cell];
}
I noticed that the part of the UILabel is covered by a row thats below of the UILabels row and that hack makes it would be properly displayed. But it has a drawback, when scrolling slowly from bottom to top it generates a flicker when label is created (part of it should be visible before real creation of UILabel).
Up mentioned answers are not solutions, but "hacks".
In the cell == nil block should be only the initialization.
You should not add any subviews in cell in cellForRowAtIndexPath.
The reason is simple: I will reuse a cell with some labels already added and add a new label.
Either use the default cell.textLabel, either create a subclass for UITableViewCell, with a
-(void)setData:(dictionary or string)object;
and in implementation just set the proper data to proper UI controls.
Add/create controls either in init method in the subclass, or in IB/Storyboard.
Call the dictionary or string should be picked in correspondence to indexPath, so you will always get proper data for proper cell at proper indexPath.
Try This
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"cellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell==nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
/// your UI on cell goes here
return cell;
}
- (UITableViewCell *)cellForInfoWithCellIdentifier:(NSString *)cellIdentifier
forIndexPath:(NSIndexPath *)indexPath
inTableView:(UITableView *) tableView
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
NSLog(#"%d", cell.contentView.subviews.count);
if (cell.contentView.subviews.count > 0)
{
[cell.contentView.subviews[0] removeFromSuperview];
}
[cell.contentView addSubview:self.viewsForOptions[self.selectedIndex]];
NSLog(#"%d", cell.contentView.subviews.count);
return cell;
}
The above code is called for a certain section and row in cellForRowAtIndexPath. This cell is changed whenever a value of the segmented control object is changed.
The method is below:
- (void)testOptionsValueChanged:(id)sender
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I only have two cells; 1 in each section so the cell == nil condition is always false.
Issue:
When the tableView loads the console logs:
0
1
When I change the value of the segmented control, I still get:
0
1
After some more tries, I basically have height of the second view (for the second index) increasing. I can't seem to get a clue why this is really happening.
EDIT:
Other pieces of code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
// test detail view is a constant height
if (indexPath.row == 0)
return 180.0;
}
else if (indexPath.section == 1)
{
// based on the view loaded from the viewForOptions array
if (indexPath.row == 0)
{
return ((UIView *)self.viewsForOptions[self.selectedIndex]).frame.size.height;
}
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *DetailCellIdentifier = #"DetailCell";
static NSString *InfoCellIdentifier = #"InfoCell";
UITableViewCell *cell;
if (indexPath.section == 0)
{
// displays views and test button
if (indexPath.row == 0)
{
cell = [self cellForTestDetailWithCellIdentifier:DetailCellIdentifier forIndexPath:indexPath inTableView:tableView];
}
}
else if (indexPath.section == 1)
{
// display the view for information based on segmented control
if (indexPath.row == 0)
{
cell = [self cellForInfoWithCellIdentifier:InfoCellIdentifier forIndexPath:indexPath inTableView:tableView];
}
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)viewDidLoad
{
// set the selected index for the options segmented control
self.selectedIndex = 0;
// instantiate all the views for test segmented control options
self.aViewController = [[AViewController alloc] ...];
self.bViewController = [[BViewController alloc] ...];
self.cViewController = [[CViewController alloc] ...];
// add all the views to an array that will be used by the tableview
self.viewsForOptions = #[self.aViewController, self.bViewController, self.cViewController];
}
Your 0,1 to 1,1 is caused by the table view reuse mechanism. Basically, the table will not reuse any cell which is already in use. So when you first populate and first refresh the table, new cells will be created. After that, there are enough cells in the reuse queue that aren't being used that no new ones should need to be created (scrolling May create a couple more).
Your height issue could be caused by auto resizing / layout. When you add the subview you should specify what size it should be and how that size should be changed as the superview (the cell) size is changed. And the cell size is changed (log it when you add the subview).
The height of the cell is one part. Usually you would want to set:
UIView *subview = self.viewsForOptions[self.selectedIndex];
subview.frame = cell.contentView.bounds;
[cell.contentView addSubview:subview];
So that when the cell is resized the subview will have the correct size. But this depends on your auto resizing rules. If you set a layout constraint to pin the height and width then you wouldn't need to set the frame.
In either case, you need to specify what happens to the subview frame when the superview frame changes.
Your issue, I guess, is that the cell is resized before being reused and your subview is still attached. So, it gets resized too. Then, in heightForRowAtIndexPath: you use the height of the subview (now invalid, try logging it) to set the height of the row.
I'd look at changing the implementation of heightForRowAtIndexPath: to use a configuration based on the selected segment rather than the subview frame height.
I have a table view of custom cells and some buttons in each cell.Clicking on any of the button inside the cell will reveal another custom view below that cell.Next click on the same button will collapse the view and need this same for all cells.I tried with insertrow method on the button click but in vain.How can i do this with using only the table view delegates.
This is what i tried:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"CustomCell_For_Dashboard";
CustomCellFor_Dashboard *customCell = (CustomCellFor_Dashboard *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellFor_Dashboard" owner:self options:nil];
customCell = [nib objectAtIndex:0];
}
[customCell.howyoulfeelBtn addTarget:self action:#selector(buttonclicked:) forControlEvents:UIControlEventTouchUpInside];
customCell.nameLabel.text = #"test";
customCell.imgView.image = [UIImage imageNamed:#"Default.png"];
// customCell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return customCell;
}
-(void)buttonclicked:(id)sender{
NSIndexPath *indexPath = [myTable indexPathForCell:sender];
[myTable beginUpdates];
NSIndexPath *insertPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:insertPath] withRowAnimation:UITableViewRowAnimationTop];
}
can anyone help me?
I got the same task on one project with just one thing different: There were no buttons, just tapping on cell will expand or collapse it.
There are several things you should edit in your code. First, the button method code will look something like this:
- (void) collapseExpandButtonTap:(id) sender
{
UIButton* aButton = (UIButton*)sender; //It's actually a button
NSIndexPath* aPath = [self getIndexPathForCellWithButtonByMagic:aButton];
//expandedCells is a mutable set declared in your interface section or private class extensiont
if ([expandedCells containsObject:aPath])
{
[expandedCells removeObject:aPath];
}
else
{
[expandedCells addObject:aPath];
}
[myTableView beginEditing];
[myTableView endEditing]; //Yeah, that old trick to animate cell expand/collapse
}
Now the second thing is UITableViewDelegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Key thing here is to determine if your cell should be expanded/collapsed and return right height in delegate method.
Going off of what #eagle.dan.1349 said, this is how to do it on the clicking of the cell. In storyboard, you also need to set the table cell to clip subviews, otherwise the content that would be hidden will show.
.h
#property (strong, nonatomic) NSMutableArray *expandedCells;
.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.expandedCells containsObject:indexPath])
{
[self.expandedCells removeObject:indexPath];
}
else
{
[self.expandedCells addObject:indexPath];
}
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat kExpandedCellHeight = 150;
CGFloat kNormalCellHeigh = 50;
if ([self.expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Saw this post and just wanted to give my 2 cents as my solution to this is very similar to the chosen answer (the tapping of a whole area).
Many people architect this by using just cells alone, but I believe there is a way to build this that might align better with what people are trying to achieve:
There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."
This is nice for a couple of reasons:
The job of headers and cells are separated which makes code cleaner.
This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.
I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header.
Link: https://github.com/fuzz-productions/FZAccordionTableView
I also had a same situation and my solution was to put a button on top of the Section Title with viewForHeaderInSection method.
noOfRows defines how many rows are there in each section and button.tag keeps which button of section is pressed.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIButton *btnSection = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
btnSection.tag = section;
[btnSection setTitle:[sectionArray objectAtIndex:section] forState:UIControlStateNormal];
[btnSection addTarget:self action:#selector(sectionButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
return btnSection;
}
- (void)sectionButtonTapped:(UIButton *)button {
sectionIndex = button.tag;
if (button.tag == 0) {
noOfRows = 3;
} else if (button.tag == 1) {
noOfRows = 1;
} else if (button.tag == 2) {
noOfRows = 2;
}
[self.tableView reloadData];
}
Hope this will help you..