Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIPopover presentFromBarButton off center

I've noticed in a project that the positioning of a popover over a right bar button item seems to be off to the right for some reason. I've tried instead using a UIButton for the custom view, then presenting the popover from that button, but popover seems to ignore the showFromRect if I actually provide it with the 'centered' value.

Off-center popover

The code behind this is quite simple:

- (void)viewDidLoad {
    [super viewDidLoad];

     UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share"] style:UIBarButtonItemStylePlain target:self action:@selector(shareTap:)];

     self.navigationItem.rightBarButtonItem = button;
}

- (void)shareTap:(UIBarButtonItem *)button {
    self.popover = [[UIPopoverController alloc] initWithContentViewController:[[UIViewController alloc] init]];

    [self.popover presentPopoverFromBarButtonItem:button permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}

Now, if I switch to using the internal button, as mentioned, I see similar behavior (note color change so image shows up).

using internal button

The code for this is still fairly simple:

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *innerButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 22, 22)];
    [innerButton setImage:[UIImage imageNamed:@"share"] forState:UIControlStateNormal];
    [innerButton addTarget:self action:@selector(shareTap:) forControlEvents:UIControlEventTouchUpInside];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:innerButton];;
}

- (void)shareTap:(UIButton *)button {
    self.popover = [[UIPopoverController alloc] initWithContentViewController:[[UIViewController alloc] init]];

//    CGRect bottomCenter = CGRectMake(button.frame.size.width / 2, button.frame.size.height, 1, 1);
    CGRect bottomCenter = CGRectMake(2, button.frame.size.height, 1, 1);

    [self.popover presentPopoverFromRect:bottomCenter inView:button permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}

Note here that instead of using the actual center, I've used the arbitrary value, 2. If I use 1 or 0, the popover becomes left-aligned. Anything greater than one is right aligned again.

left-aligned

I searched around a bit looking for a similar question, but I can't seem to find anyone with the same problem. Any idea around what causes this or how to avoid it would be greatly appreciated. The only thing I've been able to guess at is that due to its proximity to the edge, Apple does some positioning voodoo to force it to be where they want it. The problem is that top-right corner button seems like a pretty standard drop down position.

Edit: I've confirmed this same behavior occurs with a long-press on the "New Message" icon in the native Mail app.

Edit 2: I was able to confirm with Apple that this is an issue. In one of the more recent versions (I can't remember which, one of the 9s I think), they made it so you can manually set this. The default behavior is still wrong I believe (I haven't tried this for a while), but you can use the CGRect offset method to make it work correctly, should you be so inclined.

like image 319
shortstuffsushi Avatar asked Jun 30 '14 20:06

shortstuffsushi


1 Answers

Here's one way to do this: Instead of using a UIBarButtonSystemItem, use a UIBarButtonItem with a custom view. Just drag a UIButton into the UINavigationBar to get a UIBarButtonItem with an embedded UIButton, which shows up as the UIBarButtonItem's customView.

@IBAction func didTapActionButton(_ sender: Any) {
    if let vc = self.storyboard?.instantiateViewController(withIdentifier: "myPopover") {
        vc.modalPresentationStyle = .popover
        guard let innerView = actionButton.customView else {
            // Unexpected missing custom view
            return
        }

        vc.popoverPresentationController?.sourceView = navigationController?.navigationBar
        vc.popoverPresentationController?.sourceRect = innerView.frame
        self.present(vc, animated: true, completion: nil)
    }
}
like image 55
stevex Avatar answered Nov 19 '22 11:11

stevex