'Set UIPopover arrow direction and position manually

I'm using UIBarButtonItem. In my scenario I want to show a popover from every bar button when I double-click that bar button. So I use a UITapGesture from that UIBarButtonItem. But, the popover arrow always appear in the middle of all UIBarButtonItem.

I can't make the arrow appear in the middle of each bar button. My friend was telling me to set the arrow direction using point, but I don't know how to do that.

How can I set both the position and direction of the popover's arrow?



Solution 1:[1]

Set the direction for the popover using:

[yourPopover setPopoverArrowDirection: UIPopoverArrowDirectionDown];

You can also use UIPopoverArrowDirectionUp, UIPopoverArrowDirectionLeft, and UIPopoverArrowDirectionRight, and UIPopoverArrowDirectionAny.

For Swift

yourPopover?.permittedArrowDirections = .up // or .down, .left, .right

Solution 2:[2]

I use method permittedArrowDirections = .Down, It's work for me. Swift example:

if let popoverPresentationController = shareViewController.popoverPresentationController {
    popoverPresentationController.permittedArrowDirections = .Down
    popoverPresentationController.delegate = self
    popoverPresentationController.sourceView = sender as! UIButton
    popoverPresentationController.sourceRect = CGRectMake(0, 0, sender.frame.size.width, sender.frame.size.height)
}

Solution 3:[3]

If you are using an IBAction method, you can use (id)sender as its parameter. (i.e. the calling button) Then you can set the popover's attachment rectangle to the frame of the button. The arrow will do its best to attach along the side of this frame in an appropriate spot. See UIPopoverController Class Reference.

-(IBAction)sortButtonPressed:(id)sender {
  // Set up your popover class, this varies per your implementation
  MyPopoverClass *iPop = [[MyPopoverClass alloc] initWithNibName:@"MyPopover" bundle:nil];
  UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:iPop];

  // Grab the button info from the input parameter
  UIButton *button = (UIButton *)sender;
  CGRect buttonRectangle = button.frame;

  // Set the arrow direction
  UIPopoverDirection dir = UIPopoverArrowDirectionAny; // Or Up, Down, etc.

  // Put it all together
  [popover presentPopoverFromRect:buttonRectangle inView:mySubView permittedArrowDirections:dir animated:YES];

  [popover release]
  [iPop release];
}

You can also hardcode that CGRect by hand if necessary. Shrinking the width/height of the rectangle will allow you to more tightly control the arrow's position.

Solution 4:[4]

You can set the direction of the arrow in Storyboard Segue's attribute inspector. For example, I set the arrow direction to down only in the following screenshot: enter image description here

Solution 5:[5]

class MyViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    @objc func expandClicked(sender: UIButton!) {
        let vc = UIViewController()
        vc.view.backgroundColor = .white
        display(viewcontroller: vc, size: CGSize(width: 150, height: 40), sender: sender)
    }

    func display(viewcontroller: UIViewController, size: CGSize, sender: UIView) {
        let controller = viewcontroller
        controller.modalPresentationStyle = .popover
        controller.popoverPresentationController?.delegate = self
        controller.preferredContentSize = size
        let presentationController = controller.popoverPresentationController!
        presentationController.sourceView = sender
        presentationController.sourceRect = sender.bounds
        presentationController.backgroundColor = .white
        let buttonPosition = CGPoint(x: sender.bounds.minX, y: sender.bounds.maxY)
        let p = sender.convert(buttonPosition, to: transactionTableView)
        if (transactionTableView.bounds.maxY - p.y) > 70 {
            presentationController.permittedArrowDirections = .up
        } else {
            presentationController.permittedArrowDirections = .down
        }
        self.present(controller, animated: true)
   }

   public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
       return UIModalPresentationStyle.none 
   }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 shim
Solution 2 Evgeny Karev
Solution 3
Solution 4 Linh Dao
Solution 5 manoranjan nayak