Figure out UIBarButtonItem frame in window?

Solution 1:

Do you like to use private APIs? If yes,

UIView* view = thatItem.view;
return [view convertRect:view.bounds toView:nil];

Of course no one wants this when targeting the AppStore. A more unreliable method, and also uses undocumented features, but will pass Apple's test, is to loop through the subviews to look for the corresponding button item.

NSMutableArray* buttons = [[NSMutableArray alloc] init];
for (UIControl* btn in theToolbarOrNavbar.subviews)
  if ([btn isKindOfClass:[UIControl class]])
    [buttons addObject:btn];
UIView* view = [buttons objectAtIndex:index];
[buttons release];
return [view convertRect:view.bounds toView:nil];

The index is the index to your bar item in the array of .items, after removing all blank items. This assumes the buttons are arranged in increasing order, which may not be. A more reliable method is to sort the buttons array in increasing .origin.x value. Of course this still assumes the bar button item must inherit the UIControl class, and are direct subviews of the toolbar/nav-bar, which again may not be.


As you can see, there are a lot of uncertainty when dealing with undocumented features. However, you just want to pop up something under the finger right? The UIBarButtonItem's .action can be a selector of the form:

-(void)buttonClicked:(UIBarButtonItem*)sender event:(UIEvent*)event;

note the event argument — you can obtain the position of touch with

[[event.allTouches anyObject] locationInView:theWindow]

or the button view with

[[event.allTouches anyObject] view]

Therefore, there's no need to iterate the subviews or use undocumented features for what you want to do.

Solution 2:

I didn't see this option posted (which in my opinion is much simpler), so here it is:

UIView *barButtonView = [barButtonItem valueForKey:@"view"];

Solution 3:

In iOS 3.2, there's a much easier way to show an Action Sheet popover from a toolbar button. Merely do something like this:

- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
 UIActionSheet *popupSheet;
 // Prepare your action sheet
 [popupSheet showFromBarButtonItem:sender animated:YES];
}

Solution 4:

This is the implementation I use for my WEPopover project: (https://github.com/werner77/WEPopover):

@implementation UIBarButtonItem(WEPopover)

- (CGRect)frameInView:(UIView *)v {

    UIView *theView = self.customView;
    if (!theView.superview && [self respondsToSelector:@selector(view)]) {
        theView = [self performSelector:@selector(view)];
    }

    UIView *parentView = theView.superview;
    NSArray *subviews = parentView.subviews;

    NSUInteger indexOfView = [subviews indexOfObject:theView];
    NSUInteger subviewCount = subviews.count;

    if (subviewCount > 0 && indexOfView != NSNotFound) {
        UIView *button = [parentView.subviews objectAtIndex:indexOfView];
        return [button convertRect:button.bounds toView:v];
    } else {
        return CGRectZero;
    }
}
@end