iOS: ContainerView -- Externally calling prepareForSegue in childController - ios

In the following setup, Button 1 calls First VC and Button 2 calls Second VC
Currently, I use the following code to call SecondVC by tapping Button 2.
MainVC.m
#implementation MainVC()
....
UINavigationController *navController = [self.childViewControllers objectAtIndex:0];
SecondVC *secondVC = [self.storyboard instantiateViewControllerWithIdentifier:#"second"];
[navController pushViewController:secondVC animated:YES];
The code above displays SecondVC just fine. However, in the First VC there are statements that need be executed before "Second VC" is presented, that are being skipped!
I execute these statements in First VC inside -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender. When the Next Button is tapped in the First VC, all the statements execute because it triggers prepareForSegue before presenting SecondVC. This step is skipped when the code above is used.
Extra Info:
A) Though a Navigation Controller, Main VC in my app does not show the navigation bar in my app. I believe this would be against HIG to display two navigation bars on an iPhone. I show it here to identify Main VC.
B) There are textFields in FirstVC that User fills out. If I just copy the code, will it read and save the data from textFields.text?
Question: How can I call prepareForSegue from First VC when Button 2 is tapped in MainVC? or is there another approach to this?

You could try something like the following:
UINavigationController *navController = [[self childViewControllers] objectAtIndex:0];
UIViewController *vc = [navController topViewController]
if ([vc isKindOfClass:[FirstVC class]]) {
FirstVC *firstVC = (FirstVC *)vc;
SecondVC *secondVC = [[self storyboard] instantiateViewControllerWithIdentifier:#"second"];
// Copy and read data from [[firstVC textField] text] to secondVC.
[navController pushViewController:secondVC animated:YES];
}

Related

Push view from modal view and then popToRootView

Here are my ViewControllers as fallows(objects):
FirstViewController - view with tab bar + navigation bar, also part of the ta bar;
SecondViewController - view only with navigation controller
ThirdViewController - view only with navigation controller
And what I am trying to do(logical steps):
present SecondViewController from FirstViewController (modal)
push ThirdViewController from SecondViewController (push)
popToRootViewControllerAnimated - to pop from ThirdViewController to FirstViewController (pop)
And here is the code that I am using by steps:
in FirstViewController class
SecondViewController * secondViewController = [[UIStoryboard MainStoryboard] instantiateViewControllerWithIdentifier:NSStringFromClass([SecondViewController class])];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: secondViewController];
[self.navigationController presentViewController: navigationController animated:YES completion:nil];
in SecondViewController class:
ThirdViewController * thirdViewController = [[UIStoryboard MainStoryboard] instantiateViewControllerWithIdentifier:NSStringFromClass([ThirdViewController class])];
[self.navigationController pushViewController: thirdViewController animated:YES];
and in ThirdViewController class I do:
[self.navigationController popToRootViewControllerAnimated:YES];
My issues is on point 3, when I do the pop to root view controller instead of going from ThirdViewController to FirstViewController it only goes to SecondViewController.
In step 1 you created new UINavigationController instance and you set secondViewController as the rootViewController for it. So now in step 2 when you are pushing the thirdViewController it will get added to the navigation stack of secondViewController.
Finally in step 3 when you are calling "popToRootViewControllerAnimated" it will pop to secondViewController, since secondViewController is the rootViewController of the navigation.
To go to FirstViewController call "dismissViewControllerAnimated" on self.navigationController.
Please refer the below code.
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
Forget about the frist step. Even though you are presenting a view controller modally, you are making it a root view controller till you dismiss it.
You are pushing it from second VC nav controller.
If you pop it, you will go back to second VC, as the third VC is pushed on second nav controller.
If you want to go to your first View controller,present the first VC again in the third VC by
[self presentViewController:firstVC animated:YES completion:nil];
or you can dismiss
[self.navigationController dismissViewController animated:YES completion:nil];
Please note that you cannot have multiple navigation controllers in the reference. You can have only one at any time.
Even though you present the second VC by from first VC nav controller
[self.navigationController presentViewController:secondVC animated:YES completion:nil];
you are presenting the second VC navigation controller here, so in the first VC navigation stack another nav controller will be added. At this point, the second VC navigation controller will be in reference when you call self.navigationController in the secondVC

iOS, TabBar and NavigationBar got hidden after adding pushViewController

I want to pass the variable " uid " between the FirstViewController and SecondViewController, so I used the following code to pass it. It successfully pass it, but the problem it hides the NavigationBar and the TabBar !
How can I solve it?
SecondViewController *vc2 = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc2.uid = uid;
[self.navigationController pushViewController:vc2 animated:YES];
Why dont you use storyboard segues to solve this? Just embed you view controller on a Navigation View controller and call performSegueWithIdentifier function.
Just add prepareForSegue function to your class and set uid there:
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
if([segue.identifier isEqualToString:#"secondViewSegue"])
{
//**OPTION A**: If you are using Push segue
SecondViewController *secondVC = (SecondViewController
*)segue.destinationViewController;
//**OPTION B**: ***ELSE, If you are using a Modal segue
UINavigationController *navController = (UINavigationController*)segue.destinationViewController;
SecondViewController *secondVC = (SessionViewController *)navController.topViewController;
//END OF OPTION B
secondVc.uid = uidValue;
}
}

Instantiate UINavigationController on button action

I have simple application with only one main view, which has 'Settings' button, and settings are tree-grouped, so I wand to present them in navigation controller. And I don't want navigationController in main view, because I don't want navigation bar there.
That's why I don't instantiate navigationController in application: didFinishLaunchingWithOptions:. And when I check self.navigationController in 'Settings' button handler, it returns nil.
So I wrote this: (I use ARC)
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[self.view.window setRootViewController:navigationController];
[navigationController pushViewController:settingsViewController animated:YES];
}
This works, although it pushes settingsViewController without animation (don't know why).
Is this generally the correct way to do - to change rootViewController in the middle of running app?
And if yes - than when I'm done with Settings, I probably need to set rootViewController back to current viewController, as it was before I tapped 'Settings'?
I think you want to create a navigation controller that you will present modally; the following will do:
SettingsViewController* settingsViewController = [[SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:settingsViewController animated:YES];
[self presentViewController: navigationController animated: YES completion:nil];
where self here is the view controller you want to trigger the modal view controller from.
since you present modally the navigation controller you can dismiss it within the code source of your settingsViewController by accessing its navigation controller:
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
To answer your question setting the rootViewController is not the correct way. Present the new vc modally through the presentViewController method.
A better way is to build the navigation vc and present it over your main vc (not replace your main vc).
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: settingsViewController];
[self presentViewController: navigationController animated:YES completion:^{}];
}
Your main vc might realize (maybe as a delegate) that the settings flow is complete. It can then dismiss the presented navigation controller with:
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
Alternatively, the setting flow could dismiss itself...
// somewhere in the settings vc or a vc it pushes, when we decide settings are done
self.navigationController dismissViewControllerAnimated:YES completion:^{}];
Have navigation in main view and have below line (which will hide navigation bar)
[[self navigationController] setNavigationBarHidden:YES animated:YES];
(I would say have this in viewWillAppear and viewDidLoad both, BUT in viewWillAppear is MUST).
Now in second view, to show navigation bar
[[self navigationController] setNavigationBarHidden:NO animated:YES];
Hope this will solve your problem...

Passing data to UIViewController when using modal

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];

Embed a UIViewController in a NavigationController using segues

I have a viewController that is usually (most often) accessed by using a push segue. This viewController needs to be embedded in a UINavigationController. So typically, this is no problem. The push segue manages pushing the viewController, and therefore the viewController has it's UINavigationController.
My issue is that in a few cases, I'd like to present this same exact viewController using a modal segue. When I do this, the viewController is not embedded in a navigationController. Is there a way to do this using segues?
I know this can be done purely in code without segues by creating a UINavigationController, setting the rootView as the viewController and then presenting that modally. That can be done using code like this :
MyViewController *viewController = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
[self presentModalViewController:navController animated:YES];
But how do I do this same thing but using Segues?
Update
For further clarity, here is some code to supplement how I used the accepted answer in the prepareForSegue method.
When Using Modal Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue destinationViewController] isEqualToString:#"Modal to MyVC"])
{
UINavigationController *nav = [segue destinationViewController];
MyViewController *vc = [nav topViewController];
//setup vc
}
}
When Using Push Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue destinationViewController] isEqualToString:#"Push to MyVC"])
{
MyViewController *vc = [segue destinationViewController];
//setup vc
}
}
In your Storyboard, you can embed a ViewController in a Navigation Controller by selecting the View Controller and then picking from the menu at the top Editor->Embed In->Navigation Controller. From another view controller, you control drag to this Navigation controller to set up the modal segue. You can also control drag to the original View Controller to set up segues to it without the Navigation Controller.

Resources