Get a reference to the currently visible view controller - ios

I have a UIAlertView. When the user taps a button in the alert view I want to show a new UIViewController.
In order to achieve this I need to know which view controller is currently visible on screen because that particular view controller is the right one to present the new view controller.
The problem is that I have a complex hierarchy of view controllers in my app including a UINavigationController and a UITabBarController (among others). So I cannot simply use self.visibleViewController to get the currently visible view controller.
I have found a possible solution on Stackoverflow but I would like to find a neater solution without having to dig through the whole view controller stack.

UINavigationController has a property called topViewController.
Maybe it helps you.

Related

Is it possible to segue from a modal view to a view that is not in the hierarchy on a Navigation Controller in swift?

On my main view in a NavigationController I have a button that brings up a new view modally to submit a post. I have a button to dismiss this view which brings the user back to the main view, however if the users proceeds to make a post, I want it to dismiss the view and proceed to a show the post in the Navigation controller.
The ideal effect would have the standard navigation controller's back button on the top that can bring the user back to the main view when they have finished looking at their post.
I have tried several different methods, but I tend to get an error stating "Warning: Attempt to present (The View I Want to Show) whose view is not in the window hierarchy!
Thanks for any info!
Yes, it's possible. This is the best way I've found to structure the app to allow this functionality.
RootViewController
UINavigationViewController
ContentViewController (your current "main" view controller)
You're basically going to create a new RootViewController that is going to contain your current UINavigationController as a child. I usually setup the window.rootViewController programmatically in the AppDelegate. I would also keep a global reference to it.
Have a look at Apple's documentation on this. Basically, the RootViewController code would look like this:
[self addChildViewController:navController];
navController.view.frame = self.view.bounds
[self.view addSubview:self.navController.view];
[navController didMoveToParentViewController:self];
Now, whenever you need to present a modal view controller, present it from the RootViewController, instead of the current/top view controller on the UINavigationBar. This allows you to manipulate the UINavigtaionController independently and you won't get the error you're seeing.
So present the modal view controller from the RootViewController, this covers the UINavigationController and its content (top view controller). Make any changes you need to the UINavigationController stack (push, pop, etc...) with no animation. When you're done dismiss the modal view controller (with animation) and it will show you're adjusted UINavigationController.
Hopefully, this all makes sense. It's kind of complex and hard to explain in text. :p
If you don't want to setup a RootViewController, you might want to try presenting the modal view controller from the UINavigationController, as that might have the same effect. From the view controller just do self.parentViewController.present.
The main thing you're trying to avoid is presenting a modal from a view controller and then removing that view controller (pop) before the modal is dismissed. The view controller that presents the view controller is responsible for dismissing it.
I finally got it all working (I'm still new to Swift) and the two comments on the question were very helpful. The most helpful of all was the top answer here's example on github.
Similar question

Difference between navigation controller and viewcontroller?

Difference between navigation controller and viewcontroller?
I mean how can we decide when to use navigation controller or a normal view controller?
Just my two cents:
A UIViewController represents a single view and you can put buttons in this view controller to segue to another UIViewController. If you want to segue back to the first UIViewController, you will have to worry about putting a button in the second view controller that leads back to the first. If you are drilling down into view controllers, this can be tedious having to remember to give the user a way back to a previous view controller.
A UINavigationController does a lot of this tedious work for you. As mentioned, it contains a stack of UIViewControllers. It will create a navigation bar at the top that will allow you to easily go back up the hierarchy of view controllers.
In short, if you have a hierarchy of view controllers that you want the user to easily navigate around, inbed your UIViewControllers into a UINavigation controller.
UINavigation Controller is a combination of 2 or more view controllers,those are connected through "segue" feature of "Ios". Benefit of using Navigation Controller is that we can navigate between different screens easily with default "Back" button on each screen . We don't need to give any individual button to move back onto previous screen.
Whereas a ViewController provides a single screen & we can connect more screen using "segue" but we also have to design a "Back" button to navigate onto previous screen.
We should use Navigation Controller , in case where one option resides into another one.Like in an iPhone settings ->Mobile Data Options->Voice->4G or 3G or 2G. It's a hierarchy of menus so here navigation Controller is better option than using UIController.
We should use UiController with "segue " , in case where
we have to choose one option among multiple.Like -
Photos ->There are many folders in which , any one is selected, that are Favourites or People or Places .
Here's a very brief, high-level overview.
Whereas a UIViewController can be thought of as representing a single 'screen', UINavigationController, as the name implies, is used as a means of being able to navigate multiple 'screens'.
From the documentation:
The UINavigationController class implements a specialized view controller that manages the navigation of hierarchical content. This navigation interface makes it possible to present your data efficiently and makes it easier for the user to navigate that content. You generally use this class as-is but in iOS 6 and later you may subclass to customize the class behavior.
Please see the rest of the UINavigationController documentation here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/index.html
Okay, Thank you everyone for helping me to find out a clear answer on this.
Navigation Controller consists of navigation bar and tool bar to move in and out from view controllers present in navigation stack.Therefore there can be many view controllers in Navigation Controller.
In view controller we don't have this facility and it represents a single screen view.
Please correct me If I am wrong.
See the Navigation Controller discussion in the View Controller Catalog.
Bottom line, a navigation controller actually is a view controller, but it just happens to be one that presents and navigates between other view controllers.

Adding Popover to current Navigation Controller hierarchy

I've seen a lot of other questions on here about adding a UINavigationBar to a UIPopoverController. All of the examples I've seen follow one of two patterns:
In the init or viewDidLoad method of the Popover subclass, you alloc-init a UINavigationBar directly, as suggested here. This method is a little hacky, and while it shows up nicely, if the popover is a UITableViewController, you have to mess with a bunch of things to make sure the navigation bar you just added doesn't overlap one of your cells.
Alternatively, a lot of post suggest creating a UINavigationController just before presenting the popover, as shown here.
With the second method, however, won't the popover be the only controller in the newly created navigation controller? And if my view that I'm presenting the popover from is itself already in a navigation controller, the popover will NOT be in that same navigation controller, correct? It seems to be that the more appropriate thing to do would be to add the popover being created as another controller in the navigation controller that already exists (and which the controller that presents the popover is already a part of). Is that possible? Or is there a reason why the navigation controller for the popover needs to be independent from the navigation controller for the presenting controller? Or am I totally missing something here?
You have many questions, young Skywalker. :)
Creating a UINavigationController and then embedding the controller you would like to present is the way to go.
Don't get confused by all the controllers involved here:
UIPopoverController is a construct that shows an existing UIViewController in an overlay like style. UIPopoverController itself even isn't a subclass of UIViewController. The name is misleading.
So UIPopoverController hosts another controller. In your case, we let it host a UINavigationController.
UINavigationController is a subclass of UIViewController. It is a container controller and can handle a stack of UIViewControllers.
On that stack we push one UIViewController: the one you want to display and garnish with a UINavigationBar. Since Mr. UINavigationController comes with a build in UINavigationBar, he's our friend.
There is no need to subclass UIPopoverController. You just keep one static reference to it around so you can dismiss the current open popover in case you want to present another.
It does not matter where you present the UIPopoverController from. It will always be a popover. Even if presented from an existing UINavigationController. Only if you use presentViewController: you will get different results depending on the controller you're presenting from (modal or pushed on top of the stack).
won't the popover be the only controller in the newly created navigation controller?
No, the popover will contain the navigation controller and the navigation controller will only contain its root view controller (which would otherwise have been added directly to the popover as its root).
You seem to be a little confused about the relationship between the popover and the popover root view controller...
the popover will NOT be in that same navigation controller, correct
Yes, correct. The popover is effectively a window floating above all other views
Or am I totally missing something here?
Maybe... The popover would usually be used for displaying something modal, transient and smaller than full screen size. Putting a navigation controller in the popover and adding views to it is the normal approach.
Adding a navigation bar to a popover isn't hacky. A navigation bar is just another regular view. That also means that using a UITableViewController with it, the navigation bar will overlap the table view, as the UITableViewController's view property just returns the controller's tableView property. If you want to add a navigation bar above a table view, without it overlapping the table view, use a regular UIViewController and add your navigation bar and table view the normal way. UITableViewController should only be used if your only view within that view controller is a table view.
Having said that, I do agree with others that just using a navigation controller without using its navigation features is the most common approach.

How to determine which view controller is currently active/the one displaying a view?

In my app I am queueing some local notifications, when they fire I must present a modal view. The trouble is I have numerous view controllers any one of which could currently be active and thus the one that needs to present the modal view controller. How can I determine which one is currently in use?
I am setting a navigation controller as the windows root view controller, and this can push any number of other view controllers, some of them themselves may also be currently presenting another view controller modally.
This must work on iOS 4 and 5.
I have a lot of view controllers so would like to avoid putting code in each of them to each check if they are currently the top one.
You can look at the navigation controller's topViewController property to find out which controller is at the top of the stack. This will be the one whose view is displayed.
Since you may also be presenting a modal view controller, you'll probably be more interested in the visibleViewController property, which will give you the controller for the current view whether its presented modally or pushed onto the navigation stack.
Create a variable that stores a pointer to the ViewController that was most recently pushed. Every time you push a new ViewController, update this variable. Then you'll always know which one is on the top!

Objective-C - Understanding view controllers

I understand that view controllers help control multiple views in an application, but I have trouble understanding when to use them.
If I have an application with a main page, several views with a "hierarchy" structure, and an about page not connected with the hierarchy, what files should my application have? An appdelegate, navigation controller and view controller? More than one view controller? Just a navigation controller?
Also, should they all be contained in one .xib file, or multiple .xib files?
Any help would be greatly appreciated.
Thanks.
A good habit is to have a UIViewController for each page you want to show. If I get the structure of your app you should have a main page (with many other UIViews inside it) and another page (about page). If that's true I suggest two UIViewControllers.
The UINavigationController is a subclass of UIViewController that lets you "navigate" among the pages. It's not strictly necessary but suggested (you can also implement your self a custom navigation system, but it's easier to exploit the one Apple offers you). Another navigation system is the one based on UITabBarController, if you want to take a look.
Assuming I get the structure of your app you should need two .xib file, one for each page you have.
The app delegate is conceptually different from a view controller, you'll have just a single app delegate, automatically created by Xcode (you can, of course, modify it to fit your needs).
Each "screenful of content" (Apple uses this term) should be handled by it's UIViewController or more likely a subclass of it. The point of view controller is to handle view appearing or disappearing (going on/offscreen), device rotation, memory management, navigating to other view controllers and so on. If you are creating your UI with IB, then each of those view controllers would most likely have it's own .xib file.
Each view controller has one view (it's view property) that acts as main view for each "screenful of content" to which you then add your subviews.
UINavigationController and UITabBarcontroller are there to help you control the hierarchy of your app. They only act as containers for other view controllers and don't contain any UI except navigation bar or tab bar. Using tab bar controller you can have multiple view controllers which act exactly like browser tabs. Using navigation controller you can have a stack-like navigation where new view controllers are pushed from right to left and are popped from left to right when user goes back to previous view controller. You can even have a view controller inside navigation controller inside a tab bar controller.
If you don't want to use tab bar or navigation controller, you can navigate through your view controllers by presenting them modally using presentModalViewController:animated: and dismissing by dismissModalViewControllerAnimated:. If you send YES for animated parameter of these methods, you will get an animation specified by the modalTransitionStyle property of view controller being presented or dismissed. Possible animations are slide in from bottom (default), horizontal flip of entire screen, fade in/out and half-page curl.
There are also some Apple-provided subclasses of UIViewController that help you setup your UI quicker like UITableViewController which is basically a view controller that contains a table as it's main view and conforms to 'UITableViewdataSourceanddelegate` protocols which are required to define how each cell looks and what it contains.
On iPad there is one additional container controller UISplitViewController and one additional way to present new view controllers using UIPopover.

Resources