What does "presentedViewController" in UIViewController mean? - xcode

In Apple's developer documentation, the property presentedViewController in UIViewController is described as "The view controller that is presented by this view controller, or one of its ancestors in the view controller hierarchy". It is confusing. What does "its ancestors" mean?

This question is really old, but it was still the top Google hit for my search, and the existing answer is wrong and makes things even more misleading.
This property always returns a presented view controller (or nil), it will never return an ancestor view controller. What the reference to ancestors means is that if the view controller you call it on isn't presenting any view controller itself then it will walk up the hierarchy to see if any parent view controllers are presenting anything.
For a concrete example - let's say you have ContentViewController which is a contained within a UINavigationController. The NavigtionController is currently presenting another view controller (perhaps a UIAlertViewController for example).
In this case the presentedViewController property of both the ContentViewController and the NavigationController will return the UIAlertController that is being presented by the NavigationController. When this is dismissed it will return nil for both of them.
So to add a bit more clarity to the line in the Apple Docs:
"The view controller that is presented by this view controller, or [the view controller presented by] one of its ancestors in the view controller hierarchy."

There are two view controllers, viewA and viewB. viewA presents viewB. Now this is what the documentation is referring to:
Example 1 ( calling viewA.presentedViewController )
This will return viewB, because viewA presented viewB
Example 2 ( calling viewB.presentedViewController )
This will return viewA because viewA is an ancestor of viewB. In other words, imagine these two views in a line. viewB is second in line while viewA is first. So a view's ancestor is the view behind itself.
Only reason why this function call on viewB returns viewA is because viewB did not present any other view controller. Or in other words, viewB is on the top of the hierarchy. There is no view in front of viewB.

Related

Create a segue to a different NavigationController

I have an app that has two navigation controllers. I'm trying to create the segue shown in the red arrow below: Pressing the "+" button should take you to a specific view controller of another NavigationController.
I've been able to successfully go to this view controller but it doesn't have any navigation properties (Pressing "Back" doesn't do anything).
How do I jump to a view controller in a different navigation controller but still retain navigation properties?
I will say you want to move from controller A to controller B for reference.
Each UINavigationController instance has a viewControllers array that stores the controllers in the navigation stack.
In your case, the first navigation controller has 2 view controllers in the navigation stack, the same thing for the second navigation controller.
If you press back in the view controller B, it should pop to one of the view controllers in the UINavigationController stack to which B belongs. Since controller A et B don't belong to the same UINavigationController, hence you cannot access the pop API to move back.
One way is to present controller B modally from controller A instead of pushing it.

Getting nil when preparing for segue to navigation controller

I have found a few other questions about this same topic, so this is technically a repost, however the solutions provided there are not helping me at all. The solutions that have been suggested, which seem to work for the other users, are already present in my code.
Here is the code that is causing me problems.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let navView = segue.destinationViewController as UINavigationController
let destinationView = navView.topViewController as DetailViewController
And here is the error I am getting:
fatal error: unexpectedly found nil while unwrapping an Optional value
The error is thrown on the error where I am accessing navView.topViewController.
I have a table view segueing to a navigation controller, which then segues to my own custom view controller under it. I want to pass data to my custom view controller from the table view.
I had it working fine earlier today before adding the navigation controller in between, passing data directly from the table view to the navigation controller. Adding the navigation controller in between has broken things.
Are there any suggestions as to why I am having this problem? The only thing I can think is that the DetailViewController hasn't instantiated yet since it is two levels deep now. But then if that is the case, I don't know why it seems to be working for other people.
I have a table view segueing to a navigation controller, which then segues to my own custom view controller under it.
I believe that is your problem. The connection from the Navigation Controller to your Detail View Controller isn't a normal segue. It should be a Relationship Segue.
Click on the connection between the Navigation Controller and the Detail View Controller. Press delete.
Control-drag from the Navigation Controller to the Detail View Controller and select root view controller from the pop up.
Now navView.topViewController will be non-nil.

iOS8 UISplitViewController: How to switch to master view in compact width window?

My root view controller is an UISplitViewController, which has a UITableViewController as master view controller. On iPhone (compact width), it looks like a UINavigationController.
Tap a cell to show detail view controller
Tapping the trash button would delete the current note. My problem is how to go back to the master view after that? Since it's an UISplitViewController, it can't pop the current view controller as UINavigationController does.
I had a similar problem and finally found a solution. As I understand it, when in compact width, the detail navigation controller becomes a view controller of the master navigation controller. So all you have to do is:
Determine if only one view is present by checking the split view controller's collapsed property. If it isn't collapsed (e.g. on iPad), you're already showing the table view in addition to the detail view.
If it is collapsed (e.g. on iPhone) get a reference to the master navigation controller via the detail navigation controller and have it pop to its root view controller which, in this case the your table view controller.
This is the code I use in my detail view controller. In your case I think you just need to add this code to the button action in your detail view controller:
if splitViewController!.collapsed {
let detailNavController = parentViewController as UINavigationController!
let masterNavController = detailNavController.parentViewController as UINavigationController!
masterNavController.popToRootViewControllerAnimated(true)
}
Good luck!

Calling a function when a view controller is dismissed (xcode/iOS)

I have a main view controller. When a specific button is pressed, I present another view controller. When the second view controller is dismissed, I want to call a function in the main view controller. Originally I thought I could just use the completion handler for presentviewcontroller, but now I'm thinking that is not the way to go. Any tips on how I can accomplish this? I'm pretty new to this, so any help would be appreciated!
Assuming your main view controller is the second view controller's parent, you can access the method with:
[[secondViewController parentViewController] method];
Beware -- if you are using a navigation controller, the parent view controller will be the navigation controller. In this case, you will have to query the navigation controller's stack.
Good luck!

Tableview reloadData is not being executed -- sometimes

I have a view controller that is called from 2 different places.
1) I call it from a root controller. It is shown and populated. The add button works perfectly. I open a modal form, get the information and return it to the view controller via it's delegate.
- (void)itemsAddViewController:(AddItemView *)itemsAddViewController didAddItem
(OrdersDetails *)orderDetail;
{
if (orderDetail) {
[orderDetailItems addObject:orderDetail];
}
[self fetchOrderDetails];
[lineItemsTableView reloadData];
[self dismissModalViewControllerAnimated:YES];
}
However, when I call it from another view (on the right side of the split view), this same code does NOT reload the table. It adds the data -- if I leave the form and come back, the data is there, but the tableview is not being refreshed. When I step through the code, it gets the the line, but then goes over it like it doesn't see it.
When a modal view controller is presented over the view controller containing -itemsAddViewController:didAddItem: the underlying controller's view is not visible and will therefore be unloaded if the controller receives a memory warning.
As a result your view may not be loaded and your lineItemsTableView outlet may be nil when you call -itemsAddViewController:didAddItem:. Your call to reloadData would need to move to -viewWillAppear: to avoid assuming that your controller's view can have a persistent state when it is not visible.

Resources