ECSlidingViewController with Push and Unwind Segue - ios

I am using ECSlidingViewController with storyboards. ECSlidingVC is my root (starting) controller. My left menu is a TableView with static cells, and my TopViewController is a navigation controller. I want to have a single NavigationController for all my app.
From my left menu i cant use push or unwind segues, i understand that part though. i can only use ECSlidingSegue which changes topviewController of ECSlidingVC and which destroys my navigation controller and it's stack.
i want to be able to go back from a menu item VC to previous VC in my main nav controller. lets say basically i want ECSlidingVC to not change topViewController but push destination viewController to my source.topViewController.navigationController.
Also i need to use unwind segues with my menu items. i need to go back to a VC in my main nav controller.
i inspected ECSlidingSegue source code and all it does is to replace topViewController.
is there a built in method (or segue) in ECSlidingViewController for pushing (or unwinding) VC into source.topViewController.navController or do i need to implement a custom segue myself?

I think the best way to go is for you to implement a custom segue yourself. Something like ECSlidingNavigationSegue, which would look for your topViewController, then check whether it's a UINavigationController and then push the destinationController to it.
It's basically the same perform method as the ECSlidingSegue, but with this feature of pushing a controller to the topViewController instead of replacing it.
Good luck!

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

Related

One segue to rule them all

I have 4 ViewControllers in my storyboard. I want all of them to be able to access my "Settings" ViewController by performSegue.
Is it possible to have ONE segue to perform this, instead of ctrl + drag from each and every ViewController to my "Settings" ViewController?
No its not possible with a single segue. You need 4 different segues from 4 different ViewControllers. But you can do this programatically.
Make an extension for UIVIewController
extension UIViewController
{
func showSettingsScreen()
{
let storyBoard = UIStoryboard(name: "YourStoryBoardName", bundle:nil)
let settingsScreen = storyBoard.instantiateViewControllerWithIdentifier("YourSettingsViewControllerID")
self.navigationController?.pushViewController(settingsScreen, animated: true)
}
}
Now you can call showSettingsScreen() from any of your view controllers(Make sure this view controller has a navigation controller).
You cannot do that. A segue has only one source and one destination. You could programatically instantiate your Settings ViewController and display it either by using push or by using present. What you should think of though is why do you have to go to settings from so many places. It might be a sign of bad design and duplicate code. Usually applications have only one such button/action that can be accessed from multiple screens (by using some kind of container view implementation) or from only one screen.
I really dont think so there is a way to do so. U ought to connect ur SettingsViewController to all of your 4 View Controllers, add segue , and define a segue identifier which is used in
prepareForSegue:sender:
or
shouldPerformSegueWithIdentifier:sender:
methods. U can access segues through these identifiers. If u find "ctrl + drag from each and every ViewController to "Settings" ViewController " tasky you can opt for Navigation Controller as well. U just have to embed Navigation Controller in your storyboard and define Storyboard Id for every View Controller and you are done. Just use storyboard id of view controller to be instantiated and u good to go.
ViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:vc animated:YES];
Apart for assigning storyboard id you dont have to worry about storyboard ,No ctrl+drag thing.
I thought of one more elegant solution i.e. using Container View. You can take button to switch, SettingsViewController as common, in your Container View Controller while displaying every ViewController.
happy Coding..
There is one way to do this. Create a root view controller and matching view which contains a single embedded view. Add your segue to this root controller. Then in your app you switch in the other view controllers using standard child container techniques. This is pretty much the concept that UINavigationControllers use.
One advantage from this is that if you want to have common elements which are visible to all controllers then you can add them to your root controller.
But it all depends on what you are trying to achieve.

UIStoryboard Show (e.g. push) simple inverse action to go back

In storyboard we have great feature that allow us to make Show (e.g. push). So seems the logic is next:
If we don't have navigation controller then view controller will use present modal logic. My question is there any inverse action that I can use with Show?
I have a UIButton that close current view controller screen:
- (IBAction)onTappedCloseButton:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
But in case if I don't have navigation controller, how can I simple use inverse action to go back? So my solution is to check if self.navigationController is nil then use dismissing option:
[self dismissViewControllerAnimated:YES completion:nil];
But maybe there is another cool solution like Show (e.g push). But Close (e.g. pop)?
Yes, you can use an unwind segue to go back, and it will be the reverse of whatever the forward segue was.
You have two options on how to do this:
1) The Unwind segue
To make an unwind segue you have to add a method in the view controller you want to "unwind" to with the following format:
-(IBAction)someSelectorName:(UIStoryboardSegue *)sender;
You will then be able to drag from your UIButton up to the "exit" icon in your storyboard.
Wire it up to the selector you just defined and UIKit will figure out how to get back to that view controller without you having to write any code. This can be especially useful as it can figure out when it needs to call -dismissViewControllerAnimated: more than once and can call those methods successfully. It can even unwind from within a view controller embedded in a navigation controller when the view controller you're unwinding to has the navigation controller presented on top of it. (i.e. it will do a dismissViewController instead of a pop to unwind)
2) The Custom unwind method
Say you don't want to or cant trigger this action from a storyboard. There is still an option and its detailed over at this question here:
Whats the programmatic opposite of showViewController:sender:
The gist is you can write your own generic dismiss method by implementing categories on the UIKit container View controllers (or on your own container)

Creating a central log out view controller in storyboard

I am working with Parse, and one thing I have implemented in my app is their built in PFLogInViewController. This controller will be presented at two times in the application - when the app first starts and the user is not logged in, and when the user taps the "Log out" button of my application (logging out takes them back to the PFLogInViewController, as you are required to sign in to use the app). I would like to set this up using Storyboard, as that is how the rest of my app is laid out. How could I set up a central view controller (a PFLogInViewController) that is accessed at these two times? I have already Subclassed PFLogInViewController and set it up, I just need advice on how to place it in Storyboard and how to connect it to my views. To make this question help as many people as possible, the general theme of my question is how does one establish a central Login/ViewController that can be accessed at different points in the application using Storyboard. Attached is the basic idea of what I'm trying to accomplish. I haven't been able to successfully segue to the initial TabBarController, and I'm not sure how I should make the LoginController the initial ViewController if I can't segue. I am programming in Swift, if it matters.
There are a few ways to do this depending upon your application. One way is drop a UIViewController onto the storyboard, but don't wire it up to anything (no segue). Create a storyboard id for it such as "MyLoginVC". Do the necessary subclassing of UIViewController and attach the class to your VC. Then, when you want to display the VC simply do the following or wire this up to your logout button
id destinationVC = [self.storyboard instantiateViewControllerWithIdentifier:#"MyLoginVC"];
[self.navigationController pushViewController:destinationVC animated:YES];
In addition, if you want to show the login VC as the very first VC when you launch your app, then perhaps in your AppDelegate
// Load Root view controller
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.rootVC = [storyboard instantiateInitialViewController];
self.window.rootViewController = _rootVC;
[self.window makeKeyAndVisible];
// Load Login view controller
id initialVC = [storyboard instantiateViewControllerWithIdentifier:#"MyLoginVC"];
[initialVC setModalPresentationStyle:UIModalPresentationFullScreen];
[_rootVC presentModalViewController:initialVC animated:NO];
When you finish with your login VC (i.e. successful login) then within login VC
[self dismissViewControllerAnimated:NO completion:nil];
and alternatively instantiate your first VC with something similar to the following from within login VC. Note, since you loaded the root VC above first, it is already there with the login VC sitting over it. When you dismiss login VC, the underlying root VC should be ready to rock and roll. Otherwise you can do the following:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
RootTabBarController *tbController = (RootTabBarController *)[self.storyboard instantiateViewControllerWithIdentifier:#"rootTabBarController"];
[self presentViewController:tbController animated:YES completion:NULL];
}
I think what you want is an unwind segue. Here are the instructions I follow for an unwind segue: https://github.com/bradley/iOSUnwindSegueProgramatically
If the link dies, here is what it said:
In your storyboard create two view controllers.
Subclass UIViewController twice, once for each of the view controllers in your storyboard.
Connect these view controllers to the view controllers in your storyboard.
Make a segue between the first view controller and the second by control+dragging from the first to the second.
Click on the segue you created and give it an identifier in the attributes inspector.
Make a button on the first view controller and link it to an IBAction in its UIViewController subclass.
When this button is pressed, the second storyboard should appear. To make this happen (we are doing it programatically) put the following into the implementation of the action you just created:
[self performSegueWithIdentifier:#"nameOfTheSegueBetweenOneAndTwo" sender:self];
Create a second method in the implemention of the first view controller with the following:
- (IBAction)returnToStepOne:(UIStoryboardSegue *)segue {
NSLog(#"And now we are back.");
}
This method will work to unwind any view controller back to this view controller. Notice that we implement the method in the view controller we wish to return to.
Go back to the storyboard. Focus in on the second view controller. If it is active, you should see a dark bar beneath it with 3 symbols on it. One of these is orange and when hovered over will show the name of the UIViewController subclass that this view controller represents. Control drag from this symbol woth the green symbol that means 'Exit'. You should see all available segue unwinds, which XCode automatically enumerates when you create segue unwind implementations inside UIViewController subclasses that you have shown on your stroryboard. Hence, you should see the segue 'returnToStepOne' as an option. Select it.
In your storyboard's document outline, find the section for the second view controller. You should see an item listed below it with a grey symbol that says something like "Unwind segue from ... to Exit." Click on this item.
Important and easily missed step follows!
On the right side of your storyboard, in the attributes inspector, you should see two fields. One for 'Identifier' and one for 'Action'. In most cases, the 'Action' field will have the text 'returnToStepOne:', which is what we want, but the 'Identifier' field will be blank. Fill this field with the text: 'returnToStepOne' (note that we leave out the colon).
Create a button on the second view controller and link it to an IBAction in its UIViewController subclass.
In the implementation for the method you just created, put the following code:
[self performSegueWithIdentifier:#"returnToStepOne" sender:self];
Run the application. You should now be able to unwind from the second view controller to the first.

Uncertainty with instances in navigation: UITabBarController

In my app, I have a UITabBarController that is set as the initial view controller and has the storyboard ID 'TabBarControl'. TabBarControl has 4 tabs, and off of the second tab(index 1) there is a sequence of UIViewController navigations that goes as such, where VC is ViewController..
UITabBarController > UINavigationController('branchesControl') > VC1('branchesView') > UINavigationController > VC2 > UINavigationController > VC3 > UINavigationController > VC4
I have a UIBarButtonItem named 'Confirm' on the navigation bar in VC4. The Confirm button triggers the following IBAction method:
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarControl"];
tabControl.selectedIndex = 1;
[self presentViewController:tabControl animated:YES completion:nil];
}
The goal is to navigate from VC4 back to the original 'branchesView'
The problem is, I've noticed that a manual set of a color of a UIBarButtonItem on VC2's navbar resets to a default white after hitting Confirm and then revisiting the view stack. I believe that I am creating multiple instances.
How can I simply navigate back to branchesView, and then VC2 without creating new instances?
PS I'm not sure if the new instance is TabBarController or or branchesView, I just know that it would seem that I am creating multiple instances somehow with the confirmClicked: method.
Instead of going back to the existing branchesView, you are presenting a new UITabBarController with a whole new set of child view controllers which is almost certainly not what you want to be doing.
Instead, you need to pop back to the already existing view controller that you want to get back to. You can do this either by reference or index, and in your case, by simply popping to the root (provided the view controller you want to get to is indeed at the root).
You can simply do:
[self.navigationController popToRootViewControllerAnimated:YES];
Your question also makes it seem like each view controller in the stack is wrapped in a new navigation controller. if that is the case, you will need to fix that as well. You should not nest UINavigationControllers. Instead, have a single one as the root for each tab and each time you want to push a new UIViewController onto the stack, you call:
[self.navigationController pushViewController:viewControllerToPush animated:YES];

Cannot programmatically perform segue

I have a storyboard segue with an identifier that is 'Push to ResumeView'.
I try calling it in the ViewController that I'm in at the point, by doing
[self performSegueWithIdentifier: #"Push to ResumeView" sender: self];.
But nothing happens?
I'd much rather just push the ViewController using the top NavigationController or something, but I can't see how to do that either.
Try implementing the shouldPerformSegueWithIdentifier:sender: or prepareForSegue:sender: methods in the 'from' view controller. Put a break point or NSLog() inside the method to inspect the segue identifier. This will prove that you indeed set up the segue correctly in the storyboard.
If you want to manually push your next view controller to the top of the navigation controller, use pushViewController:animated:. However, if you are using storyboard, the preferred way is to use segues.
Try this one.
UIViewController *yourResumeView=[self.storyboard instantiateViewControllerWithIdentifier:#"PushToResumeView"];
[self.navigationController pushViewController:yourResumeView animated:YES];

Resources