Perform Function when Popover is Dismissed - ios

I have a popover that is using the same view controller that calls it.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "groupSelect" {
let popVC = segue.destination
popVC.popoverPresentationController?.delegate = self
}
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
However, because I used the storyboard to make everything, I'm not really sure how or where I can take over or add code when the popover gets dismissed. In Xcode 8.2 with Swift 3, adding a popover with the storyboard editor automatically makes the popover dismiss when the user touches outside the popover. Everything is working great, the only problem is that when I return from the popover, the table under it won't have the reload function called so any changes made in the popover don't take effect for the user.

Implement UIPopoverPresentationControllerDelegate method popoverPresentationControllerDidDismissPopover which tells the delegate that the popover was dismissed.

Related

UIPresentationController delegate how to set

In iOS 13, viewWillAppear is not called when dismissing view controller. As a workaround, it is mentioned to override UIAdaptivePresentationControllerDelegate delegate, but it's not working for me. What am I doing wrong?
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MyVC" {
let destination = segue.destination as! MyViewController
destination.presentationController?.delegate = self
}
}
And then,
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
resumePipeline() //<--Does not get called
}
What am I doing wrong?
You're probably assuming that presentationControllerDidDismiss is always called when the dismissal takes place. That's a false assumption. It is called when the user drags down on the presented view to dismiss it.
You need to think of the presented view controller as if it were a popover. It isn't completely replacing the presenting view controller's view; it just covers it partially. So there is no viewDidAppear call, because the main view never disappeared.
Either you need to go back to forcing your presented view controller to be fullScreen or you need to adapt your architecture to work with the new style of presented view controller.

Setting up delegates in container view

I have a container view in my Storyboard that displays another view controller that I already programmed and stuff. I want to communicate between the main View Controller and the contained-view controller. I know how to use delegates and I am comfortable with using them, however I normally set up delegates when I initialize a ViewController, however in this case I don't know where to apply this, since the view controller is already there per the storyboard. Normally I would do something like this:
class HomeVC: UIViewController {
func initializeVC() {
resultsVC = self.storyboard?.instantiateViewController(withIdentifier: "resultsView") as! GoalsVC
resultsVC.calcDelegate = self //I set the "HomeVC" as the Delegate since it has all the functions I need
}
}
As mentioned above, since I never really created this view controller via code, I don't know how to assign a delegate (specially setting the delegate to "self" (where Self is the main View Controller)
You can assign delegate in prepareforsegue. Like below code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "container_segue") {
let controller = segue.destination as! containerController
controller.delegate = self
}
}
When project runs, this method called automatically because we had created segue in the storyboard.
By using segue.identifier you can check for which controller segue is going to happen and accordingly you can achieve your requirement.
As you are using storyboard for container view. There is a segue with embed type. Give this segue a identifier, say MyContainedViewControllerSegueId
Then in prepare(for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MyContainedViewControllerSegueId" {
// here you get your contained view controller as `segue.destination`
// cast it your subclassed view controller
// use delegate on that subclassed view controller for communication purpose.
}
}

Popover covers entire screen iOS

I am trying to implement a popover whose anchor is a bar button item for my iphone app. I have connected the bar button item to the view controller via a segue pathway configured as show as "present as popover." I have also set my own size for the view controller and selected "use preferred explicit size." Based on previous posts about this similar topic, I have implemented the following code for my popover. However, the popover is still covering the entire screen, probably because my adaptivePresentationStyle method is not being called ("hello" does not print to the screen). Note that I have also implemented the UIPopoverPresentationControllerDelegate. Where did I go wrong?
override func prepare (for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popoverLogin" {
let popoverViewController = segue.destination
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.popover
popoverViewController.popoverPresentationController!.delegate = self }
}
// MARK: - UIPopoverPresentationControllerDelegate method
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
// Force popover style
print ("hello")
return UIModalPresentationStyle.none
}
Thanks!
In my case your code works and popover is presented normally!
Make sure your segue has the right "Kind": "Present As Popover".

How to detect when a popover is dismissed in iOS 9

I'm updating an app to use universal storyboards. I've created a popover segue to a new viewcontroller using interface builder by dragging from a button to my new viewcontroller and selecting 'Present As Popover' as the kind of segue.
When the user presses outside of the popover (dismissing it) I need to be notified in the presenting view controller so I can undo their actions. How can I do this?
Normally I would have created the popover manually and made my viewcontroller the popover's delegate; allowing me to use the popoverControllerDidDismissPopover delegate call back. However, this is deprecated in iOS9 and even if it wasn't I've no idea where to find the popover so I can set its delegate to my view controller.
Not sure which method you're referring to as being deprecated but you can still use the UIPopoverPresentationControllerDelegate to achieve this. Something like:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let vc = segue.destinationViewController
sortVC.modalPresentationStyle = .Popover
sortVC.popoverPresentationController?.sourceRect = filterButton.bounds
sortVC.preferredContentSize = CGSizeMake(216, 150)
sortVC.popoverPresentationController!.delegate = self
}
}
And then use the
func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController)
method to handle its dismissal.
The popoverControllerDidDismissPopover: method has been replaced by popoverPresentationControllerShouldDismissPopover: because UIPopoverControllerDelegate has been replaced by the UIPopoverPresentationControllerDelegate.
From your presenting view controller, conform to the new protocol and set the delegate for the popover presentation controller in prepareForSegue::
class MyPresentingViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let popoverPresentationController = segue.destinationViewController.popoverPresentationController {
popoverPresentationController.delegate = self
}
}
func popoverPresentationControllerShouldDismissPopover(popoverPresentationController: UIPopoverPresentationController) -> Bool {
return true
}
}
You can then use the delegate method to handle detection of the dismissal in the way that you were previously intending.
The updated answer for this issue.
All credit to this answer:
The method you must use on iOS 13:
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
The UIPopoverPresentationControllerDelegate inherits from UIAdaptivePresentationControllerDelegate which contains the presentationControllerShouldDismiss and presentationControllerDidDismiss as Beto points out.
I simply moved the code I had in the popover versions of these functions to UIAdaptivePresentationControllerDelegate version and they work exactly the same as before.
Did not have to change the delegate declarations on the view controller or set isModalInPresentation.
The original code still worked under 13.2.3 but those function are depreciated and one day they will stop working...or not?
my app is an iPad app using popovers and not presentation sheets or card styles.

Why is 'present as popover' segue covering the whole screen?

In my project I have a button on the bottom right side of the screen and i added another uiviewcontroller to the storyboard, did control-drag to the uiviewcontroller I wanted as the popover, then set that viewcontroller size to (300, 300) and checked 'use preferred explicit size'. When I load the app and click the button, the entire screen gets covered by the "popover". I also tried to go into the popoverViewController's .m file and set the size but that didn't work either.
Any ideas?
Edit: Since it looks like I have to have it be full screen that is fine however I am still running into some other problems I was having earlier. My popup screen will come up and I make the background black and alpha as .5 to make it see through however it'll do the animation, then once the animation is finished the screen will go from .5 opacity to completely black and the only thing I can see is the battery icon thing.
The OP uses Objective-C. This answer presents code in swift. Converting swift to Objective-C should be easy.
In the newly added ViewController, under “Simulated Metrics” change “Size” to “Freeform” and “Status Bar” to “None.”
Under “Simulated Size” change your view’s height and width to the actual size you want your popover’s content to be.
Create a segue to the newly added VC. Use segue type as "Present As Popover" and give a name for the segue, for example "popoverSegue".
In the ViewConroller from which this segue is to be triggered, add the UIPopoverPresentationControllerDelegate protocol.
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
}
Override the prepareForSegue function to catch your popover segue. Set the modalPresentationStyle to .Popover to explicitly state that you want a popover and then assign the delegate property of the view’s popoverPresentationController to self:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let popoverViewController = segue.destinationViewController as! UIViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
}
}
Implement the adaptivePresentationStyleForPresentationController function to tell your app that you really want that popover presentation and will accept no substitutions:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
Following these, I could get a popup on iPhone which is not full screen but the size set for the ViewController.
Source: iPad Style Popovers on the iPhone with Swift
Thanks to Bharat for the great answer, I personally use a UIStoryboardSegue that does pretty much the same thing. That way, I can change the class of the segue in the storyboard, have what I want, and not pollute my controllers:
class AlwaysPopupSegue : UIStoryboardSegue, UIPopoverPresentationControllerDelegate
{
override init(identifier: String?, source: UIViewController, destination: UIViewController)
{
super.init(identifier: identifier, source: source, destination: destination)
destination.modalPresentationStyle = UIModalPresentationStyle.popover
destination.popoverPresentationController!.delegate = self
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
}
Swift 3-5 version
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SEGUE_IDENTIFIER" {
let popoverViewController = segue.destination as! YourViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.popover
popoverViewController.popoverPresentationController!.delegate = self
}
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
Swift 4 Version
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SegueIdentifier" {
let popoverViewController = segue.destination
popoverViewController.modalPresentationStyle = .popover
popoverViewController.presentationController?.delegate = self
}
}
Don't forget to add
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
On iPhone you can creat a custom view controller that can manage all the popovers. Since each view controller has its own navigation controller, you can add a new view controller to the app.window.rootviewcontroller as a du view and bring all to front.
If you didn't want to write your own, you can use something like this for instance: http://cocoapods.org/pods/FPPopover
This is Swift 5 code, some/most of the above mentioned solutions are all valid. This is an effort to present whole solution. This example supposes you are using a xib for popover view controller but this would work otherwise as well, say, in prepare for segue. Here's a complete code:
Presenting ViewController:
let popoverVC = PopoverVC(nibName: "popoverVC", bundle: nil)
popoverVC.completionHandler = { [unowned self] (itemIndex : Int?) in
if let itemIndex = itemIndex
{
// Do completion handling
}
}
popoverVC.preferredContentSize = CGSize(width: 200, height: 60)
popoverVC.modalPresentationStyle = .popover
if let pvc = popoverVC.popoverPresentationController {
pvc.permittedArrowDirections = [.down]
pvc.delegate = self
pvc.sourceRect = button.frame
pvc.sourceView = button // Button popover is presented from
present(popoverVC, animated: true, completion: nil)
}
This is important:
extension ViewController: UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}

Resources