Scroll to cell then get subview - iphone

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.

Related

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.

UITableView in customCell hiding one button also hide the next button of other cells

e.g as you are watching the tableView having cell with plus Button, when I press that button it hides the current button but when I scroll the tableview Some other button on other customCells are also get hide but I didn't hide them. please help me out. How to fit this problem.
The UITableView is caching the cells, which means it doesn't store all cells at all time, which is brilliant in terms of memory. The problem with it, that it has no reference to the value stored in that cell when the cell is reused. What is often done, is that all values stored in a dynamic cell is stored in separate NSMutableArrays.
For your problem, you could add an array with the boolean values indicating whether they are hidden or not, and read from that in the tableView:cellForRowAtIndexPath: delegate method with
cell.hidden = [[self.yourArray objectAtIndex:indexPath.row] boolValue];
And in the button callback method you should change the hiddenproperty as well as updating the value in the array.
i'll do it as follow:
first you have to track the button's state:
Shown
or
Hidden
this is done by holding the state in an NSMutableArray
in the viewDidLoad method add the following
NSMutableArray *shownButtons = [[NSMutableArray alloc] init];
then in your tableView:cellForRowAtIndexPath do the following
NSString *tmpIndexPathString = [NSString stringWithFormat:#"%d",indexPath.row];
if ([shownButtons containsObject:tmpIndexPathString])
{
[cell.myButton setHidden:YES];
}
else
{
[cell.myButton setHidden:NO];
}
In the tableView:cellForRowAtIndexPath: delegate method you need to loop through all the visible cells:
for (UITableViewCell *cell in [self.tableView visibleCells]) {
// now you have a cell that you can update
}
You also have to remember to update your data source so scrolling the table will update the cells accordingly and not show the plus button. If you're updating your data source what you can do is reload the cell in the above for loop for example.

iPhone dev - Having problems adding text to cells of a UITableView

I am trying to make an app that will display a bunch of different people's names and address in different cells of a UITableView. First I'm just trying to get the hand of adding text to the cells, and it's not really working. I have the following code, where myTableViewController is just a subclass of UITableViewController that loads in its view from a nib (without any drastic customizations):
myTableViewController * tvc = [[myTableViewController alloc] initWithStyle:UITableViewStylePlain];
NSIndexPath *path1 =[NSIndexPath indexPathForRow:0 inSection:0];
NSIndexPath *path2 =[NSIndexPath indexPathForRow:1 inSection:0];
[self presentModalViewController:tvc animated:YES];
UITableViewCell * cell1 = [tvc.tableView cellForRowAtIndexPath:path1];
UITableViewCell * cell2 = [tvc.tableView cellForRowAtIndexPath:path2];
cell1.textLabel.text = #"test";
cell2.textLabel.text = #"test2";
I was expecting to see a tableView animate onto the screen, followed by the first and second cells being filled with the text "test" and "test2" respectively. What I get is a blank tableview animating onto the screen. Then if I scroll down so that the top cell is hidden and scroll back up so that the top cell is again visible, the top cell will now have the text "test" in it. But the second cell down never shows any text, just the top one.
Can someone please point me in the right direction? I also would like to have the text in the tableview BEFORE it loads onto the screen, but it looks like the cells don't exist unless they are being displayed. Please help me.
Populating a table view takes the form of a data-source pattern, where the table view calls your data source object for data to put in individual cells.
Try reading through the Table View Programming Guide.
You should use the delegate and the datasource protocol methods.

UITableViewCell's textLabel.text is being partially substituted by ellipsis

I have a UITableView with several UITableViewcells (UITableViewCellStyleSubtitle). When the user taps on one of cells, I push a detail VC that allows the user to change the text of the cell. When I pop this ViewController, the text on the cell appears abbreviated with an ellipsis:
Before:
After:
After changing the text to "Wartyrl" and popping the detail VC.
If I tap on any other cell, the ellipsis disappears and the correct text is displayed. What might be causing this?
This is the code that changes the text of the cell (via a delegate method):
#pragma mark - FRREditTaskViewControllerDelegate
-(void) editTaskViewController:(FRREditTaskViewController *)sender
didChangeNameForTask:(FRRFlatTask *)aTask{
NSIndexPath *idx = [NSIndexPath indexPathForRow:[self.pendings indexOfObject:aTask] inSection:0];
[self.tableView cellForRowAtIndexPath:idx].textLabel.text = aTask.name;
}
BTW, this code is called before popping the detail VC and while the UITableViewController is still hidden. I don't know if this is relevant.
When the cell is created, the UILabel is only large enough to handle the initial text (it doesn't dynamically resize). Calling [self.tableView reloadData] after changing the text (end of your didChangeNameForTask method) should make it display properly.
You can call [cell.textLabel sizeToFit].
In many cases it's cleaner to instead update your internal data and call reloadRowsAtIndexPaths:withRowAnimation:. That way you don't have so many pieces monkeying with the implementation details of the cell (when you change your mind on how to lay it out). It also ensures that your real data matches the cell.
Even better in most cases is to create a UITableViewCell subclass called TaskTableViewCell. You hand it a Task and it observes the Task with KVO, managing its own layout (including calling sizeToFit when needed). That way you don't need delegate methods to tell you when it updates. When the task changes, the cell automatically updates itself. This moves all the layout issues into the cell class and out of the datasource.
It seems that the textLabel hesitates to resize its width. If its too adamant to change, just reload the tableView directly using [tableView reloadData].
If you don't want to reload the entire tableView, you can reload just a particular cell like that following.
NSIndexPath *idx = [NSIndexPath indexPathForRow:[self.pendings indexOfObject:aTask] inSection:0];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:idx] withRowAnimation:UITableViewRowAnimationFade];
This will show a nice fade animation when changing the textLabel's text of the cell.
-(void) editTaskViewController:(FRREditTaskViewController *)sender
didChangeNameForTask:(FRRFlatTask *)aTask{
NSIndexPath *idx = [NSIndexPath indexPathForRow:[self.pendings indexOfObject:aTask] inSection:0];
UITableViewCell *thiscell=[self.tableView cellForRowAtIndexPath:idx];
thiscell.textLabel.text =[nsstring stringwithFormat:#"%#",aTask.name];
[self.tableView reloadRowsAtIndexPaths: [NSArray arrayWithObject:idx] withRowAnimation:NO];//if u dont want to use reload data then use like this or use
//- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
}

UITableView cells with buttons

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.