UITableView cells with buttons - iphone

I want to have a UIButton in each UITableViewCell that will allow me to perform selector on the object corresponding to that row. They way I got it working was to create a separate UITableViewCell for each row (no reuse), add a new UIButton that is tagged with the row. When the button gets tapped, the resulting selector checks the tag of the sender to determine which object to change.
Is there a better way of doing this? For one, I am not reusing cells which is unfortunate, and using UIView.tag seems very hacky.

You can use the same tag number on all of the UIButtons.
To extract the row number which has been clicked, implement this code in the selector:
- (void)buttonClicked:(id)sender
{
UITableViewCell * clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath * clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
int rownumber = clickedButtonPath.row;
}

I would make a subclass of UITableViewCell so that it stores a reference to a button. Then set that button when you dequeue a cell. In the method that is called, sender will be the button and you can ask for it's superview and then ask that (which is the cell) for it's index.

Related

Determine UIButton in UITableViewCell

I have UIButton in every UITableViewCell. How can I check in which cell is button pressed?
Button is added in custom class of UITableViewCell (not in cellForRowAtIndexPath).
How it works: I have thumbUp in every UITableViewCell (grouped Tableview) and I need to know for which cell is thumbUp pressed. I can see max 2 cells at a time (because cell height is 400). I tried increment counter every time cellForRowAtIndexPath is called. It works for 1st cell but when I scroll down and cellForRow is called for second cell, counter is raised, but thumbUp button of first cell is still visible and if I press that thumbUp, it will be like 2nd cell thumbUp is pressed. Hope you understand.
Need help !!! Thanks
try this
- (IBAction)buttonWasPressed:(id)sender
{
NSIndexPath *indexPath =
[self.myTableView
indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
NSUInteger row = indexPath.row;
// Do something with row index
}
or give tag to every UIButton in cellForRowAtIndexPath method
and get cell object
- (IBAction)buttonWasPressed:(id)sender
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:sender.tag];
}

Implementing Prev./Next buttons in UITableView to make first responder a UITextField in a non-visible cell

in my app I have a UITabeView with UITextFields inside every cell. However I'm having troubles implementing previous/next buttons to make the text field inside the previous/next UITableViewCell be the first responder.
I've subclassed the UITableViewCell class to have it call a certain method of it's delegate when the prev./next buttons are pressed and to pass the cell itself as a parameter of this method (so I can get its index path to calculate which is the index path of the cell whose text field has to be made first responder)
In the implementation of the delegate's method I:
get the index path of the cell whose button was pressed
add or subtract 1 from the index path of that cell (depending on which button was pressed)
get the cell whose textfield has to be made first responder using the -cellForRowAtIndexPath: method on the table view
make the text field first responder
Problem is that the -cellForRowAtIndexPath: method returns the cell only if it is visible. So when the cell is not visible it will return nil and the above algorithm will not work while when the cell is on screen it will work correctly.
Here is my code for the prev. button, provided that MUInfoMateriaTableViewCell is my subclass of UITableViewCell and that it has a textField property that returns its text field:
- (void)prevButtonPressedInCell:(MUInfoMateriaTableViewCell *)cell
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSIndexPath *previousIndexPath = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section];
MUInfoMateriaTableViewCell *newCell = (MUInfoMateriaTableViewCell *)[self.tableView cellForRowAtIndexPath:previousIndexPath];
[newCell.textField becomeFirstResponder];
}
Is there any way to "get" a cell that is not visible so I can make its text field the first responder? Or can you suggest me another algorithm to work around this problem?
You can fix this with a multi-step process:
keep track of the cell that contains the desired text field to make first responder
calculate the NSIndexPath of the cell to be shown and call [self.tableView scrollToRowAtIndexPath:atScrollPosition:animated:] to bring it into view
implement tableView:willDisplayCell:forRowAtIndexPath: to call becomeFirstResponder on the desired cell when it becomes visible and it matches the desired cell or index path
The last step is important because calling becomeFirstResponder doesn't do anything if the receiver isn't a subview of any window.
Make the cell visibile by scrolling the table view, using scrollToRowAtIndexPath:atScrollPosition:animated:.
For instance
- (void)prevButtonPressedInCell:(MUInfoMateriaTableViewCell *)cell
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSIndexPath *previousIndexPath = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section];
[self.tableView scrollToRowAtIndexPath:previousIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
MUInfoMateriaTableViewCell *newCell = (MUInfoMateriaTableViewCell *)[self.tableView cellForRowAtIndexPath:previousIndexPath];
[newCell.textField becomeFirstResponder];
}
Problem is that the -cellForRowAtIndexPath: method returns
the cell only if it is visible.
That's because the cell doesn't exist when its row isn't visible. UITableView only keeps those cells it needs to draw the table. That's why you get lots of -tableView:cellForRowAtIndexPath: messages when you scroll the table -- the table is asking its data source to provide the cells that it doesn't have.
If you want to use your current approach, you'll need to scroll the table so that the row about to be edited becomes visible, as demonstrated in Gabriele Petronella's answer.

iOS: UITableView clarifications

How can I eliminate views created upon selecting a cell in UITableView when the cell is no longer selected or other cell is on selection.
Suppose I have the code below for didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize cellSize = [tableView cellForRowAtIndexPath:indexPath].frame.size;
UIView *selectionView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, (int)cellSize.width, (int)(cellSize.height + 100))];
selectionView.layer.borderWidth = 2;
[[tableView cellForRowAtIndexPath:indexPath]addSubview:selectionView];
}
Now, I want to remove that created selectionView when I focus on another cell and create it again to the cell which I am focusing.. The problem is that when I select a cell for the first time, it works perfectly but when I select another cell, the selectionView created from the previous cell still does not disappear and it duplicates the view already. How am I suppose to solve this? Need suggestion.. :( thanks..
You need to add tag for the selectionView as follows
selectionView.tag = 100;
Also , you need to have the reference of the last selected indexPath by declaring a NSIndexPath class member and retaining it.
So while selecting a new cell, get the cell with last selected indexpath and remove the view from the cell as follows
UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:lastSelIndexPath];
UIView *view = [lastCell viewWithTag:100];
[view removeFromSuperview];
You really need to look into subclassing UITableViewCell if you want a custom selection UI. Then override [UITableViewCell setSelected:animated:] (or [UITableViewCell setHighlighted:animated:]) to perform your customisations.

UISegmentController added to a cell

I have a tableView, and in that i have a UISegmentController. So each cell will have a UISegmentController each.
When the user clicks on a Segment in the UISegmentController (of a given cell), how do i know which cell was clicked? and i need to NSLog the title of that cell, how can i do this (note: the user will only be clicking on the UISegmentController of the cell, and not the cell it self)
The following code is the method that will be called when the UISegmentController is clicked;
-(void)segmentOfCellPressed:(id)sender{
}
Try this:
-(void)segmentOfCellPressed:(id)sender{
UISegmentController *segmentController = (UISegmentController *)sender;
YourCellClass *cell=(YourCellClass *)segmentController.superview.superview; // your clicked cell
// or a little bit more verbose but imho easier to understand:
// UIView *contentView = [segmentController superview];
// YourCellClass *cell = (YourCellClass *)[contentView superview];
NSIndexPath *path = [yourTableView indexPathForCell:cell]; //indexPath of clicked cell
}
Should be pretty easy. UISegmentedController derives from UIControl which derives UIView. UIView objects have a tag property. When you create each UISegmentedControler, give each one a unique tag value. You will need to manage which tag values separately in a dictionary perhaps.
-(void)segmentOfCellPressed:(id)sender
{
UISegmentedController *segmentedController = (UISegmentedController *)sender;
UITableViewCell *cell = [self cellFromTag:segmentedController.tag];
}
You will need to create a method called cellFromTag that will return the cell from the tag value of the UISegmentedController. If you don't want the cell but something else, then you can return that instead.
You could set the tag property of the segment controller to be the index of the cell in the cellForRowAtIndexPath then in the selector you could query the segment controller and then get the cell by the tag:
UISegmentController *segmentController = (UISegmentController*)sender;
int row = segmentController.tag;
Hopefully that helps.
In the cellForRowAtIndexPath method where you add the UISegmentController to the cell do the following:
segment.tag = indexPath.row;

Scroll to cell then get subview

I have cells in a table with custom fields that allow entry of numbers using a custom numpad. That includes a tab. When tabbing from the bottom of the table to the top, I need to (i) scroll the table to the top and (ii) select the custom field in that table.
I'm finding that if I call cellForRowAtIndexPath directly after I've called scrollToRowAtIndexPath, the former returns a null cell. If called again, after the scroll has completed, the cell is returned as expected. I need the cell intself so I can get the custom view by tag value, and select it.
I have been trying to find a solution. One might be to insert a delay after the scroll to allow it to complete. But I've tried using performSlector:withObject:afterDelay without success.
This is my code called when the tab is pressed.
-(void) customDualViewTabbed:(CustomDualView *)dualView {
UITableViewCell *cell=(UITableViewCell*)([dualView superview].superview);
NSIndexPath *fromIndexPath=[self.tableView indexPathForCell:cell];
NSIndexPath *toIndexPath;
if ((fromIndexPath.row+1)<[self.tableView numberOfRowsInSection:fromIndexPath.section]){
toIndexPath = [NSIndexPath indexPathForRow:(fromIndexPath.row+1) inSection:fromIndexPath.section];
}
else {
toIndexPath = [NSIndexPath indexPathForRow:0 inSection:fromIndexPath.section];
[[self tableView] scrollToRowAtIndexPath:toIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
//get cell we tabbed to
cell=[self.tableView cellForRowAtIndexPath:toIndexPath];
CustomDualView *dualView2 =(CustomDualView *)[cell viewWithTag:dualView.tag];
[dualView2 setSelected:(SelectedState)SelectedLeftTabbed];
}
The cells in a UITableView are only loaded as needed. As a result, if the cell that you are scrolling to resides at an index that currently off-screen, you will get a nil response from cellForRowAtIndexPath.
Now, since UITableview conforms to the UIScrollViewDelegate protocol, you can implement those delegate methods in addition to the UITableViewDelegate methods. Implementing one that you know will be called when your cell is in view, like scrollViewDidEndDecelerating:, and then making your call to cellForRowAtIndexPath might do the trick.