I am attempting to use a storyboard/segue to handle the transition between a UITableView with both standard transition as well as detail disclosure button. Having read a few different posts on here I have set up my project this way:
Tie main segue between UITableViewCell and ViewController
Tie secondary segue from parent ViewController to new ViewController
Implement accessoryButtonTappedForRowWithIndexPath as follows:
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"DetailSegue" sender:self];
}
This works great and my prepareForSegue: sender: gets called as expected. The trouble is, I need to know the indexPath for the element selected. The segue from the UITableViewCell retrieves the indexPath like this:
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Unfortunately when I try to do that having called the accessoryButton, that returns null.
The original question I am basing some of this code off of is here: Detail Disclosure Button and Segues
Is there a method of the tableView which returns indexPath for accessoryButtons? Do I need to access the indexPath in some other manner?
You don't have to override accessoryButtonTappedForRowWithIndexPath.
In prepareForSegue, when working with a detail disclosure button, instead of:
[self.tableView indexPathForSelectedRow]
use:
[self.tableView indexPathForCell:sender]
The sender is already the detail disclosure button's cell.
The sender argument is, according to the documentation:
The object that you want to use to initiate the segue. This object is made available for informational purposes during the actual segue.
I don't see any reason why you can't use the index path as the sender instead of self, then access the index path in prepareForSegue:.
If that doesn't work, store the index path in an ivar and access that in prepareForSegue
Related
i want to use uitableview just like a uipickerview. on click a button the uitableview comes.and on clicking any cell the selected value will be shown on the button on previous page
The normal way to pass data back to another controller is to use a delegate protocol. The controller with the button would implement the protocol method(s) declared in an #protocol declaration put in the .h file of the table view controller. In the tableView:didSelectRowAtIndexPath: method, you would call the delegate method, passing the value of the clicked on cell.
rdelmar is correct, the simplest way to do this would probably be to use a delegate protocol and pass the selected value back to the previous view.
--
However, supposing you wanted to store this value outside of the table view and the picker, you could create a singleton or use NSUserDefaults to avoid having to pass the value around and potentially losing something. If this tableview/picker was in a settings menu or the like, that's what I would do.
See this for basics:
http://mobile.tutsplus.com/tutorials/iphone/nsuserdefaults_iphone-sdk/
Some code for delegation; A protocol in your tableview class:#protocol SelectionDelegate <NSObject>
-(void)setSelectedTableCellValue:(NSString*)value;#end Have your previous view confirm to and be the delegate for the tableview that you wish to display and also implement the protocol method. Now on cell selection, just retrieve the value for the cell and pass it over to your delegate via [delegate setSelectedTableCellValue:<some cell value>]; HTH.
You may want to take a look into core data, you will be able to save the data and re-access the data/value and reload in into any view controller you like. It is a very versatile tool, and once you learn it, you will be able to do so many things a lot easier. Take a look at this Tutorial
Just use this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell=[self.m_TableView cellForRowAtIndexPath:indexPath];
AppDelegate* appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.m_SavedDictionary setObject:cell.textLabel.text forKey:#"driver"];
[self.navigationController popViewControllerAnimated:YES];
}
and on ViewwillAppear method of previous view use;
if([[appDelegate.m_SavedDictionary objectForKey:#"driver"]length]>=1)
{
[m_driverButton setTitle:[appDelegate.m_SavedDictionary objectForKey:#"driver"] forState:UIControlStateNormal];
}
Thats it.
I have an app that gives you a popOver Tableview controller. This popOver has n number of cells. I need to be able to call a function on the main view whenever one of the table cells in the popover is touched. How would I go about doing that?
It would be nice to able to dismiss the popover too once a cell is touched....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell check %#", [totalArray objectAtIndex:indexPath.row]);
[self dismissPopoverAnimated:YES];
}
Here it will print the console the text in the cell, but I need to be able to pass that cell text back to the mainview... oh yeah, and the [self dismisspopoveranimated: yes] doesn't work...
The traditional way to address this is to make your "main" view controller a delegate of the class with the table view:
Define an application-specific delegate protocol, add a delegate method specific to your use case, and change your "main" view controller to conform to it. The delegate method should take a parameter whose type is the data you want to pass to the "main" view controller (in your case, either a UITableViewCell or the NSString containing its text).
Create a property on the class with the table view whose type is the new delegate protocol.
Pass your "main" view controller into the class with the table view by setting it as the value of the new property.
When the tableView:didSelectRowAtIndexPath: method is called, invoke the delegate method on the delegate you have created, passing in the data you want to give to the "main" view controller. This gives your "main" view controller an opportunity to respond to the fact that the table cell was selected in the popover, and it can call whatever function you need it to.
You can actually do the same thing in less code with blocks, but it's a bit more advanced. I won't get into that here.
You could also just use NSNotificationCenter, but personally I try to avoid using that unless absolutely necessary.
Under certain circumstances, UITableView didSelectRowAtIndexPath is being called twice causing the error Pushing the same view controller instance more than once is not supported.
Here's are the sequence of events:
TableView::didSelectRowAtIndexPath.
TableView::viewWillDisappear.
PushedViewController::viewWillAppear.
TableView::didSelectRowAtIndexPath.
Error: Pushing the same view controller instance more than once is not supported'
The only thing worth noting is that the UITableView is loading images asynchronously, but that never calls didSelectRowAtIndexPath. Also, the PushedViewController is reused to avoid having to reload it each time a cell is selected in the UITableView.
Anyone have any idea what may be causing this?
Thanks.
I'm seeing this problem too, probably one out of a 1000 users gets affected, or less. I сan clearly see two didSelectRowAtIndexPath registering 50 ms one after another. My guess is that it is a bug in iOS - no new taps should be directed to old view once new view-controller has been pushed. Alas, it is likely up to us to write code guarding against this. Here's what I'm thinking:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
if (self.navigationController.topViewController != self)
return;
... do other stuff
}
Disable user interaction after the first "didSelectRow". It's possible for multiple taps to "stack up" during the transition.
It usually takes someone with amazing dexterity in their fingers to get this behavior, but still.
if you already created Storyboard Segue don't call;
[self performSegueWithIdentifier:#"TYPE" sender:self];
in this method;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
This occurs because of the segue you are using inside the didSelectRowAtIndexPath method. Ensure that this segue is created by ctrl dragging from the view button on top of the source view controller to destination view controller. The error occurs when you create the segue by ctrl dragging from the table view cell to destination view controller.
You do not need to change any of you code. Just delete the segue and create it in the correct way. Hope this solves your problem, if I got the question right.
Swift 5
In my case also helped to update the cells instead of this code:
tableView.reloadRows(at: [indexPath], with: .automatic)
to use this:
tableView.reloadData()
Hi
I've got simple question which I don't know how to answer.
In my app I've got UITableViewController with cells. When I select one item (cell) it's getting higlighted and in other thread I'm loading chunk of data to display to the user (after load is done new VC is pushed). When doing it with thread user still can interact with application like, going back to other NavController and I do want that to happen. What I don't want to happen is that when loading isn't complete user can select other cell in table and it get's highlted. How I can prevent that (only highlit, I'm checking if there was a previous request so I'm not putting another thread untli previous request is done).
So basicly my question is, how can you foribd user from interacting with table view controller?
Set the selectionStyle of the UITableViewCell's to UITableViewCellSelectionStyleNone.
You can use the following to check if row can be selected:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (rowSelected) {
return nil;
}
return indexPath;
}
So, you only select it if no row is selected. In your didSelectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
rowSelected = YES;
// call method that is going to do something and mark rowSelected = NO;
}
You can deselect the row by using
[[tableView cellForRowAtIndexPath:indexPath] setSelected:NO animated:YES];
There is a risk that your users will be confused. A highlight is not enough. There should be very clear visual feedback that a network opperation is ongoing and that different rules apply.
either push the details view immediately after the user selected a row and show an activity indicator in there.
or give the whole table view a different look while loading data for the selected row: e.g. Show activity indicator in the selected row & hide the disclosure chevrons in all the other. While doing that, you can set the selection style to 'none'
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.