UIPopoverController Appears in Wrong Spot
Asked Answered
B

6

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.

Bubalo answered 8/7, 2011 at 19:59 Comment(2)
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.Celie
@Celie - 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.Bubalo
B
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];
Bubalo answered 9/7, 2011 at 12:49 Comment(2)
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.Predictor
or you can use [self.popoverController presentPopoverFromRect:[sender frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES]; and it'll be fineArnone
B
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];
Botha answered 4/11, 2013 at 23:23 Comment(3)
Great simple solution. Works great in XCode 5.0!Johathan
Is there any reason you don't call this line like this? CGRect tRect=[sender convertRect:sender.bounds toView:self.view];Phantasmal
@EvanR good point, there isn't a reason here to cast it from id to UIButton since I can send any message to it anywaysBotha
D
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.

Discussion answered 8/7, 2011 at 21:25 Comment(2)
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.Bubalo
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.Discussion
B
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.

Bushtit answered 4/11, 2011 at 20:59 Comment(0)
A
0

Try This one:

[self.popoverController presentPopoverFromRect:[sender bounds]
                    inView:sender
  permittedArrowDirections:UIPopoverArrowDirectionAny 
                  animated:YES];
Antoninus answered 29/8, 2013 at 9:28 Comment(0)
S
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!
    }
}
Sachasachem answered 20/4, 2016 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.