How can I interact with a View I just showed using presentViewController? - ios

I've got a view that I use for adding data into a table. I show this view using presentViewController:
AddViewController *avc = [self.storyboard instantiateViewControllerWithIdentifier:#"addview"];
[self presentViewController:avc animated:YES completion:nil];
This UINavigationController is the root view controller for a UIViewController that displays the add controls.
I trigger this UINavigationController from a couple of different places in my project and I'd like to have slightly different text for each. How can I interact with the UIViewController from where I call presentViewController, or otherwise change UIViewController depending on where it was invoked?

Ok, you can access the rootView controller of your navigation controller like this:
UINavigationController *yourNavigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"yourViewControllerID"];
YourRootController *yourRootController = (YourRootController*)[yourNavigationController topViewController];
yourRootController.someproperty = someValue
Than, just change some desired property in the view controller that you just get.
Please, tell me if it was something like this that you were looking for.

UIViewController *controller= (UIViewController)[self.navigationController subviews] count-2 ];
controller is the second-last view controller on the stack. which is the one that called add.
depending upon the value for [controler class] , you can set the text.

Related

Navigation View Controller from Subview

I have been working on this problem for a while and thought I would ask for some help. I have 3 view-controllers: 1 Navigation Controller, 1 Main controller and 1 detail view controller.
Within the main view controller, I have a series of subviews with buttons. Due to the class structure, however, I am unable to directly call self.storyboard to get the current storyboard object.
I have tried 2 different methods, a variety of ways, and am still unsuccessful. I posted my methods below and described what is and what is not happening in each segment. The overall goal is to present a child view controller (the detail view) by tapping a button in a subview, of which does not have access to the parent storyboard directly.
Method 1
//Instantiate the new view controller
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
// Pass data into the new view controller
tempViewToShow.thisUser = self.postUser;
// Output a simple log to ensure both were created
NSLog(#"Temp User Name: %#, Profile Desc: %#", [tempViewToShow.thisUser getFullName], tempViewToShow.description);
// Using the AppDelegate for the RootViewController, present the detail view
[UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:tempViewToShow animated:YES completion:NULL];
Issues
The issue with this series is that the detail view does not carry the navigation controller (since it is not mentioned), however, this way still shows a full View Controller
Method 2
...
// Use the Delegate and the navigation controller to present the new view controller
[UIApplication.sharedApplication.delegate.window.rootViewController.navigationController presentViewController:tempViewToShow animated:YES completion:NULL];
Issues
Does not display anything
Method 3
// Use the recommended 'pushViewController' for the navigation controller to carry over
[UIApplication.sharedApplication.delegate.window.rootViewController.navigationController pushViewController:tempViewToShow animated:NO];
Issues
Does not display anything
En toto, how would I make this work? What lines would I modify and how? Thanks!
You can solve this issue like this:
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
UINavigationController *naviController = [[UINavigationController alloc] tempViewToShow];
And then do this :
[UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:naviController animated:YES completion:NULL];
You can create instance of storyboard from storyboard name.once you have correct storyboard instance, get NavigationController from Its identifier, and detailviewController from its identifier. Push detailviewcontroller on Navigationviewcontroller.
get storyboard-- replace name of your storyboard in "MainSToryboard"
UIStoryboard *storyboard =
[UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:[NSBundle mainBundle]];
get instance of Navigationcontroller - replace identifier:
UINavigationController *navController =(UINavigationController *)
[storyboard instantiateViewControllerWithIdentifier:#"navcontroller"];
get detailviewconrtoller :
UIViewController *detailvc=
[storyboard instantiateViewControllerWithIdentifier:#"profile"];
Push detail on current navigationcontroller:
[navController pushViewController:detailvc animated:YES];
I found an alternate solution. The cause was because the incorrect view controller was being called by
UIApplication.sharedApplication.delegate.window.rootViewController.*
The workaround is:
In the primary view controller class, I passed the displayed viewcontroller into the delegate class. Then, from the child class I wanted to call, I referenced that view controller, and navigation controller, and it worked just fine. My final code is below:
// Create the detail View Controller
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
// Set the user variable in the detail view controller
tempViewToShow.thisUser = self.postUser;
// Push the view controller into the navigation controller
// Note that del.currentNav comes from this code:
/*
* In this class, create the delegate reference
* AppDelegate *del = (AppDelegate *)[[UIApplication sharedApplication] delegate]
*
* In the Delegate class, get the set the current navigation controller {let currentVC : UIViewController = passedInVC}
* self.currentNav = currentVC.navigationController;
*/
[del.currentNav pushViewController:tempViewToShow animated:YES];

loading a view manually objective c

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

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

Programmatically changing view in ECSlidingViewController

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

Present modal view controller from ECSlidingViewController

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

Resources