I am trying to navigate to "Home" view controller and for this I have written the following code in the ContainerViewController. But once the code executes, the application hangs and it show 100% CPU usage. Please help.
- (IBAction) home:(UIButton *)sender
{
HomeViewController *homeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"HomeViewController"];
[self.navigationController pushViewController:homeViewController animated:YES];
//[self presentViewController:homeViewController animated:YES completion:nil];
}
I have a question for you
1-If You want to push SecondViewController on to FirstViewController then your code is good enough
2-If you have a containerview in firstViewController and you want to add SecondViewcontroller's view to firstViewController
then use this code
UIViewController*vc1 = [[test1 alloc]initWithNibName:#"SecondViewController" bundle:nil];
//add to the container vc which is self
[self addChildViewController:vc1];
//the entry view (will be removed from it superview later by the api)
[self.view addSubview:vc1.view];
I think you want an unwind segue here. In your first view controller add :
- (IBAction)unwindToFirstViewController:(UIStoryboardSegue*)sender
{
}
You then need to hook up each of your view controllers home button to the green Exit button at the bottom of the view controller, choosing the unwindToMainMenu option. This will then take you back to the first view controller when pressed.
Have you tried popping the current view?
navigationController?.popViewControllerAnimated(true)
or just popping to root?
navigationController?.popToRootViewControllerAnimated(true)
or setting a new stack?
navigationController?.setViewControllers(homeViewController, animated: true)
The code is in Swift but it would work the same in ObjectiveC
Related
Lets say I am in ViewController1 and I want to load ViewController2. The only way I know to accomplish this is using a button in ViewController1, and connecting to ViewController2 in the storyboard but I do not want to do it that way. I want to do in a method in MyViewController1. For example, in my viewDidLoad method.
Any help is greatly appreciated. Thanks
Create a segue from vc1 to vc2 inside the storyboard, give it a name and call:
[self performSegueWithIdentifier: #"mySegueCustomName" sender: self];
If you are trying to embed the view managed by ViewController2 into the view managed by ViewController1, you can do this by adding a UIContainerView to ViewController1 with an embed segue pointing to ViewController2.
This is useful, for example, if you have a reusable view managed by ViewController2 and want to use that in multiple places.
You can do this in InterfaceBuilder by dragging a UIContainerView from the Object library onto the view for ViewController1. You can then change the class of the view controller it creates to ViewController2.
Do not confuse Views and ViewControllers. You post title suggests you do not have it ingrained yet fully.
If you want to present another view controller programatically, then simply create an action method for the button and do it there. ViewDidLoad is absolutely NOT fit for that purpose.
You can present a view controller included in the storyboard like this:
-(IBAction)goToDetails:(id)sender
{
//identify the correct storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"storyboard's name" bundle:nil];
//extract a view controller instance from the storyboard
DetailViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"details view controller's identifier"];
//present the view controller instance
[self presentViewController:detailViewController
animated:YES
completion:nil];
}
And here's an example to go a to an unrelated non-storyboard view controller.
-(IBAction)loginToSystem:(id)sender
{
LoginViewController *loginViewController = [[LoginViewController alloc] init];
[self presentViewController:loginViewController
animated:YES
completion:nil];
}
I'm building a complex app that has kind of a branch in the middle.
At some point in the app, a particular UIViewController is presented, we'll call it mainViewController (shortened mainVC).
The mainVC presents another view controller, by code, using the following code (I strip out parts of it for privacy reasons):
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"SecondaryStoryboard" bundle:secondaryBundle];
SecondViewController *secondVC = [storyboard instantiateInitialViewController];
[self presentViewController:secondVC animated:YES completion:nil];
So the secondVC will later present another view controller, called thirdVC. This is done using a custom segue, set in the storyboard used in the code above, which code looks like this:
#implementation VCCustomPushSegue
- (void)perform {
UIView *sourceView = ((UIViewController *)self.sourceViewController).view;
UIView *destinationView = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width, destinationView.center.y);
[window insertSubview:destinationView aboveSubview:sourceView];
[UIView animateWithDuration:0.4
animations:^{
destinationView.center = CGPointMake(sourceView.center.x, destinationView.center.y);
sourceView.center = CGPointMake(0 - sourceView.center.x, destinationView.center.y);
}
completion:^(BOOL finished){
[self.sourceViewController presentViewController:self.destinationViewController animated:NO completion:nil];
}];
}
#end
As you can see this segue presents the destination view controller modally (by the use of presentViewController:) with a custom animation (a slide from right to left).
So basically up to here everything is fine. I present the secondVC with a classic modal animation (slide up from bottom) and present the thirdVC with my custom transition.
But when I want to dismiss the thirdVC, what I want is to go back directly to the mainVC. So I call the following from the thirdVC :
self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:_animate completion:nil];
That way, I'm calling dismissViewControllerAnimated: directly on mainVC (referenced by self.presentingViewController.presentingViewController), and I'm expecting the thirdVC to be dismissed with an animation, and the secondVC to just disappear without animation.
As Apple says in the UIViewController Class Documentation:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, it automatically forwards the message to the
presenting view controller.
If you present several view controllers in succession, thus building a
stack of presented view controllers, calling this method on a view
controller lower in the stack dismisses its immediate child view
controller and all view controllers above that child on the stack.
When this happens, only the top-most view is dismissed in an animated
fashion; any intermediate view controllers are simply removed from the
stack. The top-most view is dismissed using its modal transition
style, which may differ from the styles used by other view controllers
lower in the stack.
The issue is that it's not what happens. In my scenario, the thirdVC disappears, and shows the secondVC being dismissed with the classic modal slide to bottom animation.
What am I doing wrong ?
Edit :
So #codeFi's answer is probably working in a classic project, but the problem here is that I'm working on a framework. So mainVC would be in a client app, and the secondVC and thirdVC are in my framework, in a separate storyboard. I don't have access to mainVC in any other way than a reference to it in my code, so unwind segues are unfortunately not an option here.
I've been having this exact same issue, and I've managed to visually work around it by adding a snapshot of the screen as a subview to secondVC.view, like so:
if (self.presentedViewController.presentedViewController) {
[self.presentedViewController.view addSubview:[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]];
}
[self dismissViewControllerAnimated:YES completion:nil];
Not pretty, but it seems to be working.
NOTE: if your secondVC has a navigation bar, you will need to hide the navigation bar in between snapshotting the screen and adding the snapshot as a subview to secondVC, as otherwise the snapshot will appear below the navigation bar, thus seemingly displaying a double navigation bar during the dismissal animation. Code:
if (self.presentedViewController.presentedViewController) {
UIView *snapshot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[self.presentedViewController.navigationController setNavigationBarHidden:YES animated:NO];
[self.presentedViewController.view addSubview:snapshot];
}
[self dismissViewControllerAnimated:YES completion:nil];
I had the same issue and I've fixed it by using UnwindSegues.
Basically, all you have to do is add an IBAction Unwind Segue method in the ViewController that you want to segue to and then connect in IB the Exit action to your Unwind Segue method.
Example:
Let's say you have three ViewControllers (VC1, VC2, VC3) and you want to go from VC3 to VC1.
Step 1
Add a method to VC1 like the following:
- (IBAction)unwindToVC1:(UIStoryboardSegue*)sender
{
}
Step 2
Go in Interface Builder to VC3 and select it. Then CTRL-drag from your VC icon to Exit icon and select the method you've just added in VC1.
Step 3
While still in IB and with VC3 selected, select your Unwind Segue and in the Attributes Inspector add a Segue Identifier.
Step 4
Go to VC3 where you need to perform your segue (or dismiss the VC) and add the following:
[self performSegueWithIdentifier:#"VC1Segue" sender:self];
I have an UITabBarController that has 3 buttons. The second button points to ViewController1 which is connected to another view called ViewController2. After I tap a button in ViewController2 I programmatically present ViewController1 again, that works perfect except one thing. After I "arrived" to ViewController1 the tab bar disappears.
I'm using this method to navigate back to ViewController1. (exactly I navigate to its navigation controller, but already tried with the view)
- (void)presentViewControllerAnimated:(BOOL)animated {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"storyboard" bundle:nil];
UINavigationController *firstViewNavigationController = [storyboard instantiateViewControllerWithIdentifier:#"destination"];
[self presentViewController:firstViewNavigationController animated:animated completion:nil];
}
I call here the first method
- (void)didTapButton:(id)sender {
UIButton *button = (UIButton *)sender;
CGPoint pointInSuperview = [button.superview convertPoint:button.center toView:self.tableView];
[self presentViewControllerAnimated:YES];
}
This method hides the tab bar in the ViewController2, I already tried without it, therefore there is no problem with it.
-(BOOL)hidesBottomBarWhenPushed
{
return YES;
}
I can't figure out why this thing happens, I think it's a fair solution, that worked well for a several times when I needed to present views. I've read it can happen with segues, but I'm doing it with code without segues.
Actually your code works right. There should not be tab bar when you present FirstViewController from SecondViewController. Because when you call instantiateViewControllerWithIdentifier its basically creates a new instance of that view controller, and of course, there is no tab bar.
The right way to go back to your first view controller is to pop SecondViewController (or dismiss it, if it presented modally). So your final code should be like this
- (void)didTapButton:(id)sender {
// If this view controller (i.e. SecondViewController) was pushed, like in your case, then
[self.navigationController popViewControllerAnimated:YES];
// If this view controller was presented modally, then
// [self dismissViewControllerAnimated:YES completion:nil];
}
And of course, your view controller hierarchy in storyboard must be like this:
-- UINavigationController -> FirstViewController -> SecondViewController
|
->UITabBarController____|
-...
-...
I've tried the same and got the same result.
My solution was simple, on the push do this :
UINavigationController *firstViewNavigationController = [storyboard instantiateViewControllerWithIdentifier:#"destination"];
firstViewNavigationController.hidesBottomBarWhenPushed = true; // Insert this and set it to what you want to do
[self presentViewController:firstViewNavigationController animated:animated completion:nil];
and then remove your
-(BOOL)hidesBottomBarWhenPushed
{
return YES;
}
I'm trying to programmatically change the selected menu item and the displayed topViewController. In other words, I'm trying to do the same one does to change the selected tab in UITabBarController:
[self.tabBarController setSelectedIndex:2];
Therefore, according to another SO question (https://stackoverflow.com/a/20309377/1161723) I'm using this to change the displayed topViewController:
[self.slidingViewController anchorTopViewToRightAnimated:NO];
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MESettingsNavigationController"];
[self.slidingViewController resetTopViewAnimated:NO];
But it doesn't work. Well, the two first lines work correctly, displaying the menu and changing the topViewController, respectively but the last line simply doesn't hide the sidemenu so it stays there until I hide it by gesture or tapping the button. Debugging show that the last self.slidingViewController returns nil instead of instance of ECSlidingViewController. And if I skip the first and the last line, leaving only:
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MESettingsNavigationController"];
it makes the app crash.
Any idea how to change the view properly, with hiding the side menu? I'm using ECSlidingViewController 2.0.1
EDIT:
using competition block and/or creating a reference to the sliding view controller doesn't make any difference:
ECSlidingViewController *slidingViewController = self.slidingViewController;
[slidingViewController anchorTopViewToRightAnimated:NO onComplete:^{
slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MESettingsNavigationController"];
[slidingViewController resetTopViewAnimated:NO];
}];
UPDATE 14.7.2014:
The sidemenu is not hiding only if custom view controller transitions are used. For instance in TransitionFun example.
The app crashes when leaving only the following in unwind segue handler:
- (IBAction)unwindModalView:(UIStoryboardSegue *)sender
{
ECSlidingViewController *slidingViewController = [self slidingViewController];
slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"METransitionsNavigationController"];
}
NOTE: If the last view controller (where the unwind segue is called from) is presented
by push segue instead of modal segue, the app don't crash.
You can see the hierarchy of the view controllers on the image here
(it's basically the TransitionFun example with one more VC encapsulated in NavigationVC and modally presented by segue from Settings' cell)
self.slidingViewController is a calculated variable. The calculation is done by navigating the view controller hierarchy. So, if the view controller is removed from the hierarchy as part of your changes then self.slidingViewController will cease to work.
It's also very inefficient to keep calling it. Change to:
ECSlidingViewController *slidingController = self.slidingViewController;
[slidingController anchorTopViewToRightAnimated:NO];
slidingController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MESettingsNavigationController"];
[slidingController resetTopViewAnimated:NO];
After some further investigations, I found out that the top view controller's (MyViewController) presentingViewController property returns an instance of ECSlidingViewController instead of MESettingsViewController, even though it was the MESettingsViewController that presented the MyViewController modally.
And because it's ECSlidingViewController that is actually presenting the modal view, the unwind handler wasn't working because it left the modal view on screen.
Solution:
Forget unwind segues, use the following code in the modally presented top view controller (MyViewController) to change the ECSlidingViewController's topViewController property:
- (IBAction)switchToTransitionsScreen:(id)sender
{
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"METransitionsNavigationController"];
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
Or use the delegate pattern and put it into MESettingsViewController, for example.
In case someone haven't found answer, I did it in this way.
1- #import "UIViewController+ECSlidingViewController.h" to your menuViewController 2- Set stroboardID of your destinationViewController to "someID" 3- When triggering some action, in backend, use this code:
if(self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered){
[self.slidingViewController anchorTopViewToRightAnimated:YES];
}
else{
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"someID"];
[self.slidingViewController resetTopViewAnimated:YES];
I got first ViewController with out navigation controller, I go from it to second ViewController via
if (!self.mapViewController)
{
self.mapViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MapViewController"];
}
[self presentViewController:self.mapViewController animated:YES completion:nil];
I send some data with prepareForSegue to it, but I need that it also be with navigation controller.
I embed in a navigation controller in storyboard but my code still called second ViewController with out navigation.
I am not a professional with the way of the storyboard, however i believe that instead of presentViewController
you should be using the following function to present a storyboard VC based on segues.
[self performSegueWithIdentifier:#"SegueIdentifierHere" sender:nil];
Make sure that in your storyboard you have incorporated a UINavigationControllerVC as well.
Give an identifier to the navigation controller in storyboard. Instantiate and present that.
UINavigationController *navVC = [self.storyboard
instantiateViewControllerWithIdentifier:#"TheNavVCWhoseRootIsMyMapViewController"];
self.mapViewController = navVC.viewControllers[0]; // your map vc is at the root
[self presentViewController:navVC animated:YES completion:nil];
You are using presentViewController:animated:completion which will indeed display a UIViewController without the UINavigationController the previous UIViewController was embedded in.
Try using the following:
[self.navigationController pushViewController:self.mapViewController animated:YES]