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.
Related
I have to make a call to specific number when imageview is clicked in tableviewcell.The number to which call is made is displayed in a label beside the imageview.But I couldn't get which cell is clicked.Always its referring to the last cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(cell == nil)
{
cell =[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
lblphone = [[UILabel alloc] initWithFrame:CGRectZero];
lblphone.tag = 116;
lblphone.backgroundColor = [UIColor clearColor];
[lblphone setFont:[UIFont fontWithName:#"Helvetica" size:12]];
[lblphone setLineBreakMode:UILineBreakModeWordWrap];
[lblphone setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelButton:)];
tapGestureRecognizer.cancelsTouchesInView=NO;
[tapGestureRecognizer setNumberOfTapsRequired:1];
[lblphone addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];
[cell addSubview:lblphone];
myImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
myImageView.tag = 120;
myImageView.image=[UIImage imageNamed:#"CallImg.png"];
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageSelectedInTable:)];
[tapped setNumberOfTapsRequired:1];
[myImageView addGestureRecognizer:tapped];
[tapped release];
[cell addSubview:myImageView];
}
[myImageView setFrame:CGRectMake(10, 50,30,30)];
myImageView= (UIImageView*)[cell viewWithTag:120];
myImageView.image=[UIImage imageNamed:#"CallImg.png"];
myImageView.userInteractionEnabled=YES;
CGSize constraint5 = CGSizeMake(320, 2000.0f);
CGSize size5=[phone sizeWithFont:[UIFont fontWithName:#"Helvetica" size:14] constrainedToSize:constraint5 lineBreakMode:UILineBreakModeWordWrap];
lblphone =(UILabel *)[cell viewWithTag:116];
[lblphone setFrame:CGRectMake(45,name.frame.size.height+name.frame.origin.y,320, size5.height)];
lblphone.textAlignment=UITextAlignmentLeft;
lblphone.backgroundColor=[UIColor clearColor];
lblphone.text=[NSString stringWithFormat:#"%# ",phone ];
[lblphone sizeToFit];
}
And in the tapgesture of imageclick I am writing this :
-(IBAction)imageSelectedInTable:(UITapGestureRecognizer*)gesture
{
UIImageView *imgView = (UIImageView *) [gesture view];
UITableViewCell *cell = (UITableViewCell*)[[imgView superview]superview];
NSIndexPath *tappedIndexPath = [self.tableView indexPathForCell:cell];
NSLog(#"Image Tap %#", tappedIndexPath);
UILabel *phoneno = (UILabel *)[cell viewWithTag:116];
NSString *phoneNumber = [#"telprompt://" stringByAppendingString:phoneno.text];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
}
But it is always referncing to the last cell clicked irrespective of which cell is clicked.Couldn't understand where im going wrong ?
I see you try to access your cell,
UITableViewCell *cell = (UITableViewCell*)[[imgView superview]superview];
but you add your image view to
[cell addSubview:myImageView];
You should be adding your image view to cell.contentView.
It looks like the image view is a subview of the cell (one step down the tree), and the code that's probing for the parent is taking two steps up ...superview superview. (That latter approach is right for nib defined cells which add subviews to the cell's contentView).
Here's a safer way to climb the view hierarchy that will work no matter how the cell was built.
- (UITableViewCell *)tableViewCellContaining:(UIView *)aView {
UIView *answer = aView;
while (answer && ![answer isKindOfClass:[UITableViewCell self]])
answer = answer.superview;
return answer;
}
I think your code will probably work if you replace this line:
UITableViewCell *cell = (UITableViewCell*)[[imgView superview]superview];
with this line
UITableViewCell *cell = [self tableViewCellContaining:imgView]
for get/know Which cell is clicked, Write Following code
UITableViewCell *cell = (UITableViewCell *)[[imgView superview]superview];
and if you alSo want to get indexPath of tapped cell then write
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
EDITED :-> Another way is
Give tag of UIImageView in cellForRowAtIndexPath Method
Such like,
imageView.tag = indexPath.row;
And Use Following Code
int row = ((UIImageView*)imgView).tag; //Or// int row = ((UIGestureRecognizer *)sender).view.tag;
NSIndexPath* indexpath = [NSIndexPath indexPathForRow:row inSection:0];
UITableViewCell* cell = [table cellForRowAtIndexPath:indexPath];
Here You can get Cell of Tapped UIImageView.
This is how the structure looks like.
--- UIView
---- ScrollView
--- TableView
.
UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, -250, 320, 250)];
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];
[topView addSubview:tableView];
[self.scrollView addSubview:topView];
In the tableview I am working with a custom tableview cell with a button in it. Here is the code for the button in my CellForRowAtIndex
[cell.btnDelete addTarget:self action:#selector(deleteAppointment:) forControlEvents:UIControlEventTouchUpInside];
Now to get the specific row I do this in my deleteAppointment
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
NSLog(#"row: %d",indexPath.row);
But it is still giving the following error.
-[ExceptionCell indexPathForCell:]: unrecognized selector sent to instance 0x210a2fa0
Can anyone help me?
its really Simple, in cellForRowAtIndexPath. just tag your button like
cell.button.tag = indexPath.row;
remove the value from array or dictionary (thats your dataSource) in button #selector method
[array removeObjectAtIndex:button.tag];
Now reload the tableView.
[tableView reloadData];
The error says you are trying to call indexPathForCell on ExceptionCell. indexPathForCell is a method on UITableView, not UITableViewCell.
cell.superview is not returning your UITableView as you expect.
What you want when deleting a Row from a TableView is to keep the Intuitive Animation that gives the method DeleteRowsAtIndexPath, I have been around that problem, and finaly found an EASY solution.
As the question asked, we are using a Custom Cell with a Custom UIButton for Deleting the Cell
So, you'll have to use delegate // CallBack.
That following demonstration is for MonoTouch in C#, it's the same in objective C, but methods are named a little bit differently + syntax.
//TableViewController
//CALLBACK Methods WHEN DELETING A ROW
public void DeletedItem (MyObject arrayObject, CustomCell cellInstance)
{
NSIndexPath[] arrayPath = new NSIndexPath[1] { this.myTableView.IndexPathForCell (cellInstance) };
this.myArray.RemoveItem (arrayObject);
this.myTableView.DeleteRows (arrayPath, UITableViewRowAnimation.Fade);
}
[Export("tableView:cellForRowAtIndexPath:")]
public virtual UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
...
cell.FactoryMethodsThatImUsing (myObject.items[indexPath.Row], DeletedItem);
return cell;
}
//CustomCell
public delegate void FOODelegateMethods (MyObject item, CustomCell instance);
public event FOODelegateMethods ItemDeleted;
IN MY Factory Method
if (!(this.ItemDeleted is Delegate)) {
this.ItemDeleted += new CustomCell.FOODelegateMethods (ResultCallBack);
}
And then on the DeleteItem Actions
partial void DeleteItem (NSObject sender)
{
ItemDeleted(arrayObject, this);
}
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 add button in UITableView cell. My code is
UIButton *btnView = [[UIButton buttonWithType:UIButtonTypeContactAdd] retain];
btnView.frame = CGRectMake(280.0, 8.0, 25.0, 25.0);
//[btnView setImage:[UIImage imageNamed:#"invite.png"] forState:UIControlStateNormal];
[btnView addTarget:self action:#selector(action:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btnView];
[btnView release];
My button action is
- (void)action:(id)sender
{
}
I want to know, how to get current table row index when click. I want to delete table row when click on button.
Edit::
I found a way for delete with animation
[listOfItems removeObjectAtIndex:indexPath.row];
[self.tableview deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
I believe one approach is to store the row number in the button tag
btnView.tag = [indexPath row]
in the action
- (void)action:(id)sender
{
UIButton * btn = (UIButton)sender;
int row = btn.tag;
}
I usually use this method:
- (IBAction)buttonAction:(id)sender {
UIButton *button = sender;
UIView *contentView = [button superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
//Do something
}
Depending on the layout of your UITableViewCell it is possible that you have to traverse deeper into your TableViewCell Views.
http://nshipster.com/associated-objects/ Use an associated object of a NSNumber surrounding the row or the index path itself as the associated object
//when you create the button
objc_setAssociatedObject(button, associationKeyForButton, [NSNumber numberWithInteger:indexPath.row], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//in the touch up inside handler to get the row back.
NSNumber* rowNumber = objc_getAssociatedObject(sender, associationKeyForButton);
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.