uipopoverpresentationcontroller present modally modal ios swift modal-dialog uipopovercontroller

ios - present - cómo centrar una vista popover en swift



swift present modal view controller programmatically (8)

Tengo el siguiente código para mostrar una vista popover (diálogo) sin una flecha, que funciona bien. El único problema es que el diálogo se muestra en la parte superior izquierda (IPad). Me gustaría centrar la vista en la pantalla.

¿Qué puedo cambiar o agregar en mi siguiente código? :

func show_help(){ let storyboard = UIStoryboard(name: "Main", bundle: nil) let controller = storyboard.instantiateViewControllerWithIdentifier("Help") as! UIViewController controller.modalPresentationStyle = UIModalPresentationStyle.popover let popoverPresentationController = controller.popoverPresentationController // result is an optional (but should not be nil if modalPresentationStyle is popover) if let _popoverPresentationController = popoverPresentationController { // set the view from which to pop up _popoverPresentationController.sourceView = self.view; _popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirection.allZeros; // present (id iPhone it is a modal automatic full screen) self.presentViewController(controller, animated: true, completion: nil) } }

Additional Infos

In my view, which is linked to my viewcontroller I set the preffered size like this:

override func viewDidLoad() { let dialogheigth:CGFloat = self.view.frame.height * 0.5; let dialogwidth:CGFloat = self.view.frame.width * 0.5; self.preferredContentSize = CGSizeMake(dialogwidth,dialogheigth); }


Swift 4 implementation :

popover.popoverPresentationController?.sourceRect = CGRect(x: view.center.x, y: view.center.y, width: 0, height: 0) popover.popoverPresentationController?.sourceView = view popover.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)


Another way for Swift 3 (Xcode 8, iOS 9) is this:

Called from somewhere:

self.performSegue(withIdentifier: "showPopupSegue", sender: yourButton)

Function that gets called before segue gets fired:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let popoverPresentationController = segue.destination.popoverPresentationController { let controller = popoverPresentationController controller.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0) controller.sourceView = self.view controller.sourceRect = CGRect(x: UIScreen.main.bounds.width * 0.5 - 200, y: UIScreen.main.bounds.height * 0.5 - 100, width: 400, height: 200) segue.destination.preferredContentSize=CGSize(width: 400, height: 200) } }

Remember to set the storyboard segue''s Kind attribute to "Present as Popover" and Anchor attribute to any view in your previous view controller.


Basically consist of three steps (iOS 8):

1.- Present the view:

Let''s say, you want to show a custom view to ask for a Review to the user.. here the function loadNibForRate() returns an instance of RateDialog loaded from its nib, but you can use here any way you desire to locate your UIViewController

private static func presentCustomDialog(parent: RateDialogParent) -> Bool { /// Loads the rateDialog from its xib, handled this way for further customization if desired if let rateDialog = loadNibForRate() { rateDialog.modalPresentationStyle = UIModalPresentationStyle.Popover rateDialog.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve let x = parent.view.center let sourceRectX : CGFloat //Here we check for the orientation of the device, just to know if we are on an iPad let maximumDim = max(UIScreen.mainScreen().bounds.height, UIScreen.mainScreen().bounds.width) if maximumDim == 1024 { //iPad sourceRectX = x.x }else { sourceRectX = 0 } rateDialog.popoverPresentationController?.sourceView = parent.view rateDialog.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.allZeros rateDialog.popoverPresentationController?.sourceRect = CGRectMake(sourceRectX, x.y, 0, 0) rateDialog.popoverPresentationController?.popoverLayoutMargins = UIEdgeInsetsMake(0, 0, 0, 0) rateDialog.popoverPresentationController?.delegate = parent rateDialogParent = parent callFunctionAsync() { parent.presentViewController(rateDialog, animated: true, completion: nil) } return true } return false }

2.- If we rotate our device, then the popover will not know where to reposition itself, unless we have this on the parent RateDialogParent

public class RateDialogParent: UIViewController, UIPopoverPresentationControllerDelegate { /** This function guarantees that the RateDialog is alwas centered at parent, it locates the RateDialog''s view by searching for its tag (-555) */ public func popoverPresentationController(popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer<CGRect>, inView view: AutoreleasingUnsafeMutablePointer<UIView?>) { if popoverPresentationController.presentedViewController.view.tag == RateDialog.thisViewTag { let x = popoverPresentationController.presentingViewController.view.center let newRect = CGRectMake(x.x, x.y, 0, 0) rect.initialize(newRect) } } }

3.- Your RateDialog should have a tag setted, this is just to avoid relocating unwanted popovers if there is more that one presented from your RateDialogParent

class RateDialog: UIViewController { @IBOutlet weak var reviewTitle: UILabel! @IBOutlet weak var reviewMessage : UILabel! @IBOutlet weak var cancelButtonTitle: UIButton! @IBOutlet weak var remindButtonTitle : UIButton! @IBOutlet weak var rateButtonTitle : UIButton! /// For being able to locate this view static let thisViewTag = -555 override func viewDidLoad() { super.viewDidLoad() //sets the tag to identify this view self.view.tag = RateDialog.thisViewTag } }


Here''s an implementation using Swift 3

let popover = storyboard?.instantiateViewController(withIdentifier: "popover") as! PopoverVC popover.modalPresentationStyle = UIModalPresentationStyle.popover popover.popoverPresentationController?.backgroundColor = UIColor.green popover.popoverPresentationController?.delegate = self popover.popoverPresentationController?.sourceView = self.view popover.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) popover.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0) self.present(popover, animated: true)

Based on Istvan''s answer


In case if it helps anyone, I have created an extension on UIViewController

extension UIViewController{ func configureAsPopoverAndPosition(withWidthRatio widthRatio:CGFloat, heightRatio:CGFloat){ modalPresentationStyle = .popover let screenWidth = UIScreen.main.bounds.width let screenHeight = UIScreen.main.bounds.height let popover = popoverPresentationController popover?.sourceView = self.view popover?.permittedArrowDirections = [UIPopoverArrowDirection(rawValue: 0)] preferredContentSize = CGSize(width: (screenWidth * widthRatio), height: (screenHeight * heightRatio)) popover?.sourceRect = CGRect(x: view.center.x, y: view.center.y, width: 0, height: 0) } }

Usage:

if UIDevice.current.userInterfaceIdiom == .pad{ yourViewController.configureAsPopoverAndPosition(withWidthRatio: 0.7 /*Make view controller width 70 % of screen width*/, heightRatio: 0.7/*Make view controller height 70 % of screen height*/) }

This will show the popover at center of the screen.


In iOS8, you don''t need to use self.view.frame to calculate width and height.

You can the dialog height and width using the following way:

override func viewDidLoad() { var frameSize: CGPoint = CGPointMake(UIScreen.mainScreen().bounds.size.width*0.5, UIScreen.mainScreen().bounds.size.height*0.5) self.preferredContentSize = CGSizeMake(frameSize.x,frameSize.y); }

Edited:

You can also set contentSizeForViewInPopover as below too:

self.contentSizeForViewInPopover = CGSizeMake(320.0, 360.0)

Let me know this helps or not?


You need to provide the source rect for the popover.

From the apple documentation: the source rect is the rectangle in the specified view in which to anchor the popover. Use this property in conjunction with the sourceView property to specify the anchor location for the popover.

In your case, under

_popoverPresentationController.sourceView = self.view;

add:

_popoverPresentationController.sourceRect = CGRectMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds),0,0)

It will do the trick!


Swift 4 implementation for center Popover controller let navigationController = UINavigationController(rootViewController: controller) navigationController.modalPresentationStyle = .popover navigationController.modalPresentationStyle = UIModalPresentationStyle.popover let popover = navigationController.popoverPresentationController controller.preferredContentSize = CGSize(width:500,height:600) //manage according to Device like iPad/iPhone popover?.delegate = self popover?.sourceView = self.view popover?.sourceRect = CGRect(x: view.center.x, y: view.center.y, width: 0, height: 0) popover?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0) self.present(navigationController, animated: true, completion: nil)