how to center a popoverview in swift

2019-01-13 06:04发布

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

enter image description here

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);
}

9条回答
【Aperson】
2楼-- · 2019-01-13 06:08

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
 }
}
查看更多
祖国的老花朵
3楼-- · 2019-01-13 06:08

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?

查看更多
男人必须洒脱
4楼-- · 2019-01-13 06:11

Swift 4.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.

查看更多
Melony?
5楼-- · 2019-01-13 06:13
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)
查看更多
神经病院院长
6楼-- · 2019-01-13 06:17

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)
查看更多
神经病院院长
7楼-- · 2019-01-13 06:18

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.

查看更多
登录 后发表回答