I am having trouble animating a custom UITableView section header.
The goal was to create collapsable sections.
When I tap on the custom header the first time it animates as expected, however every time after that it leaves a duplicate in the original location and animates another.
Image Example:
My Custom Header:
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView* customView = [[[UIView alloc]initWithFrame:CGRectMake(10.0, 0.0, 300.0, 44.0)]autorelease];
customView.backgroundColor = [UIColor lightGrayColor];
UILabel * headerLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor darkGrayColor];
headerLabel.font = [UIFont boldSystemFontOfSize:16];
headerLabel.frame = CGRectMake(10, 7, 260.0, 44.0);
headerLabel.textAlignment = UITextAlignmentCenter;
NSDictionary *dictionary = [self.data objectAtIndex:section];
headerLabel.text = [dictionary objectForKey:#"Title"];
[customView addSubview:headerLabel];
// add button to right corner of section
UIButton* headerButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 0, 320, 44)];
headerButton.center = CGPointMake( 160.0, 22.0);
headerButton.backgroundColor = [UIColor clearColor];
headerButton.tag = section;
[headerButton addTarget:self action:#selector(expandSection:) forControlEvents:UIControlEventTouchUpInside];
[customView addSubview:headerButton];
return customView;
}
My Animation Method:
- (void) expandSection:(id)sender {
if (expandedSection == [sender tag]) {
expandedSection = -1;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else if (expandedSection == -1){
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else{
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:expandedSection] withRowAnimation:UITableViewRowAnimationNone];
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
//[self.tableView reloadData];
}
I am not exactly sure whats going on, but the instances suggest that I need to dealoc something. I've tried a few things but I can't figure this out. Anyone help on this would be great!
Edit: I believe the problem is that reloadSections is causing the custom view to instance. I can't release the view because I need it as a reference to do the update for the animation. Any ideas on what I can do to fix this?
Solution found.
The table needed to be reloaded before every change. This way the table is at the latest state before making any changes.
add
[self.tableView reloadData];
as the fist entry in the "expandSection" method.
CODE:
- (void) expandSection:(id)sender {
[self.tableView reloadData];
if (expandedSection == [sender tag]) {
expandedSection = -1;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else if (expandedSection == -1){
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else{
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:expandedSection] withRowAnimation:UITableViewRowAnimationNone];
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
//[self.tableView reloadData];
}
I had a similar issue which was caused by using dynamic height cells. I had an expandable custom header view and when I was updating tableView to insert and remove the associated rows of the section (meaning that they were expanding, respectively collapsing), the section header which was a subclass of UITableViewHeaderFooterView was not recycled. So basically a new one was allocated and added over the old one resulting in overlapping views. The cell identifier was properly set so must have been something else. When I removed tableView.sectionHeaderHeight = UITableViewAutomaticDimension and implemented func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat the view got properly recycled and only one header view was displayed for each section.
Another solution that I turned out to actually work was to use UITableViewCell instead of UITableViewHeaderFooterView and when you return in func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? you just return cell.contenView, this will work because the method requires to return a UIView and since the contentView of the UITableViewCell is a UIView it works just fine. The idea behind is to use the recycling mechanism of UITableView through UITableViewCell and just return its content after you configure it.
Conclusion. The problem it is very possible to be caused by UITableViewHeaderFooterView when is used with self sizing tableView cells and UITableViewAutomaticDimension instead of manually calculating cell height.
Related
I need to set different styles for cells that I have inserted compared to reloaded ones.
I am inserting my cells like the following:
[tempArray addObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[[self tableView] beginUpdates];
[[self tableView] insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
[[self tableView] insertRowsAtIndexPaths:(NSArray *)tempArray withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
Is there a way for me to do the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (cell was inserted) {
cell.mylabel.textColor = [UIColor redColor];
} else {
cell.mylabel.textColor = [UIColor blackColor];
}
}
As is often the case for maintaining the state of a table view cell, the right answer is to keep the state in your model. In other words, if tempArray is your model containing a collection of objects that describe the table's contents, add a BOOL attribute to those objects meaning something like userAdded.
Then your "cell was inserted" pseudo-code can become:
MyModelClass *modelElement = [tempArray objectAtIndex:indexPath.row];
if (modelElement.userAdded) {
cell.mylabel.textColor = [UIColor redColor];
} else {
cell.mylabel.textColor = [UIColor blackColor];
}
I have a tableview with a custom tableviewCell. In this tableview cell I have a button for deleting a row.
First of all I create this tableview by the following code.
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 70, 320, 150) style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor blackColor];
tableView.separatorStyle = normal;
[tableView reloadData];
I have an appointments array that I use as my datasource. In my cellForRowAtIndex I use the following code to attach my button to an action.
cell.btnDelete.tag = indexPath.row;
[cell.btnDelete addTarget:self action:#selector(deleteAppointment:) forControlEvents:UIControlEventTouchUpInside];
And then I have my action itself.
-(void)deleteAppointment:(id) sender{
NSLog(#"till here");
UIButton *button = (UIButton *)sender;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
NSLog(#"indexpathc: %d",indexPath.row);
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject:indexPath]
withRowAnimation: UITableViewRowAnimationFade];
[_exceptions removeObjectAtIndex:indexPath.row];
[tableView endUpdates];
}
It gives back the right index path in my log but it don't delete the row inside my tableview. Can anybody help me?
Kind regards
You also need to update the method numberOfRowsInSection. For example if you are keeping your elements in an array, delete one element from it and return updated array in numberOfRowsInSection.
I assigned custom action for edit button.
"My query is when i press the edit button it will call the customactionmethod (purpose of this method is to edit the text in textview and store the current values in array).
My requirement is after edited again it will back to didselctrowatindex method.How can i back to didselctrowatindex from customaction method.here is my code
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//The user is selecting the cell which is currently expanded
//we want to minimize it back
if(selectedIndex == indexPath.row)
{
selectedIndex = -1;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
return;
}
//First we check if a cell is already expanded.
//If it is we want to minimize make sure it is reloaded to minimize it back
if(selectedIndex >= 0)
{
NSIndexPath *previousPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
selectedIndex = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:previousPath] withRowAnimation:UITableViewRowAnimationFade];
}
//Finally set the selected index to the new selection and reload it to expand
selectedIndex = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[button setBackgroundImage:[UIImage imageNamed:#"slider2.png"] forState:UIControlStateNormal];
labelname.textColor = [UIColor yellowColor];
passionnameslabel.textColor=[UIColor yellowColor];
textview1.font = [UIFont fontWithName:#"Helvetica" size:15.0f];
textview1.text = [textview1.text stringByAppendingString:[mylifearray objectAtIndex:indexPath.row]];
textview1.delegate=self;
[pes setInteger:selectedIndex forKey:#"selected"];
[editbutton addTarget:self action:#selector(customActionPressed:)forControlEvents:UIControlEventTouchUpInside];
[editbutton setBackgroundImage:[UIImage imageNamed:#"edit.png"] forState:UIControlStateNormal];
}
-(void)customActionPressed
{
textview1.inputView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
textview1.editable=YES;
mylifearray=[NSMutableArray arrayWithArray:[pes objectForKey:#"mylifearray"]];
[mylifearray removeObjectAtIndex:selectedIndex];
[mylifearray insertObject:textview1.text atIndex:selectedIndex];
[pes setObject:mylifearray forKey:#"mylifearray"];
}
Can any body Please help Thanks in advance
You can call the delegate method explicitly:
[self tableView:self.tableView didDeselectRowAtIndexPath:indexPath];
Or else, you can also write a method that performs the same action as in didDeselectRowAtIndexPath and call it as and when you need.
I have a table view that has many cells. Each cell has its own UITextField. I added the text fields programmatically. I want each and every textField to appear when the edit button is hit. (now table is in edit mode) and when pressed again, i want all of the textFields to vanish(leaving the edit mode). I know that i can accomplish this using the hidden property, but i tried doing this in this method:
- (IBAction)editButton:(id)sender
{
if (self.editing)
{
[self setEditing:NO animated:YES];
[self.myTableView setEditing:NO animated:YES];
EditButton.title = #"Edit";
cellText.hidden = YES; //<-- THIS IS THE CODE
}
else
{
[self setEditing:YES animated:YES];
[self.myTableView setEditing:YES animated:YES];
EditButton.title = #"Done";
cellText.hidden = NO; //<-- THIS IS THE CODE
}
}
but it only shows and hides the VERY LAST cell's textField. How can i get it to where it shows and then doesnt show EVERY cell's textFIeld? Many thanks in advance!!!
CELL FOR ROW
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cellText = [[UITextField alloc]init];
[cellText setFrame:CGRectMake(190, 15, 55, 30)];
cellText.text = #"1";
cellText.borderStyle = UITextBorderStyleRoundedRect;
cellText.hidden = YES;
cellText.userInteractionEnabled = NO;
[cell addSubview:cellText];
}
return cell;
}
Thanks in advance!! :D
You can get rid of this issue, using this trick, I am not sure as if it would create the memory leak within your code.Since, it create new cell each time.But surely you can use it,if you don't get some proper way of doing it. ;)
- (IBAction)editButton:(id)sender
{
if (self.editing)
{
[self setEditing:NO animated:YES];
[self.myTableView setEditing:NO animated:YES];
EditButton.title = #"Edit";
}
else
{
[self setEditing:YES animated:YES];
[self.myTableView setEditing:YES animated:YES];
EditButton.title = #"Done";
}
[self.myTableView reloadData];
}
After reloading the TableView, check the condition in cellForRowAtIndexPath, either pass the value of self.editing to TextField which makes it hide/show.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cellText = [[UITextField alloc]init];
[cellText setFrame:CGRectMake(190, 15, 55, 30)];
cellText.text = #"1";
cellText.borderStyle = UITextBorderStyleRoundedRect;
cellText.hidden = YES;
cellText.backgroundColor = [UIColor redColor];
cellText.userInteractionEnabled = NO;
[cell addSubview:cellText];
cellText.hidden=!self.editing;
return cell;
}
Although you create a text field for each cell, you keep a reference only to the last one in the ivar named cellText. That's why you show/hide the only text field.
I suggest you reload the table when toggling the editing mode, and set the text fields visibility in tableView:cellForRowAtIndexPath:.
Oh, and you should release cellText after adding it as a subview. Otherwise you are leaking memory. And it is highly recommended that you add subviews to UITableViewCell content view, rather than directly to the cell.
Try this code
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (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] autorelease];
}
UITextField * cellText = [[UITextField alloc] initWithFrame:CGRectMake(1, 1, 100, 30)];
cellText.tag = 1;
cellText.textColor = [UIColor darkTextColor];
//cellText.numberOfLines = 0;
cellText.font = [ UIFont fontWithName: #"Helvetica-Bold" size: 12.0 ] ;
cellText.backgroundColor = [ UIColor clearColor ] ;
cellText.text = #"123";
cellText.hidden = YES;
[cell.contentView addSubview:cellText];
[cellText release];
cellText =nil;
// Set up the cell...
return cell;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
// Detemine if it's in editing mode
UITextField *cellText = (UITextField *)[[aTableView cellForRowAtIndexPath:indexPath] viewWithTag:1];
if (!self.editing)
{
[self setEditing:NO animated:YES];
[self.tableView setEditing:NO animated:YES];
// EditButton.title = #"Edit";
cellText.hidden = YES; //<-- THIS IS THE CODE
}
else
{
[self setEditing:YES animated:YES];
[self.tableView setEditing:YES animated:YES];
// EditButton.title = #"Done";
cellText.hidden = NO; //<-- THIS IS THE CODE
}
return UITableViewCellEditingStyleNone;
}
hi friend this code is working fine for me, believe you too have a grate day
This is actually normal. Per the Apple documentation under addSubview:
Views can have only one superview. If view already has a superview and
that view is not the receiver, this method removes the previous
superview before making the receiver its new superview.
So it will keep removing it adding and removing from cells until it gets to the last one.
I've a custom section header in my UITableView and I can't figure out why they are appearing bellow the UITableViewCell of the table. See the screenshots:
This is the code that creates the section header:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil) {
return nil;
}
return [LojaInfoHeaderView lojaInfoHeaderForSection:section withTitle:sectionTitle opened:[self sectionIsOpen:section] andDelegate:self];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return [LojaInfoHeaderView viewHeight];
}
And the section's cell are inserted or deleted when the user touches the section header:
- (void)lojaInfoHeader:(LojaInfoHeaderView *)lojaInfoHeader sectionDidOpen:(NSInteger)section {
NSArray *indexPathsToInsert = [self indexPathsForSection:section];
[self setSection:section open:YES];
[_tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationTop];
}
- (void)lojaInfoHeader:(LojaInfoHeaderView *)lojaInfoHeader sectionDidClose:(NSInteger)section {
NSArray *indexPathsToDelete = [self indexPathsForSection:section];
[self setSection:section open:NO];
[_tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
How can I make the section header appears above the cells? How to fix it?
Update to show how things are created
These are the class methods I'm using:
+ (CGFloat)viewHeight {
return 44.0;
}
+ (LojaInfoHeaderView *)lojaInfoHeaderForSection:(NSInteger)section withTitle:(NSString *)title opened:(BOOL)isOpen andDelegate:(id<LojaInfoHeaderDelegate>)delegate {
LojaInfoHeaderView *newHeader = [[[LojaInfoHeaderView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
newHeader.section = section;
[newHeader setTitle:title];
newHeader.delegate = delegate;
[newHeader setOpen:isOpen animated:NO];
return newHeader;
}
I found the problem. I was setting the backgroundColor using alpha (yeah, I can't believe I miss this).
Wrong code in initWithFrame:
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1];
Correct code:
self.backgroundColor = [UIColor colorWithRed:0.89 green:0.89 blue:0.89 alpha:1.0];
Try to change your whole table style to be grouped instead of plain. Or change your section view to be opaque. Whatever is the design requirement.