I am having trouble with a Navigation Controller in my iOS project which uses Storyboards.
The main part of the project is a UITabBar. When the app loads for the very first time the screen RegisterViewController is presented:
When the user taps on the "Register" button a HTTP POST request is made, then upon receiving the response (JSON object) the screen QuestionnaireViewController is presented using:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
QuestionnaireViewController *questionnaireViewController = [mainStoryboard instantiateViewControllerWithIdentifier:#"QuestionnaireView"];
[questionnaireViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[questionnaireViewController setQuestionsAndAnswers:_questionsAndAnswers];
[self presentViewController:questionnaireViewController animated:NO completion:nil];
When the user taps on a cell in QuestionnaireViewController a message is sent to prepareForSegue:sender:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UINavigationController *navigationController = segue.destinationViewController;
AnswersViewController *answersViewController = [[navigationController viewControllers] objectAtIndex:0];
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSArray *answers = [[_questionsAndAnswers objectAtIndex:indexPath.row] objectForKey:#"answers"];
[answersViewController setAnswers:answers];
}
At this point the app crashes with an error of:
-[AnswersViewController viewControllers]: unrecognized selector sent to instance.
Below is a more comprehensive overview of my storyboard, which may help in understanding where I have gone wrong:
I am stumped as to how to fix this, and am hoping that someone out there is able to help.
Thanks,
Nick
First, you should present the navigation controller, not its root view controller. Then when you want to populate the root view controller with some data, you need to reference it as the navigation controller's root view controller:
UINavigationcontroller *questionnaireNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"QuestionnaireNavigationController"];
[questionnaireNavController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
QuestionnaireViewController *qvc = (QuestionnaireViewController *)[questionnaireNavController topViewController];
[qvc setQuestionsAndAnswers:_questionsAndAnswers];
[self presentViewController:questionnaireNavController animated:NO completion:nil];
Second, in prepareForSegue:sender: you don't need to reference a navigation controller at all:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
AnswersViewController *answersVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSArray *answers = [[_questionsAndAnswers objectAtIndex:indexPath.row] objectForKey:#"answers"];
[answersVC setAnswers:answers];
}
You are not getting the navigationController properly. Instead of segue.destinationViewController, use self.navigationController
By calling segue.destinationViewController, you are getting a pointer to the VC you are segueing to (AnswersViewController) which doesn't understand the message viewControllers because it is not a UINavigationController.
Also the AnswersViewController is objectAtIndex:1 I believe, check this though.
Related
I have an app with a sidebar menu using SWRevealViewController. I followed this tutorial on how to make it, but have a problem. I have many subviews of the first subview that has the prepareForSegue: sender: method. In my subclass view controller, I have the implementation:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
do Saving....
[super prepareForSegue:segue sender:sender];
}
The saving never gets called. I put an NSLog() statement and it was never executed. My layout is the same as the video tutorial (modified for my own app, different view controllers for each table cell). Any help?
Try this
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SecondViewController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[navController setViewControllers: #[rootViewController] animated: YES];
It work for me
Or else follow this tutorial you will find solution.
SWReavalview Controller
Try This
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue isKindOfClass:[SWRevealViewControllerSegueSetController class] ]){
UIViewController *dvc = [segue destinationViewController];
UINavigationController *navCtrl = (UINavigationController *) self.revealViewController.frontViewController;
[navCtrl setViewControllers:#[dvc] animated:NO];
[self.revealViewController setFrontViewPosition:FrontViewPositionLeft animated:YES];
}
Now in my project I have an issue with auto layout.
In iOS 8: It's still working well.
However in iOS 7: It pushed to view and can not load content the view. In navigation I saw it changed title. I guess auto layout is the cause of the error.
Code:
MyViewController * viewcontroller = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[employeeListVC.view updateConstraintsIfNeeded];
[self.navigationController pushViewController:employeeListVC animated:YES];
Please help me to resolve it. Many thanks.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main"
bundle: nil];
MyViewController *controller = [mainStoryboard instantiateViewControllerWithIdentifier:#"MyViewController"];
[self.navigationController pushViewController:controller animated:YES];
I guess auto layout is not the cause. One possible case is that the MyViewController is not embedded in a UINavigationController.
Check self.navigationController is nil or not.
Error message from console will be helpful (if exists).
Use Segue instead of Navigation Controller. That better one as per my personal opinion.
Steps:
1. Embed your viewcontrollers [Initial View Controller must be embed in Navigation Controller]
Give a name identifier your segue.
Write code in your event
[self performSegueWithIdentifier:#"SegueBlockedContacts" sender:self];
Implement prepareForSegue method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"SegueBlockedContacts"]) {
BlockedContactTableVC *blockedContactVC = segue.destinationViewController;
[blockedContactVC.blockedContacts removeAllObjects];
blockedContactVC.blockedContacts = blockedContacts;
blockedContactVC.myDelegate = self;
}
}
Thats it.
I have two view controllers, MainViewController and SecondViewController.
In my main view controller, I have data I pass to the second view controller like so:
-(IBAction)shareToSocial:(id)sender {
SecondViewController *view2 = (SecondViewsController *)[storyboard instantiateViewControllerWithIdentifier:#"PushToNext"];
view2.secTitle = self.MainTitle;
[self.navigationController setModalPresentationStyle: UIModalPresentationCurrentContext];
[self setModalPresentationStyle:UIModalPresentationCurrentContext];
}
Where both secTitle and MainTitle are NSStrings for each controller. From what I've read, by setting view2.secTitle to self.MainTitle, when SecondViewController pops in I my view2.secTitle will have the same value as MainTitle. So if MainTitle is "Hello World" then I open secondViewController, secTitle would also be "Hello World". However, I've been getting null with secTitle even though I am setting the data for it.
If I use pushViewController instead of modal, the values are passed between the two controllers fine. So I'm not sure if I'm doing anything wrong here exactly.
I've also tried using [view2 setSecTitle:MainTitle] to no avail.
There's not much code on my SecondViewController but here's how it looks:
-(IBAction)showMessage:id(sender){
NSLog(#"test: %#", self.secTitle);
}
-(IBAction)closeMessage:id(sender){
[self dismissViewControllerAnimated:YES completion:nil];
}
Also, I've noticed that when I used dismissViewControllerAnimated on SecondViewController to close it and go back to the MainViewController, a black empty screen shows for a few seconds before showing the Main View and touch events on MainViewController is not detected anymore.
Please help.
Could you use performSegueWithIdentifier and prepareForSegue? If you've got your views in your storyboard and set up a segue between them, given it an identifier and set the style to modal you should be able to do this:
-(IBAction)shareToSocial:(id)sender {
[self performSegueWithIdentifier:#"modelToNextSegue" sender:self];
}
Then in prepareForSegue method you pass your values:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *view2 = [segue destinationViewController];
view2.secTitle = self.MainTitle;
}
Try this:
SecondViewController *view2 = (SecondViewsController *)[storyboard instantiateViewControllerWithIdentifier:#"PushToNext"];
view2.secTitle = self.MainTitle;
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:view2];
[nav setModalPresentationStyle: UIModalPresentationCurrentContext];
[self setModalPresentationStyle:UIModalPresentationCurrentContext];
I have a SplitViewController App with 2 initial controllers:
leftViewController (master)
rightViewController (detail)
my rightViewController (detail) has the delegate of the splitViewController for presenting a button which shows/hides my menu (master)
my leftViewController (master) is a menu application, when I select any element of the menĂº I trigger a segue connected to my master and it replaces my detailViewController for the selected element of menu
when I do that and if I try to rotate my iPhone for hide menu my app crashes and says:
*** -[rightViewController respondsToSelector:]: message sent to deallocated instance
I guess it is because my splitViewController wants to communicate with its delegate, its old rightViewController, but it is gone, it has been replaced on my view,
maybe I need whether:
reasign my delegate to my new viewController (detail)
or
remove delegate of my rightViewController and assign it then to my newViewController
also tried this in my new viewController:
#interface newViewController ()<UISplitViewControllerDelegate>
#end
#implementation newViewController
- (void)viewDidLoad
{
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
}
...
#end
but still not working... I get the same message
how do I fix this???
thanks in advance
EDIT: add my segue code for helping to answer my question
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"viewSceneAgenda"]) {
[segue.destinationViewController setTitle:#"Citas"];
[segue.destinationViewController setUserIDElement:UID_CUS];
[segue.destinationViewController setOverallAppointments:overallDates];
}
}
Maybe you have to pass your delegate as you said to your newViewController... why don't you add this in your leftViewController (master)...
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showNewViewControllerScene"]) {
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
splitViewController.delegate = segue.destinationViewController;
}
}
just replace the segue identifier (showNewViewControllerScene) to match with yours on your storyboard
Gonna complement Jesus Answer
Since I'm working with several segues, all of them must have the delegate of my UISplitView, also since iPhone doesn't support splitviews, I added a condition for assigning the delegate just if the user is running this app on any iPad
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[UIDevice currentDevice].model hasPrefix:#"iPad"]) {
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
splitViewController.delegate = segue.destinationViewController;
}
if ([segue.identifier isEqualToString:#"viewSceneAgenda"]) {
[segue.destinationViewController setTitle:#"Citas"];
[segue.destinationViewController setUserIDElement:UID_CUS];
[segue.destinationViewController setOverallAppointments:overallDates];
}
}
Basically, i'm using the prepareForSegue method to transition to another view within my storyboard, my view had to be embedded in a navigation controller in order to display the navigation bar so I am passing variables in such a way:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"exploreAddSelected"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
AddTableViewController *controller = (AddTableViewController *)navController.topViewController;
controller.tagName = tagName;
}
}
My issue is I need to be able to set some method values in the actual view controller when using the prepareForSegue method, as you can do when using pushViewController as below:
DetailViewController *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
detailViewController.tagName = tagName;
[detailViewController setCurrentHashtag:nil];
[navigationController pushViewController:self.detailViewController animated:YES];
I need to be able to set the "setCurrentHashtag" method, that is declared in the view controller that the segue "exploreAddSelected" goes to. How would I do this?
The answer to this is actually extremely obvious, but it takes me to ask the question to realise the answer myself.
You can simply set:
controller.currentHashtag = nil;
Where currentHashtag is a newly declared instance in the view controller.
Then in the actual view controller set:
[self setCurrentHashtag:currentHashtag];
Just add the currentHashtag to the method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"exploreAddSelected"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
AddTableViewController *controller = (AddTableViewController *)navController.topViewController;
controller.tagName = tagName;
[controller setCurrentHashTag:nil]; //add this line
}
}
I'm not sure what you are doing here. If the segue points to a view controller directly (and nor via another navigation controller), you do not need the mapping to topviewcontroller, but use the destinationcontroller directly.