7

So I've spent some time looking for an answer for this, but so far haven't found anything.

I'm trying to present a popover from a button on an UIInputAccessoryView. The UIBarButtonItem i want to display the popover from has a custom view so I can use an image. I create the button like this:

 buttonImage=[UIImage imageNamed:@"tags.png"];
 aButton=[UIButton buttonWithType:UIButtonTypeCustom];
 [aButton setImage:buttonImage forState:UIControlStateNormal];
 aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
 UIBarButtonItem* compButton2=[[UIBarButtonItem alloc]initWithCustomView:aButton];
 [aButton addTarget:self action:@selector(tagIt:) forControlEvents:UIControlEventTouchUpInside];

When it comes time to display the popover, I do it like this:

CGRect tRect=[((UIButton*)sender) convertRect:((UIButton*)sender).frame toView:self.notePlainText];
NSLog(@"TagIt tRect: %f %f %f %f", tRect.origin.x, tRect.origin.y, tRect.size.width, tRect.size.height);
[self.popoverController presentPopoverFromRect:tRect inView:self.notePlainText permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

But what I get is this:

enter image description here

The popover looks fine, but it appears over the second button when it should be appearing over the first.

So then I found this question: UIBarButtonItem with custom image and no border and I thought, "ah, perhaps it will work better if I present the popover from a barbuttonitem instead of doing it this way. The code sub classes UIBarButtonItem in order to have a button with an image, not text, and represents the barbuttonitem properly when the action is called. This allowed me to use ...presentPopoverFromBarButtonItem...

So I tried the code in the second answer in the aforementioned question, and got a result that was better. The popover points right to the button as I wanted, but if the orientation of the device is not portrait with the button on the bottom, the popover displays with the wrong orientation:

Popover with wrong orientation

Finally, I will point out that I have other bar button items elsewhere in the program that are not in UIInputAccessoryViews that come up properly and without issue. It only seems to be happening form the input accessory view.

This just in: Hiding and showing the keyboard seems to make this work a bit better. Looking at the frame (CGRect) data for the button, it doesn't change between rotations unless it's hidden and brought back. Is this a bug?

So.. my question is this: what is the preferred, best-practices way of presenting a popover from a bar button item embedded in a ui accessory view?

Thank you very much.

Community
  • 1
  • 1
Chris
  • 1,013
  • 1
  • 15
  • 35
  • Have you tried `[UIPopoverController presentPopoverFromBarButtonItem:(UIBarButtonItem *)item permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated]`? It's preferable, if you have hold of the bar button item. – bshirley Jul 08 '11 at 20:33
  • @bshirley - yes, I did try that. the question i referenced above sub classes uibarbuttonitem in order to more easily create a barbuttonitem with an image instead of text. When I did that and used ... popoverfrombarbuttonitem... the result was the second image above, in which the popover appeared in the right position but was rotated improperly. – Chris Jul 09 '11 at 02:20

6 Answers6

8

So I got this to work with the following kludgy code, although if someone could tell me why I have to do this (or why I SHOULDNT do this) I would be much obliged.

CGRect r=((UIButton*)sender).frame;
CGRect tRect=[((UIButton*)sender) convertRect:((UIButton*)sender).frame toView:self.view];
tRect.origin.x=r.origin.x;

[self.popoverController presentPopoverFromRect:tRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
Chris
  • 1,013
  • 1
  • 15
  • 35
  • 3
    You should use the `bounds` property instead of `frame`. `convertRect:toView:` converts the rect from the receiver's coordinate system to the view passed in `toView:`. A view's `frame` is in its superview's coordinate system, while `bounds` is in that view's coordinate system. – Tobias Klüpfel Nov 02 '11 at 12:38
  • 1
    or you can use [self.popoverController presentPopoverFromRect:[sender frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES]; and it'll be fine – Dannie P Feb 05 '13 at 17:19
5

I had the exact same issue where the popover was pointing at a different place in iOS 7 and thanks to Chris' answer I was finally able to see consistency between iOS 6 and iOS 7. I had to make 1 minor adjustment to the code by replacing frame with bounds to have it positioned where I expected it to be.

CGRect r = [sender frame];
CGRect tRect = [sender convertRect:sender.bounds toView:self.view];
tRect.origin.x=r.origin.x;

[self.popoverController presentPopoverFromRect:tRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:NO];
Community
  • 1
  • 1
Korey Hinton
  • 2,532
  • 2
  • 25
  • 24
1

Use presentPopoverFromBarButtonItem:permittedArrowDirections:animated. To do this, make sure you have a reference to your bar button (either by storing it in a property on the view controller, or using an IBOutlet) and then call the method on the popover you want to show.

Benjamin Mayo
  • 6,649
  • 2
  • 26
  • 25
  • Yup. As I explained in my question and in my comment above, I did try that and the result was a properly positioned yet improperly rotated popover. Thanks, though. – Chris Jul 09 '11 at 02:21
  • Hmm ... Did you allow any arrow directions? The view may have been rotated to fit to a specified arrow direction? Otherwise, that's really odd. – Benjamin Mayo Jul 10 '11 at 08:46
1

I used the same workaround in this answer; this appears to be an issue with iOS 4.3 (maybe 4.x). It appears to be fixed in iOS 5.

Community
  • 1
  • 1
azsromej
  • 1,619
  • 13
  • 15
0

Try This one:

[self.popoverController presentPopoverFromRect:[sender bounds]
                    inView:sender
  permittedArrowDirections:UIPopoverArrowDirectionAny 
                  animated:YES];
Shehbaz Khan
  • 1,892
  • 3
  • 24
  • 31
0

You can just offset that point:

if segue.identifier == "layersPopover"
{
    let vc = segue.destinationViewController
    let controller = vc.popoverPresentationController

    if controller != nil
    {
        controller?.delegate = self

        var rect = controller?.sourceRect
        rect?.offsetInPlace(dx: 19, dy: 0)
        controller?.sourceRect = rect!
    }
}
AndrewK
  • 907
  • 8
  • 14