sending some data via prepareForSegue issue - ios

I have some issue when doing the segue with some data to to another view.
I have a contact book, first tableview is displaying contacts correctly, also with images
While I click on contact it should perform segue to another tableView. But from some reason it takes only first contact (seems like it's not in a loop).
I have one class for contacts and second ViewController. I can't understand why it happens
I created "pastie" link:
Here is the link to My code, to save place

I checked your code and I think that the problem is when the prepareForSegue is called the selected row is already deselected from didSelectRowAtIndexPath. I think that the best approach here is to send the selected index path from didSelectRowAtIndexPath into performSegue method something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.contTableView deselectRowAtIndexPath:indexPath animated:YES];
[self performSegueWithIdentifier:#"NextTable" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"NextTable"]) {
NSIndexPath *indexPath = (NSIndexPath*)sender
contact = self.contactsToShow[indexPath.row];
[segue.destinationViewController setPhoneNumbers:contact.numbers];
}
}
In order for this approach to work you must link the segue between view controllers not between cell and table view controller.

The Problem is line 196: You wanna have the index path for the selected row. But because in line 215 you are deselecting the cell immediately after being selected, the index path is always 0.

Related

Segue to same view Controller loads before prepareforsegue is called

Hi guys i am a beginner on Obj c Programming, I have created a "self segue"(segue to same view controller) from a tableviewcell. Now my problem is, i am setting few parameters in the prepareForSegue method, but somehow the segue already happens before this method is called(i am not even calling "performSeguewithIdentifier"). I understand that this might be because the segue is associated with the cell. But i found no other way to create a "self-segue"
please help.Btw i am using xcode6..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_selctdObj = [avObjArray objectAtIndex:indexPath.row];
if([_selctdObj isContainer])
{
[self performSegueWithIdentifier:#"isContainerSegue" sender:self];
}
else
{
[self performSegueWithIdentifier:#"isItemSegue" sender:self];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"isContainerSegue"]) {
// Get destination view
serverBrowseVC *sbvc = [segue destinationViewController];
[sbvc setServerSelected:_serverSelected];
[sbvc setBrowseObjID:_selctdObj];
}
}
Now invariably "isContainerSegue" gets executed ,even is the object is not a container. i also tried commenting the
//[self performSegueWithIdentifier:#"isContainerSegue" sender:self];
But every time "isContainerSegue" gets executed..
You said you weren't calling performSegueWithIdentifier, but you are. You shouldn't be if you've connected the segue from the cell. You also shouldn't implement didSelectRowAtIndexPath at all, just prepareForSegue. You can get the indexPath from the sender argument (the sender will be the cell) of prepareForSegue:sender:. If you're having a problem with the "if [_selctdObj isContainer]" clause, you need to post what isContainer does.

Pass tableviewcell data with segue?

I have a popover segue that goes to a view controller. I want to set some properties in the view controller when a cell is clicked. I tried using -prepareForSegue:, but then I realized that it doesn't have the scope of the selected cell. I know that I have to use:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
delegate method, but I'm not sure how that will affect the popover segue. Do I have to programatically create the popover, or is there another way?
Thanks much!
~Carpetfizz
Use self.tableView.indexPathForSelectedRow to get the data from your table's data source, e.g., the array backing the table data. If, for some reason, you don't want to get the data from your data source, then the sender is the UITableViewCell that was tapped to trigger the segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YourSegueID"]) {
YourDestinationViewControllerClass* destinationController = segue.destinationViewController;
destinationController.somePublicProperty = self.yourTablesDataSourceArray[self.tableView.indexPathForSelectedRow.row];
UITableViewCell *cell = (UITableViewCell *)sender;
destinationController.someOtherPublicProperty = cell.textLabel.text;
}
}
So add an NSIndexPath propriety to your VC. Let's call it selectedCell. In your tableView:didSelectRowAtIndexPath method, save the selected index path to the instance variable.
Then in your prepareForSegue method, use the instance variable.
Alternately, in your prepareForSegue method, you could ask the table view for the currently selected row using the table view's indexPathForSelectedRow method.
The best approach is to use the indexPathForSelectedRow property.
For example you can write a code like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YourSegueIdentifier"])
{
NSIndexPath *indexPath = [sender indexPathForSelectedRow];
NextViewController *vc = (NextViewController *)segue.destinationViewController;
vc.yourProperty = #"Some Value";
}
}
Assuming NextViewController as your segue destination controller and yourProperty as the property you want to set during the segue, you can access and set information from your parent controller because indexPathForSelectedRow automatically knows which cell has been clicked.
More information can be found here.

Modal segue needs 2 clicks instead of one

My UITableView needs 2 clicks to show the detail page of the selected cell : one for selection and another one for show the detail view. I would like one clic to directly show the detail view of the clicked cell.
I use a modal segue with this method inside my UITableViewManager.m :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [playerList indexPathForSelectedRow];
TCPlayerStat *object = _objects[indexPath.row];
[[segue destinationViewController] setPlayerStat:object];
}
}
I can't work with a push segue because I don't have Navigation Controller (and don't really want to).
I've looked in the TableView Attribute Inspector but didnt find out anything relevant for this. I have Selection "Single Selection" selected and "Show Selection on Touch" checked.
I don't know if this is possible and if it is, where to look..
Thanks for your help.
EDIT 1: When I write the two methods like this, it still doesn't work (needs 2 clicks) and I have a new warning log:
"Warning: Attempt to present TCDetailViewController: 0xa27b900 on
TCRootViewController: 0xa24f050 while a presentation is in progress!"
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [playerList indexPathForSelectedRow];
TCPlayerStat *object = _objects[indexPath.row];
TCDetailViewController *detailViewController = [segue destinationViewController];
[detailViewController setPlayerStat:object];
}
}
EDIT 2: I don't know why, but sometimes it works perfectly and doesn't need a second click on the table view. Can't find out :/
Solution below
I fond out what the problem was!
I changed a parameter on the Storyboard that I shouldn't have. I wanted the selection cell not to display a background highlight color when I click on it, but it seems the Segue is based on it to work decently.
HOW TO FIX:
In the storyboard, select your Table View Cell in the Navigator and don't chose the "None" option in "Selection" (Attributes Inspector). "Blue", "Gray" or "Default" seems to work nice.
I already had the selection attribute set to "Default" and was still experiencing the two-click problem. That suggestion did, however, point to a related solution. In didSelectRowAtIndexPath, call:
self.tableView.deselectRowAtIndexPath(indexPath, animated: false)
(I'm using Swift, obviously.)
I think the more correct way to solve this problem is to perform any action on Main thread, in your case triggering the segue:
Objective-C
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"showDetail" sender:self];
});
Swift
DispatchQueue.main.async {
self.performSegue(withIdentifier: "showDetail", sender: self)
}
It's actually a bug, related to the main RunLoop and tableViewCell, while selectionStyles force RunLoop to be awake (as it has to process the animation), if you don't have selectionStyle, plus you don't do any startup animations inside your presented viewController, RunLoop will go asleep sometimes.
If you, for some reason, don't want to do the action (in your case it's triggering the segue) on main thread, or you don't have startup animation on presented ViewController, you can simply add empty main thread call at the end and it will also trigger RunLoop to be awake.
Objective-C
[self performSegueWithIdentifier:#"showDetail" sender:self];
dispatch_async(dispatch_get_main_queue(), ^{});
Swift
self.performSegue(withIdentifier: "showDetail", sender: self)
DispatchQueue.main.async {}
A solution that works for me (XCode 9.2) and seems easy, but still allows for None for the Selection is :
As #Tulleb says, in the storyboard use anything but None for the Table View Cell "Selection", but then set it to .none in the code.
For example, like this...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tv.dequeueReusableCell(withIdentifier: "cell")
cell?.selectionStyle = .none // This is the important line
Use
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
instead of
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Then you can write any code and it works on the first click.
FYI. until now, with Xcode 8.2.1 and Swift 3.x, the issue also exists for my customized UICollectionViewCell subclass (But I suppose subclass is not needed to reproduce this issue. I have no time to test this case.).
I summarize the issue as: tapping on the previously selected cell will consume the tap event to cause deselection, instead of expectedly triggering the selection segues.
So the solution is to, either not use the selection segues, say, but use programmatically triggering segues, or deselect the cell itself whenever a selection segue is triggered.

Have to press detail disclosure button twice for data to be passed using prepareForSegue method

EDIT: I have fixed this problem. The code is corrected to show this fix.
I have an app that uses a UITableViewController that displays a list of all 50 states separated into sections alphabetically. I have added the detail disclosure button to the prototype cell in the storyboard and the table displays all the correct information.
When I press the button on a cell, it should segue to a new viewController and display the name of the state as the view title and then display a picture of the license plate in the view.
I am using the -(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath method to control the button press and then I am using the -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender to control the values being passed to the new viewController.
The problem I am having is that when I press the detail disclosure button nothing gets passed. NSLogging the values in the new viewController outputs (null), (null) and the view has no title or picture. However, when I press back and then press the same cell's detail disclosure button a second time, it works like it should. The title is correct and the picture displays properly.
The same is true when I choose another cell, the viewController displays the last information sent and I have to press back and then choose the cell again for it to update. When choosing a different cell for the first time, (null, (null) is what the NSlog is outputting. So I believe what is happening is that the first time a detail disclosure button is pressed, no values are passed, but the second time it is pressed, the values are being passed.
Here is the relevant code:
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
//Don't even use this method now.
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UITableViewCell *cell = (UITableViewCell*)sender; //Added this line
self.path = [self.tableview indexPathForCell:cell]; //Added this line
if ([segue.identifier isEqualToString:#"stateSegue"])
{
self.controller = segue.destinationViewController;
self.controller.stateName = [self.stateArray[indexPath.section][indexPath.row]stateName];
self.controller.licensePlateURL = [self.stateArray[indexPath.section][indexPath.row]licensePlateURL];
}
}
Any help with this would be greatly appreciated.
Check which method fires first. Maybe you set properties after segue is performed.
There's no informations about how (when) do you perform segue.
You could add this line to your accessoryButton method:
[self performSegueWithIdentifier:yourIdentifierFromStoryboard sender:self];

Storyboards and programmatically views

I'm totally lost in this issue. I have been working with storyboards, I have created a navigation controller with tableviews and some stuff. There are Services in each row of the tableview and I need to create one detail view for each service.
There are a lot of services, so I can't create them in the storyboard. The idea is to download the Services from a webservice (number of parameters, types of each one, etc..) and add as textfields / buttons as appropriate to the Service.
So, my problems and questions are:
1) Can I combine Storyboards and views programmatically? When I create a NewView in MyTableviewClass, should I do it in my prepareforsegue method? How can I show it in the screen without loosing my navigation controller? this is what I have (it doesn't work: it says to me that there is no segue with name 'Service1' ) :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([[segue identifier] isEqualToString:#"NextLevel"]) {
[segue.destinationViewController setActualNodo:[actualNodo getSonAtIndex:indexPath.row]];
} else if ([[segue identifier] isEqualToString:#"Service1"]) {
CGRect bounds = self.view.bounds;
UIView *myview = [[UIView alloc] initWithFrame:bounds];
[myview setBackgroundColor: [UIColor redColor]];
[self.view addSubview:myview];
[self.view bringSubviewToFront:myview]; }
Any book or reference is welcomed but I couldn't find anything similar. Is this very complicated in iOS? I have done a similar thing in Java. I have read about generating interfaces dynamically with XIBs but, sincerely, I don't know what it is..
Thanks for all.
Yes you can create a StoryBoard with a view and then add views programmatically to it.
You should not try creating a view within your prepareForSegue method. This really should be used for passing objects to another ViewController.
I would suggest this to you. Go back to your StoryBoard and create a new UIViewController scene. Click on your first scene and CTRL drag to the new scene. Next, click on your segue and give it a name.
Step 1:
Create a new class called ServicesViewController and make sure it's a subclass of `UIViewController:
Step 2:
Go back to your StoryBoard and click on scene so that it is selected. Next, click on the Files Owner and finally click on the class info button (the third button) and finally select your ServiceViewController class you just created.
Step 3:
Back in your ServicesViewController in the didSelectRowAtIndex method call your seque:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"YOUR_SEGUE_NAME" sender:nil];
}
For now, clean out all the code in your prepareForSegue method and just get the transition down first.
In addition to Flea's answer, if you need to keep the navigation controller, just create a push segue in your storyboard by control dragging from the file owner icon (the yellow box under your view controller's view) of the table view controller to the ServiceViewController you added to the storyboard, this should show a popup window where you can select "push" as the type of the segue. Next, select the segue and in the attribute inspector (the fourth button, next to the one in the snapshot) and in the "Identifier" text field type in a unique identifier for your segue, such as serviceSegue.
At this point, using Flea's code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"serviceSegue" sender:nil];
}
And in the code you posted:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([[segue identifier] isEqualToString:#"serviceSegue"])
[segue.destinationViewController setActualNodo:[actualNodo getSonAtIndex:indexPath.row]];
}
I'm not sure what you are trying to do with the other segue "Service1", but if you want to change the view of the TableViewController, segues are not the way to do it. If anything you should do it in the didSelectRowAtIndexPath method depending on the row selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (You want to transition to other view controller)
[self performSegueWithIdentifier:#"serviceSegue" sender:nil];
else
Change your view here.
}
I hope this helps!
It sounds like you just have multiple cells in a tableView. Instead of using segues you can simply create different cells with different identifiers and show or hide them based on what services are detected in your services array which is populated from your web service.

Resources