Reputation: 11651
I have the following code to show a popoverview (dialog) without an arrow, which works fine. The only problem is, that the dialog is shown in the top left (IPad). I would like to center the view on the screen.
What to change or add in my following code ? :
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);
}
Upvotes: 49
Views: 46801
Reputation: 8997
Simple Solution: modalPresentationStyle = .formSheet
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let viewController = storyboard.instantiateViewController(withIdentifier: "PurchaseViewController") as? PurchaseViewController {
viewController.modalPresentationStyle = .formSheet
self.present(viewController, animated: true)
}
Upvotes: 0
Reputation: 1033
Add the below mentioned line of code to make it centre.
popoverController.popoverPresentationController?.sourceView = view
popoverController.popoverPresentationController?.sourceRect = view.bounds
Upvotes: -2
Reputation: 949
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. .y, width: 0, height: 0)
popover?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
self.present(navigationController, animated: true, completion: nil)
Upvotes: 6
Reputation: 1
Here is the simple solution:
Take a public variable var popover
var popover: UIPopoverPresentationController?
Present YourViewController
as popover, use the popover?.sourceRect
as mentioned below.
let storyboard: UIStoryboard = UIStoryboard(name: "YOUR_STORYBOARD", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "YOUR_IDENTIFIER") as! YourViewController
let navController = UINavigationController(rootViewController: vc)
navController.modalPresentationStyle = UIModalPresentationStyle.popover
popover = yourController.popoverPresentationController!
popover?.sourceRect = CGRect(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY, width: 0, height: 0)
popover?.sourceView = self.view
popover?.delegate = self
popover?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
vc.preferredContentSize = CGSize(width: width, height: height)
self.present(navController, animated: true, completion: nil)
use viewWillTransition
for view transitions landscape and portrait.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
popover?.sourceRect = CGRect(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY, width: 0, height: 0)
}
this will give you popover center aligned to screen in both landscape and portrait. Much flexible while using split view for iPad.
Upvotes: 0
Reputation: 2183
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.
Upvotes: 1
Reputation: 789
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)
Upvotes: 24
Reputation: 1466
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.
Upvotes: 5
Reputation: 651
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
Upvotes: 42
Reputation: 6824
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 yourRateDialogParent
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
}
}
Upvotes: 2
Reputation: 1249
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!
Upvotes: 109
Reputation: 9540
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?
Upvotes: 1