I have a tableview that is within a container view. When the user selects any of the rows, a method is called in the parent controller, which tells the parent controller to perform a segue. However, I am unable to figure out why it doesn't work. The code gets called from the didSelectRow -function in the tableView. The method does perform, but it gives me an error about no segue with that identifier.
However, when I call the method(listJobsOfSite) from within the parent view controller it works.
(tableview)
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.jobVC listJobsOfSite:#"locPwn"];
}
parent view controller
-(void)listJobsOfSite:(NSString *)site
{
[self performSegueWithIdentifier:#"JobSegue2" sender:nil];
}
EDIT:
The segue is between view controller 1, and view controller 2. view controller 1 holds a container view, which again holds a table view controller. This tableview controller should tell view controller 1 to segue into view controller 2.
EDIT 2:
screenshot http://tinypic.com/r/30x7dr9/8
You don't call classes. The word you are looking for is "method." You call your listJobsOfSite method that is in your parent view controller class .
Have you made sure you have given the segue an identifier? In your Storyboard you should click on the segue, inspect it, and enter "JobSegue2" in the field marked "Identifier."
If you have done step 2, do you need a container view? What are you trying to accomplish with the Container View + TableView? It sounds like your design would make more sense without the container view and the TableView as a property of View Controller 1.
If your heart is set on using the container view, in your Storyboard try to Control + Drag from your TableView cell to your View Controller 2. Name that segue "JobSegue2." Then you don't need to call any methods in your didSelectRow method. You also don't need a storyboard segue from View Controller 1 to 2. It seems like this question has the behavior you want (the question, not the answer! He is having the opposite problem you are).
Edit: Just noticed you have a Navigation Controller within your container view, so my suggestion above will likely push View Controller 2 within the container view. I'm totally confused by what you're trying to do.
Related
I have a ViewController that has a child controller with a dynamic tableview.
On tapping a table cell I need to segue to a new view.
I've tried all kinds of methods, including directly control dragging from the cell (resulting in a 'detached controller' warning) with little success and the current situation is as follows.
Child Controller (currentName, currentType are globals that can be picked up by the parent, myHome)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
currentName = "John"
currentType = 1
let segueVC = myHome()
segueVC.segueToMain()
}
Parent Controller (myHome)
func segueToMain() {
dispatch_async(dispatch_get_main_queue()){
self.performSegueWithIdentifier("home_myMain", sender: self)
}
}
I know this is probably horrendous but it appears to work fine in that it stores the data and gets to the parent function. However the attempt to segue causes the 'Receiver has no segue with identifier' error. The segue id is exactly correct. It links to a Navigation Controller.
I have a feeling that I need some delegates etc but I'm not really sure how to deal with them. Getting rid of the navigation controller and segue direct to view didn't make any difference.
Any help with how to get this segue to trigger would be greatly appreciated.
EDIT
In relation to answers suggesting the segue direct from the child, here is the setup which seems to result in a 'detached' controller error. For the record, I would love to use this method but all attempts failed.
The Parent Controller (A) builds a PageMenu class that gets its page structure from Controller B. B has a table with cells directly segued to another view.
A constructs the menu as follows
self.addChildViewController(self.pageMenu!)
self.view.addSubview(pageMenu!.view)
self.pageMenu!.didMoveToParentViewController(self)
PageMenu uses class B as the template for each menu page.
There is no physical link between A and B on the storyboard - only that the class for A sets B as a child.
Segues from B fail with a 'detached controller' error so maybe there's something I'm missing from the child setup?
Thanks
You can't just allocate a new instance of your parent view controller and ask it to perform a segue - As you are instantiating the view controller directly, it won't be associated with the storyboard, this is why you get an exception stating that that view controller doesn't have a segue of the specified identifier.
Even if you did instantiate a new instance from the storyboard you would still get an error as that new instance wouldn't be on screen, resulting in a message that you attempted to present a view controller on a view controller that isn't in the view hierarchy.
You need your child view controller to have a reference to the parent view controller instance that contains it and then invoke the segue using that instance.
Also, there is no need for the dispatch_async - the tableview delegate method will be invoked on the main queue already.
Finally, by convention, class names start with an upper case letter, so myHome should be MyHome (or even better, something like MyHomeViewController)
I have a application with UIViewController "hierarchy" of this form:
Tab Bar Controller
- View Controller 1
- View Controller 2
- View Controller 3
* View Controller 4
I am using the XCTest Framework to write test methods for my iOS application. View controller 3 is a UITableViewController. When the user selects a row in the table, view controller 4 is shown.
I make a call to the application's window to get the root view controller, the tab bar controller for the application:
// get reference to the tab bar controller
let tabBarController = UIApplication.sharedApplication().delegate!.window!!.rootViewController! as! UITabBarController
But that only gets me access to the three view controllers corresponding to each tab. I checked the number of child view controllers for the root view controller and the number is 3. Makes sense. I checked the number of child view controllers for the 3rd view controller...zero. The view controllers are all setup via IB. I still need to somehow get access to the 4th view controller for my test. Is there a programmatic way to reach the 4th view controller?
A good night's grinding got me to the answer:
In my test code, I was making a call on the UITableView using this:
selectRowAtIndexPath:animated:scrollPosition:
But that method call does not trigger the UITableViewSelectionDidChangeNotification notification to get posted. Since my application's code does use the UITableView delegate function that depends on that notification's posting, I instead just did this:
// get reference to the table view
let tableView = tableViewController.view.viewWithTag(300)! as! UITableView
tableView.delegate!.tableView!(tableView, didSelectRowAtIndexPath: indexPath)
Doing the row selection this way triggered my table view's delegate method to be called and thus the
performSegueWithIdentifier:sender:
inside of that method was called.
The view controller responsible for displaying the site details was loaded and I was able to get a reference to it using the presenting table view controller's method:
let detailViewController = tableViewController.presentedViewController!
This seems to work and I am satisfied thus far...
Unwind segues seem not to behave as expected in iOS 8.1 when combined with a modal view and container view.
Here's the view controller hierarchy for the test project which can be found on github:
Tapping on the "tap me" button pushes the modal view which is embedded in a navigation controller and which has a tableView as a child view controller. Tapping on a row in the tableView pushes another tableView. Finally, tapping on a row in this final tableView should call the unwind segue named bUnwindSegue found on the previous view controller.
Problems:
bUnwindSegue is never called.
According to technical note TN2298 a container view controller is responsible for selecting the child view controller to handle a segue. In this case viewControllerForUnwindSegueAction:fromViewController:withSender: should be called on the container view controller. It isn't.
In the example project, you can see that BTableViewController contains the unwind segue:
- (IBAction)bUnwindSegue:(UIStoryboardSegue *)segue;
{
NSLog(#"Unwinding...this unwind segue will never get called.");
}
In the storyboard, the cell selection action for CTableViewController is indeed the bUnwindSegue. Also note that if you change the cell select action of CTableViewController to the unwind segue in the container view controller -- containerVCUnwindSegue -- that the segue is called correctly.
Are unwind segues not behaving as expected?
(1) You're misunderstanding the technical note TN2298 you cited and (2) you're not overriding viewControllerForUnwindSegueAction: appropriately.
As the TN2298 doc section you linked to about Container View Controller states underneath its "Selecting a Child View Controller to Handle An Unwind Action" subheading:
Your container view controller should override the method shown in
[viewControllerForUnwindSegueAction:] to search its child
view controllers for a view controller that wants to handle the unwind
action. If none of a container's child view controllers want to handle
the unwind action, it should invoke the super's implementation and return
the result.
First off, to override the method, you have to subclass the UINavigationController in your storyboard and add the viewControllerForUnwindSegueAction: method there. After doing that, you'll see the method is now being called as expected.
But your second error is that your current attempt to override the viewControllerForUnwindSegueAction: method simply contains return self;. You should instead be returning the view controller that you'd like to handle the unwind action.
So say, for example, you have a public variable in VCWithContainedVCsViewController to hold the current instance of BTableViewController and you access you're able to access that current container view controller, ex:
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
NSLog(#"Technical note TN2298 indicates child VCs defer to their parent to determine where an unwind segue will be handled.");
if ([NSStringFromSelector(action) isEqualToString:#"bUnwindSegue:"]) {
NSLog(#"%#", self.viewControllers);
VCWithContainedVCsViewController *containerVC = (VCWithContainedVCsViewController*)self.viewControllers[0];
return containerVC.container;
}
return [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}
What you'll see in that case is that bUnwindSegue: is in fact being called (your message should print), but the segue still won't happen.
Why is this?
Because as I mentioned in the comments, BTableViewController is not on the current navigation stack. Some child view controllers of BTableViewController, like CTableViewController, will be on the navigation stack because, for example, CTableViewController is not a container view. But BTableViewController itself is not capable of performing the segue on its own because it is not on the current navigation stack. So although you can in fact select some child view controllers to handle unwind actions as the documentation states, BTableViewController isn't going to be one of them.
I use three view controllers and on each view controller I put UITableView and UITableViewCell. When I connected from the first view controller's table view cell to another view controller's from within storyboard and ran the simulator, the segue works as expected. However, when I connect from the second view controller's table view cell to the last view controller from within storyboard IN THE EXACTLY SAME WAY as the first one, then for some reasons the transition doesn't work.
If I define didSelectRowAtIndexPath: and within it call [self performSegueWithIdentifier:#"showDetail" sender:self]; in the second view controller's implementation file, the transition can work as expected. I don't care about whether it's storyboard segue or methods defined in my code to perform the transition, as long as the transition does work correctly.
However, I'd still like to know why such inconsistency occurs. As I said, I connected two storyboard in the exactly same way in both cases and checked out attribute inspector and connection inspector, but I don't find any differences between the two connection there.
Also, while the first view controller can perform the transition without the method being defined, when I define it then the transition doesn't work, issuing the following error message:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
I think I cannot use both approaches (i.e. storyboard segue and method calls) - I just wanted to know what derives the inconsistency here.
I use iOS 7 and Xcode 5.
First of all, if you use push segues, you can't make a push for the second segue if the first segue is modal (unless you embed your second VC in a navigation controller).
Second, make sure de segue identifiers are unique for each segue.
If you ctrl+drag a segue in storyboard, don't call performsegue in code, you just attempt to do the same operation twice. If the segue is in storyboard, in code you should use prepareforsegue delegate.
Another way of doing all of this is not using any segue in storyboard, then in code #didselectrowatindexpath you can instantiate your destination vc using [storyboard instantiateviewcontrolerwithidentifier...], then [self.navigationcontroller pushviewcontroller..] for a push segue or [self presentviewcontroller...] for a modal.
EDIT: Also, when you ctrl+drag, make sure you are dragging from the cell and not from the table.
Self Answer
I finally found out that the issue was not caused in storyboard - it's on the implementation code. Since I have to use UITableViewCellStyleValue1, I cannot use dequeueReusableCellWithIdentifier, and for some reasons the dequeueReusableCellWithIdentifier has to be used in order to make an automatic transition from cell to another view controller from within storyboard only. I checked out that using dequeueReusableCellWithIdentifier and disabling UITableViewCellStyleValue1 temporarily makes it successful to make the transition without didSelectRowAtIndexPath: method being defined.
I have a table view controller embedded in a container in a view controller.
In both the view and table view controllers prepareForSegue method I put NSLog(#"name of the controller")
I see a log for the view controller but not the table view controller. Shouldn't I also see the nslog for my table view's prepareForSegue?
Exactly - as Greg explains, the embed type of segue is
only called during setup!
This is very confusing. You could say that
"prepareForSegue" is A REALLY BAD NAME!
the name "prepare for segue" only makes sense in the (rare!) case where you are actually "segueing" from one scene to another
In an iOS app container views are commonplace, you have them everywhere, whereas you rarely use an actual "scene segue".
So really, "prepareForSegue" should be called something like:
"Hey, we're setting up all your container views -- you can grab any info you need at this time! Oh, if you happen to be doing a scene segue, you can use this also!"
what about:
containerViewBeingSetUpOhAndAlsoPrepareForSegueIfYouHappenToBeDoingThat:
That's a bit long, but clearer!
It's just one of those weird things about iOS that you have to know, but is never explained anywhere.
Here's a full explanation of using container views for beginners https://stackoverflow.com/a/23403979/294884
In Your Log Frame View Controller prepareForSegue will be called directly after initWithCoder: to prepare your TableViewController. I cannot see your connection between table view controller and another view (view on the right) but I believe it will be called when you hit the row.
//EXTENDED
The other thing could be that you haven't add UINavigationController on the view hierarchy and you set up your segue style to 'push'. Try change style of your segue to 'modal' in attribute inspector.
Hope this help.
Other than what's already discussed, you should make sure you aren't ignoring segue identifier in following delegate call.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender