Present modal view controller from ECSlidingViewController - ios

In my app I have an ECSlidingViewController declared as initial root controller via Storyboard. In my AppDelegate's didFinishLaunchingWithOptions method, I instantiate it as above:
self.slidingController = [[UIStoryboard storyboardWithName:#"AppStoryboard" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"ECSlidingViewController"];
What I want is to be able to show a global modal view controller (eg. when a push notification arrives while the app is active) without knowing which controller is currently top in the sliding controller.
What I do is (in my AppDelegate):
[self.slidingController.topViewController presentModalViewController:controller animated:YES];
but it doesn't seem to work.
Is there any way I could present a modal controller from my sliding controller regardless which controller is topViewController?
PS. If no, is there any chance that what I want will work with SWRevealViewController instead of ECSlidingViewController? If it's worth, I will take the painful road to switch.
Thank you in advance!

If the ECSlidingViewController is set as the initial view controller in the storyboard, then why are you instantiating another one in your app delegate code? By doing that, you're calling your methods on a different instance of ECSlidingViewController than the one that's put on screen by the storyboard. This is likely the source of your problem. Instead, get a reference to your ECSlidingViewController like this:
self.slidingController = self.window.rootViewController;
Then try,
self.slidingController.topViewController presentModalViewController:controller animated:YES];
or
self.slidingController presentModalViewController:controller animated:YES];
I haven't worked with ECSlidingViewController, so I don't know which of these might work.

Try this
UIViewController *rootViewController = self.window.rootViewController;
// You now have in rootViewController the view with your "Hello world" label and go button.
// Get the navigation controller of this view controller with:
UINavigationController *navigationController = rootViewController.navigationController;
[navigationController.topViewController presentModalViewController:controller animated:YES];

Related

Xcode 6 - Added Navigation Controller to storyboard, but not appearing in app

I added a Navigation Controller to my storyboard and it appears like so:
Now in the table view controller, I gave the TableViewController a storyboard id and class to a TableViewController Controller
When I run my app, I don't see the Navigation Bar at the top. This has been extremely frustrating and can't find a solution anywhere. PLEASE HELP
To get to the scene, someone clicks a button and this code runs and it goes to my Table View Controller:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
LHFileBrowser *LHFileBrowser = [storyBoard instantiateViewControllerWithIdentifier:#"FileBrowser"];
[self.navigationController pushViewController:LHFileBrowser animated:YES];
[self presentViewController:LHFileBrowser animated:YES completion:nil];
The error is in your code.
If you want to (modally) present a view controller when the user presses a button, you need to present the navigation controller (which will contain the table view controller), not the table view controller itself.
Right now, you're presenting the view controller, which won't show it being embedded in a navigation controller.
Also, you're mixing up two different approaches, by trying to push a view controller onto a navigation controller stack, and also presenting the view controller.
Code Sample:
Here's what you apparently mean to do:
UIStoryboard *storyboard = self.storyboard;
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:#"MyNavigationControllerID"];
LHFileBrowser *rootViewController = [navigationController topViewController];
// Configure your LHFileBrowser view controller here.
rootViewController.someProperty = ...;
// Modally present the embedded view controller
[self presentViewController:navigationController animated:YES completion:nil];
If you want to change the presentation or transition style, you can set those details in your storyboard.
You didn't explain why you had to programmatically add buttons, but Storyboard segues would have instantiated and presented an embedded view controller for you, without you having to have done it in code.
The more you can do in Storyboard, the less code you have to maintain, support, and update, and the more likely your app will still work properly when a new SDK is released.
Update:
The better way to do this is to let Storyboard do it for you, by adding a segue from the button to the navigation controller that you want to present.

Add Animation to View Change

So after a lot of research if finally found the code that allows me to change to another view without giving me any errors:
UIViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"gameOverPage"];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:vc];
The only problem with this code is that there is no animation. I want to somehow add the cross dissolve animation to this if possible.
Another major problem is that it shows the view two times (some times three). So it goes to the second view and then less than a second later, it shows the page again. I know this because iAd is reloaded and when I press a button that goes to another page, it is interrupted by the second page coming up again.
To change the view to another (navigating) you don't need to setRootViewController: set it as root view controller always.
you can use a UINavigationController and set a UIViewController as root, then to change view use pushViewController: method of navigation controller, like
//Pre condition - already a viewController is root view controller of navigation controller.
UIViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"gameOverPage"];
[self.navigationController pushViewController:vc animted:YES];
Another way is,
[self presentViewController:vc animated:YES completion:nil];
Read more presentViewController, UINavigationController

How to switch between view controllers and get rid of the previous one

In android, switching between activities, is fairly straightforward
you call
Intent intent = new Intent(this,NextActivity.class); <- define the next activity
startActivity(intent); <- start the next activity
finish(); < -get rid of the current activity
now in iOS i know how to do this:
UIViewController *nextviewcontroller = [[UIViewController alloc]initWithNibName:#"nextvc" bundle:nil];
[self presentViewcontroller:nextviewcontroller animated:YES completion:nil];
How do I get rid of the current view controller? so that currentviewcontroller dies after presenting nextviewcontroller ?
[self dismissViewController:YES]; doesnt seem to do the trick
the lifecycle methods viewWillDisappear and viewDidDisappear are called even if I don't call [self dismissViewController:YES];
i want "currentviewcontroller" to be removed from the memory, and from the viewcontroller stack, so that clicking "back" in "nextviewcontroller" will go to some thirdviewcontroller that was before currentviewcontroller
In iOS is different, since there's no concept of Activity and everything is more focused on the app itself (in Android you can mix activities from different apps). Therefore, there's no concept of "view controller stack".
The most similar concept is the "navigation stack" of navigation controllers, where you actually push and pop new view controller into some kind of linear navigation. A navigation bar is automatically created and populated with back buttons.
presentViewController will show your view controller modally upon the current one, but you can't thrash the presenting one since it's holding and containing ("defining context") the new one.
If you use a navigation controller for your navigation hierarchy (I don't know if you can), you can override the back button and use something like
UIViewController * prev = self.navigationController.viewControllers[self.navigationController.viewControllers.count -2 ]
[self.navigationController popToViewController:prev animated:YES]
With a modal view controller, you may try something like (I haven't tried but it may work)
[self.presentingViewController.navigationController popViewControllerAnimated:YES]
You should write one of these code into the target action of your close button.
iOS doesn't maintain a global stack of controllers in the way that Android does. Each app shows a controller at its root, and that one is responsible for showing the other controllers in the app. Controllers can display other controllers modally using presentViewcontroller:animated:completion: but the presenting controller remains underneath the presented one.
If your current controller is the root controller, then instead of using presentViewcontroller:animated:completion: you'd just do this:
self.view.window.rootViewController = nextViewController;
It's very common for the root controller to be a UINavigationController, which does manage a stack of controllers. If that is the case, and if your current controller is at the top of the stack, you'd do this:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:nextViewController animated:YES];
If your setup is different, you'd do something different; it's hard to say what without knowing more. But it's most likely that you'd be in the UINavigationController case.
In the viewDidAppear of your nextviewcontroller you could add :
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObjectAtIndex:[controllers count]-2];
self.navigationController.viewControllers = newViewControllers;
}
There is nothing available like this in iOS but you can achieve it doing something like below
NSArray *viewControllers=[self.navigationController viewControllers];
NSMutableArray *newControllers=[[NSMutableArray alloc] init];
for(int i=[viewControllers indexOfObject:self];i<viewControllers.count;i++){
[newControllers addObject:[viewControllers objectAtIndex:i]];
}
[self.navigationController setViewControllers:[[NSArray alloc] initWithArray:newControllers]];
I have tried the method of storing all the view controllers in an array but it didn't work for me . When you try popViewController it will move to the View Controller which is last in the stack.
You can make 2 navigation controllers and switch between them and also switch between the view controllers of a particular Navigation Controller.
For eg.
You can switch between 2 Navigation Controller using the following code:
FirstNavController *fisrtView=[storyboard instantiateViewControllerWithIdentifier:#"firstnavcontroller"];
self.window.rootViewController = firstView;
}else{
SecondNavController *secondView=[storyboard instantiateViewControllerWithIdentifier:#"loginnavcontroller"];
self.window.rootViewController = secondView;
}
If your FirstNavController has 2 ViewControllers then you can switch between them using pushViewController
SecondViewController *sc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
[[self navigationController] pushViewController:sc animated:YES];
and popViewController
[self.navigationController popViewControllerAnimated:YES];

Memory management - How to show an already instantiated ViewController without creating a new instance

I am having major memory management issues. After small use of the program it will crash running out of memory. I have finally found the cause, every time I create a new ViewController rather than accessing the instance, I am creating a new instance.
So app loads and instantiates the FirstViewController. You click a button which instantiates FilterViewController. From here when going back to FirstViewController I am creating a new instance of this as follows:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName
:#"MainStoryboard" bundle:nil];
FirstViewController *fvc = [storyboard
instantiateViewControllerWithIdentifier:#"FirstViewController"];
fvc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
And repeat process. Any way of presenting the view controller without re-instantiating it? I am close to submitting the app (tomorrow hopefully) so I need to try get this sorted. Thanks!
Here is the presentation of the ViewController.
[self presentViewController:fvc animated:YES completion:nil];
Presenting FilterViewController from FirstViewController
- (IBAction)searchOptions:(id)sender {
FilterViewController *ctrl = [[FilterViewController alloc] init];
[UIView transitionFromView:self.view toView:ctrl.view duration:1 options:UIViewAnimationOptionTransitionCurlUp completion:nil];
self.filterViewController = ctrl;
[self.navigationController pushViewController:self.filterViewController animated:NO];
}
If you're using presentViewController, you get back to the previous view by calling [self dismissViewControllerAnimated:YES];. You would do that in the method where you're currently creating the new controller.
If you are pushing into a navigation controller you would pop from the navigation controller: [self.navigationController popViewControllerAnimated:YES];.
Based on your last update it seems like you don't have a navigation controller and you're just adding the view as a subview and storing the filter view controller. That makes life more complicated really and the correct way to remove it is to setup a delegate relationship so that the filter view controller calls back to the first view controller when it's done and the first controller then transitions the views and nil's the reference.
If you can, change to use a navigation controller properly. You already have half the code, but the first view controller seems to not be in a navigation controller. If you use a nav controller life will be easy...

Clarification on prepareforsegue and presentModalViewController while using Storyboard

I have an app with a LoginViewController as the initial view.
Note: So in appDelegate.m, self.window.rootViewController is NOT the TabBarController.
After Auth, I present the main part of the app, which has a tabbarController (identifier:tabBar) with two tabs and one tab has a navigation controller. I am using Core Data, so I need to pass MOC.
If I use,
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UITabBarController *obj=[storyboard instantiateViewControllerWithIdentifier:#"tabBar"];
[obj setSelectedIndex:0];// Which tab to show first
[self presentModalViewController:obj animated:YES];
It works good visually. Now I need to pass the MOC. Read about PrepareToSegue method,created a segue (modal, Not shown in pic) from loginVC to my TargetViewController (TabBar>NavigationController1>View1), named the segue "LoginSegue" and used the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
View1 *devicelist = (View1 *)[[navController viewControllers] lastObject];
devicelist.managedObjectContext = managedObjectContext;
}
The TabBar does not show. How do i set the tabbar controller in this case?
I have been trying to get a grasp on getting a reference for the Modal Tabbar, but still not clear. Can some one explain in layman terms how to handle a situation like this?
I think it would be better to use a design that doesn't use a modal transition to the tab bar controller. Modal presentations are generally supposed to be for interruptions to the normal flow of the app, not for getting your main controller on the screen. There are two alternatives, that I think are better. You can leave the login controller as the initial root view controller of the window, but then switch it out for the tab bar controller (which will be the new root view controller of the window, and the login controller will be deallocated). This usually works ok, but I think in this case where you want to pass the MOC from the app delegate (I presume) to a controller in the tab bar controller, I think a second way would be better.
The second way to do this, and the way I usually do login controllers, is to have the tab bar controller be the root view controller of the window, and then present the login controller modally from the viewDidAppear method of the initial view (which would be the one you're calling View1). If you do this presentation with animation set to NO, the login controller will be the first thing the user sees:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static int first = 1;
if (first) {
LoginViewController *login = [self.storyboard instantiateViewControllerWithIdentifier:#"Login"];
[self presentViewController:login animated:NO completion:nil];
first = 0;
}
}
The if statement is in there so the presentation doesn't happen again when you come back from the login controller (you could do something more sophisticated like having a delegate call back to View1 from the login controller indicating that the login was successful if you want, but this works).
If the login succeeds, you just dismiss the login controller, and you'll be there in your first view (if it fails, you just never dismiss it, and maybe put up a message saying the login failed).
If you go this route, then you can pass the MOC in the app delegate like this:
UINavigationController *nav = [(UITabBarController *)self.window.rootViewController viewControllers][0];
View1 *devicelist = (View1 *)nav.viewControllers.lastObject;
devicelist.managedObjectContext = managedObjectContext;

Resources