popoverPresentationController is nil when used with a Navigation View Controller - ios

This code was working properly before I embed my view in a navigation controller
if segue.identifier == "PopupInfo" {
let controller = segue.destinationViewController as! PopoverInfoViewController
controller.popoverPresentationController!.sourceRect = sender!.frame
}
However, after adding the navigation controller, this slightly edited code is no longer working because popoverPresenetationController is now nil! I need to set its sourceRect programmatically not with the storyboard because the sender is a control inside a cell in a table view
if segue.identifier == "PopupInfo" {
let navcontroller = segue.destinationViewController as! UINavigationController
let controller = navcontroller.topViewController as! PopoverInfoViewController
controller.popoverPresentationController!.sourceRect = sender!.frame
}
Note: controller is not nil, only its popoverPresentationController property

You embedded PopoverInfoViewController in a UINavigationController. That navigation controller is what is directly embedded in a UIPopoverPresentationController. It should now have a non-nil popoverPresentationController property, as it's the view controller that is directly embedded in the popover controller. The setting of these kind of parent view controller properties do not propagate past the first child view controller. This is why navcontroller.popoverPresentationController will be non-nil while any child of navcontroller will have nil for popoverPresentationController.
You should use navcontroller.popoverPresentationController!.sourceRect = sender!.frame

Related

add navigation bar but there is no back button

I created a table view and from there let say a user pressed a cell it will go to ListTavleView but the only problem right now is that whenever a user is in ListTableView there is not back button even thought i already embed a navigation controller
and i want the fist view navigation bar is small title second view navigation bar is large title
enter image description here
Below is my code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
controller.item = items
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Below is my storybord setup
Navigation bar with no back button
From the image it seems that view controller is added as a child view controller in current view controller.
There is not need to embedded navigation controller when a cell is pressed becoz there is already a navigation controller at start point so no need to create a new one.(If you present a view controller then you may need to embed navigation controller.)
So the solution is...
Delete the navigation controller.
Connect directly to the destination view controller without navigation controller as there is already.
it is better if you use pushViewController, just get a reference of the other view controller, it will always a back button since you are pushing threw navigation Controller here is a simple example:
let story = UIStoryboard(name: "Main", bundle: nil)
let vc = story.instantiateViewController(withIdentifier: "ExampleViewController") as! ExampleViewController
self.navigationController?.pushViewController(vc, animated: true)
as for the back button, the issue is with your hierarchy.
are you changing the left item of navigation bar in another view controller that might affect navigation bar in your destination view controller.
You are pushing new NavigationController(say Nav.B) to the existing one(Nav.A).
Each navigation controller keeps different navigation stack. The back button is visible when you add viewcontroller to Navigation controller. Read more about UINavigationController.
For your current scenario, you could delete the second navigation controller(i think it not essential) & connect direct segue to ListTableViewController
So this
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
becomes
let controller = segue.destination as! ListTableViewController
When you need large titles(available 11+), you can add this line in viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
And if it needed only for this Viewcontroller, add in viewWillDisappear() or viewDidDisappear()
navigationController?.navigationBar.prefersLargeTitles = false
If you wanted to have navigation bar back button on next view, then just push the target view on navigation, it will show default navigation back button. No, need to any extra work.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
guard let controller = segue.destination as? ListTableViewController else {
return
}
controller.item = items
self.navigationController?.pushViewController(controller, animated: true)
}
}}
And if you are pushing the viewcontroller with segue, then no need to add below line self.navigationController?.pushViewController(controller, animated: true)

iOS (Swift): Presenting View Controller Embedded in Navigation Controller

I have a UIViewController (AVC) that is embedded in a UINavigationController. AVC (present modally) segues to another UIViewController (BVC). Inside BVC, the variable self.presentingViewController is of type an optional NavigationController rather than a AVC as I would have expected.
I have to downcast the first childViewControllers as an AVC as follows:
let pvc = self.presentingViewController
if let avc = pvc?.childViewControllers.first as? AVC {
// ...
}
Why is self.presentingViewController not as I expected it to, i.e. an AVC?
Many thanks.
To access it
if let pvc = self.presentingViewController as? UINavigationController {
if let avc = pvc.viewControllers.first as? AVC {
// ...
}
}
//
From Docs
When you present a view controller modally (either explicitly or
implicitly) using the present(_:animated:completion:) method, the view
controller that was presented has this property set to the view
controller that presented it. If the view controller was not presented
modally, but one of its ancestors was, this property contains the view
controller that presented the ancestor. If neither the current view
controller or any of its ancestors were presented modally, the value
in this property is nil.

How can I pass data to a segue when the destination controller is embedded in a navigation controller

I need to segue to destination view controller (which is a tableview controller) to which I need to pass some data. I also need to have a navbar in the destination view controller. So I embedded the destination view controller in a navigation controller. segue.destination now points to the embedded navigation controller. It does not help to access the destination controller directly since it probably creates another instance. How can I either (1) create a navigation bar directly in the destination view controller (which is a tableview controller) or (2) send the data to the destination controller
You can cast the segue.destination to a UINavigationController and then the the navigation controller's root view controller to a view controller class of your choice.
switch segue.destination {
case let navigationController as UINavigationController:
if let controller = navigationController.viewControllers.first as? UIViewController {
// controller.property = value
}
}

how to find the viewcontroll from a segue in iOS

I need to assign data source to a view controller segued by press a button in appdelegate.swift. How can I find the view controller by a segue identifier. I know I can find in a navigation controller by
let navigationController = window!.rootViewController as! UINavigationController
let controller = navigationController.viewControllers[0] as! ListViewController
like this. Is there a way to find a view controller when only the segue is connected with the previous view controller?
In other words, how to identify any vc in a project? Is storyboard ID used for this?
let viewController = segue.destinationViewController as! ListViewController

No back button on segue in Swift 2

I just ported my project over to Swift 2, and everything is working great - except that even the most simple segues have no back button. Here is the prepare for segue function that I am using:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if segue.identifier == "showExercise" {
if let nav = segue.destinationViewController as? UINavigationController {
if let exercisesController = nav.topViewController as? ExercisesController {
let cell = sender as! WorkoutCell
if let workout = cell.name!.text {
exercisesController.exercises = Workouts[workout]!
exercisesController.navigationItem.title = workout
}
}
}
}
}
Before, the back button to the parent segue used to automatically populate. Now, all I get is the title in the child navigation vc
Are you using show or show detail segue? It seems like you are using a modal segue. Destination view controller for show or show segue is usually the second view controller itself, and not embedded in another UINavigationController.
If your destination view controller for the show segue is really a UINavigationController, the new navigation controller's navigation bar settings may override the old one (the navigation controller of the source view controller). Try not embedding your destination view controller in another UINavigationController.

Resources