Getting NSIndexPath of a row for UISwitch - iphone

I have a tableview with custom UITableViewCells, each row in the table has a UILabel, UISwitch and detail disclosure indicator.
I'm looking for the best method of capturing the UIControlEventChanged for the switch but I also need the NSIndexPath of the switch that changed to update Core Data.
I don't want the UISwitch as an accessory type either.
I've been googling this for hours and the solution that keeps popping up is to use
[switch addTarget:self action:#selector(switchTapped:) forControlEvents:UIControlEventChanged];
then in the switchTapped: method use the following to get the NSIndexPath
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
Is there a better way of doing this? I already have UITableViewCell subclassed, can I put a method in this class to return the NSIndexPath for a given switch?
Thanks in advance for any help

I'll take another opportunity to link to my answer here - tags and view hierarchy walking are clunky, error prone and unnecessary, and I see them recommended all over the place in SO answers. You can find the index path of any control using its frame and the table view's indexPathForRowAtPoint: method.

Setting a proper tag for the switch in each row of the table is a feasible solution. Set the tag according to the indexpath of the row in which it is present.
That way you avoid the possible problems that might arise to changes in view hierarchy.
This post might also help you.

Related

Programmatically move/animate a UITableView row from one position to another

I have a UITableView and I want to programmatically move one row from position N1 to position N2 and I would like it to animate from the old location to the new. I've looked through the UITableView documentation and I'm only seeing inserts, reloads, and deletes. Do you know of a way I can do this programmatically?
A couple of notes:
I know that I can animate the deletion from location N1 and animate the insertion to location N2 at the same time. That's my fallback, but I'd like the user to understand that it is truly moving from N1 to N2.
I'm not talking about allowing the user to drag it from one place to another. I understand how to do that, I'm looking for a way to initiate and animate the move programmatically.
I've dealt with something kind of like this in the past and the solution is not pretty but it does work.
Basically you have to do some math to calculate where on the screen the row is. Then adjust that based on the y property of the contentOffset of your UITableView.
You can do that like this:
[myView convertPoint:localPosition toView:nil];
Then you would construct a new UIView that's identical to your view. Set its frame to be directly on top of the UITableViewCell you're trying to move. Add it as a subview of your app's main UIWindow.
You'll want to set userInteractionEnabled or scrollEnabled to NO on your UITableView while all of this is going on so that the user can't muck with the position of things throughout the process (just remember to set it back to YES when everything is done animating).
Then animate it to the new position however makes the most sense.
I understand you may have to scroll the UITableView in the process of all of this, which complicates things considerably, but you can animate that scrolling as well.
You'll of course have to do similar operations on the row you're animating to if it is necessary in your app.
Like I said its not easy or pretty. But I do understand what you're trying to accomplish and I think the effect is super awesome when it's pulled off properly.
If anyone has a better idea of how to pull this off without going insane I'd love to know, but this type of implementation is the best I've been able to do.
[table beginUpdates];
[table moveRowAtIndexPath:indexPath toIndexPath:destIndexPath];
[table endUpdates];
If you're using iOS 5 or hight you could use the method:
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
For example:
//move last cell one row up.
NSIndexPath *oldPath = [NSIndexPath indexPathForRow:[items count] - 1 inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:[items count] - 2 inSection:0];
[[self tableView] moveRowAtIndexPath:oldPath toIndexPath:newPath];
Check out apple's documentation here.
I guess your best fallback would indeed be the deletion and insertion method, something like:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:oldPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newNewPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView endUpdates];

TableView's viewWillAppear() called but data is not refreshed

Sometimes tiny looking problem is ignored to handle in the last but you never know that will become nightmare for you, happening with me.
Thanks in advance, problem my app's settings page contains tableView and every setting element is one of the view. One of the setting item (row) offers show expands a few list of items in another listTableView. After making a selection in listTableView when i come back to my settings tableView using navigationItem.leftButton (Back), selected item value overlaps the previous item's value, in the sense [tableView reloadData] in viewWillAppear works fine but tableView rows are not fresh drawn. It doesn't refresh the cell's UI.
Note that if settingTableView has any subview like UIButton etc it has the latest value, i can use that as workaround but the only problem is when is select this row again Selection has old value that overlaps new value on selecting the row.
I will appreciate any suggestion or sample code using will be great help.
Thanks a ton
Amit Singh
Use [tableView reloadData]; in your viewWillAppear method.
or use [tableView reloadData]; in viewDidAppear method
The problem you are facing is perhaps due to portion of cell that is reusable and the one that is not reused. Check out the code you are writing inside the block of
if(cell==nil){}
components you have created inside block will not get recreated and others might be recreating causing the overlapping on the cell.
In my case, I had to use [self.tableView reloadData]; rather than [tableView reloadData]; in the viewWillAppear method.

Change Height of UITableViewCell from within the UITableViewCell inherited class when UITextField receive focus

I have a inherited UITableViewCell class from which I create a custom cell containing a UITextField.
The UITextField is 25 pixel height by default.
The behavior I want is that when the user clicks in the textField, the UITextField should change to 100 pixel height and the cell should grow accordingly.
I can detect when the UITextField receive focus thanks to notifications and observers but I wonder how to programmatically make that tableView:HeightForCellAtindexPath: be called.
Like Endemic says, tableView:heightForCellAtIndexPath: is the method you need to implement. According to Apple, the most efficient way to trigger a resize is an empty beginUpdates / endUpdates block, like this.
[tableView beginUpdates];
[tableView endUpdates];
It saves you the overhead of reloading the cell contents and, I believe, gives you a nice animation you wouldn't otherwise get from reloadData.
You must have a link between the cell and the table view controller. Since you are already creating your custom cell in your controller the easiest way would be to use the delegate pattern.
#class CustomTableViewCell;
#protocol CustomTableViewCellDelegate
- (void)customTableViewCellDidEnterTextMode:(CustomTableViewCell *)cell;
#end
#protocol (nonatomic, assign) id<CustomTableViewCellDelegate> delegate;
and just call the delegate method where you are detecting when the text field gets focus
[self.delegate customTableViewCellDidEnterTextMode:self];
and in the controller
- (void)customTableViewCellDidEnterTextMode:(CustomTableViewCell *)cell {
self.editingIndexPath = [self.tableView indexPathForCell:cell];
// from Jablair's answer
[tableView beginUpdates];
[tableView endUpdates];
}
And then in tableView:heightForCellAtIndexPath: just return your special height for self.editingIndexPath.
You would probably have to include another delegate method to know when focus is leaving the text field as well.
Another approach would be to use notifications but that will just complicate your code and if there is only one receiver of the message a delegate is the preferred way. A third approach would be to set the delegate of the text field to your controller instead of to your cell.
The bottom line, you need to provide the link between the cell and table view your self and I believe using a delegate pattern is the best approach.
The tableView:heightForCellAtIndexPath: method is called whenever the table view loads data, so simply calling reloadData (or one of the other, more selective reload methods) on the table view should work fine.

How to auto-scroll UITableView?

I am trying to do something interesting.
I am pulling some JSON data and populating cells in a UITableView. How can I make the UITableView scroll ever second or so? I want to give the effect that as new data is coming in, the table is scrolling, so it is streaming.
Any ideas?
Yes, use scrollToRowAtIndexPath:atScrollPosition:animated:.
You could also use UIScrollView's scrollRectToVisible:animated: if you want to have more finegrained control and can calculate exactly what your scroll position should be in pixels. (UITableView is subclass of UIScrollView.)
But if you are just adding cells, sounds like you could also just use insertRowsAtIndexPaths:withRowAnimation:, this is also "interesting."
Just do this
[table reloadData];
NSIndexPath *myIP = [NSIndexPath indexPathForRow:[arrayIn count]-1 inSection:0] ;
[table scrollToRowAtIndexPath:myIP atScrollPosition:NULL animated:YES];
in anywhere where arrayIn is your array by which your table populate.
you can use uitableviews scrollToRowAtIndexPath to do this....heres a link reference
You can call this method whenever as youd like to scroll your tableview
And you can call performSelector:withObject:afterDelay: for the timer effect - although if you are really pulling in data every second, it's best to just reloadData as you go.

UISegmentControl and UITableViewController animation

How can I animate my "removal of tableviewitems" when the user changes between segments in the UISegmentControl?
The behaviour should be similar to "Missed/All" calls in the Phone App.
The deleteRowsAtIndexPaths:withRowAnimation: and insertRowsAtIndexPaths:withRowAnimation: will provide the animation for you.
If you need to perform a more complex operation (inserts and deletes), you start a block. similar to a UIView animation block:
[tableView beginUpdates];
//add and delete
[tableView endUpdates];
Bear in mind you will need to update the model to reflect the changes in the table.
UITableView can be sent a message: deleteRowsAtIndexPaths:withRowAnimation:
The withRowAnimation argument determines the type of animatio that will be used in removing the cell.
I don't know if any of those animations match the missed/all functionality exactly though. If they don't, I'm guessing you will have to set up an animation to collapse the height of the cells before removing them.