I have a UIBarButtonItem
with UIButton
as custom view.
The UIButton
has a addTarget:action:
on it. In the action
// get the UIBarButtonItem from the UIButton that is passed in sender
UIToolbar *toolbar = (id)[sender superview];
UIBarButtonItem *ourButtonItem = [toolbar items][2];//pick the right index
[parkingPopOver_ presentPopoverFromBarButtonItem:ourButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Not sure that you need the UIButton. If what you're doing is dispatching an action, or even customizing the button with an image, the UIBarButtonItem
should suffice.
If what you're after is to grab the frame from the UIButton
in order to achieve the presentation, I think you might be better served to simply estimate the position of the UIBarButtonItem
. This shouldn't be too difficult, especially if it's one or the other of your UINavigationItem
's leftBarButtonItem
or rightBarButtonItem
.
I'm generally for the KISS (Keep It Simple, Stupid!) rule of thumb. Even Apple does this... when you launch an app from Springboard, the app always expands from the center of the screen, not from the app's icon.
Just a suggestion.
EDIT
OK, I just read the UIPopoverController reference (I've never used one). I think what you want is presentPopoverFromBarButtonItem:permittedArrowDirections:animated:
and to pass in your BBI as the first parameter. The reason that method exists is to solve your problem - BBI's have no frame because they're not subclasses NSView. Apple knows that you want to do this kind of thing, and provides this method. Also, I think if you use this method, that your autorotation will work as well. I may be wrong about this, give it a shot.
As far as your customized layout is concerned, I think if you replicate it in a UIView and make the BBI custom with that, you will do better. This is of course up to you.
Either way, you get a reference to the BBI by either connecting it as an IBOutlet with your NIB, or by saving a reference to it when you create it in code. Then just pass that reference to the popover method I described above. I think this might work for you.
MOAR
The BBI is just a member of your class - an iVar with a strong reference property on it, perhaps linked up as an IBOutlet to your NIB. Then you can access it from whatever method you want in the class.
Example: (not sure that I have the popover controller memory management right)
@interface MyViewController : UIViewController {
UIBarButtonItem *item;
}
@property (nonatomic, retain) UIBarButtonItem *item;
@end
@implementation MyViewController
@synthesize item;
-(void)viewDidLoad {
// assuming item isn't in your NIB
item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlus target:self action:@selector(doit)];
self.navigationItem.rightBarButtonItem = item;
}
-(void)doit {
UIPopoverController *popover = [[[UIPopoverController alloc] initWithContentViewController:yourViewController] autorelease];
[popover presentPopoverFromBarButtonItem:self.item permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// or in the line above, replace self.item with self.navigationItem.rightBarButtonItem
}
@end
Let's say you have added the UIButton aButton to UIBarButtonItem info_Button as following:
//Add a button on top of the UIBarButtonItem info_button
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[aButton addTarget:self action:@selector(showInfo:) forControlEvents:UIControlEventTouchUpInside];
info_Button.customView = aButton;
Now, in showInfo, presentPopoverFromBarButtonItem:info_Button(instead of presentPopoverFromBarButtonItem:sender), this will pass the UIBarButtonItem info_button to make pop over works:
- (IBAction)showInfo:(id)sender
{
[self.flipsidePopoverController presentPopoverFromBarButtonItem:info_Button permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
First of all, why do you need to access the UIBarButtonItem
?
Then, to create a UIBarButtonItem
with a custom view I suggest you to create a category extension like the following (in this case the custom view is a UIButton
).
//UIBarButtonItem+Extension.h
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image title:(NSString*)title target:(id)target action:(SEL)action;
//UIBarButtonItem+Extension.m
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image title:(NSString*)title target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.titleLabel.textAlignment = UITextAlignmentCenter;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
return [barButtonItem autorelease];
}
and the use it like this (first import UIBarButtonItem+Extension.h
):
UIBarButtonItem* backBarButtonItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:@"YoutImageName"] title:@"YourTitle" target:self action:@selector(doSomething:)];
where the selector it's a method that is implemented inside the class (the target) that uses that bar button item.
Now inside the doSomething:
selector you could the following:
- (void)doSomething:(id)sender
UIButton* senderButton = (UIButton*)sender;
[popover presentPopoverFromRect:senderButton.bounds inView:senderButton...];
}
A category in this case it's useful to not duplicate code. Hope it helps.
P.S. I'm not using ARC but same considerations can be also applied.
Edit:
An easy solution to access the bar button item could be the following:
UIButton
UIBarButtonItem
(a weak one to avoid retaincycle)UIButton
when you create it.Maybe it could work, but I will prefer the first one.
If you have set your UIButton
as the customView
of a UIBarButtonItem
, or a child of the custom view, then you can walk up the view hierarchy from your button until you find the UIToolbar
or UINavigationBar
that contains the button, then search the bar's items for the one whose custom view is the button (or an ancestor of the button).
Here's my completely untested code for doing that. You would call [[self class] barButtonItemForView:myButton]
to get the item containing your button.
+ (BOOL)ifBarButtonItem:(UIBarButtonItem *)item containsView:(UIView *)view storeItem:(UIBarButtonItem **)outItem {
UIView *customView = item.customView;
if (customView && [view isDescendantOfView:customView]) {
*outItem = item;
return YES;
} else {
return NO;
}
}
+ (BOOL)searchBarButtonItems:(NSArray *)items forView:(UIView *)view storeItem:(UIBarButtonItem **)outItem {
for (UIBarButtonItem *item in items) {
if ([self ifBarButtonItem:item containsView:view storeItem:outItem])
return YES;
}
return NO;
}
+ (UIBarButtonItem *)barButtonItemForView:(UIView *)view {
id bar = view;
while (bar && !([bar isKindOfClass:[UIToolbar class]] || [bar isKindOfClass:[UINavigationBar class]])) {
bar = [bar superview];
}
if (!bar)
return nil;
UIBarButtonItem *item = nil;
if ([bar isKindOfClass:[UIToolbar class]]) {
[self searchBarButtonItems:[bar items] forView:view storeItem:&item];
}
else {
UINavigationItem *navItem = [bar topItem];
if (!navItem)
return nil;
[self ifBarButtonItem:navItem.backBarButtonItem containsView:view storeItem:&item]
|| [self ifBarButtonItem:navItem.leftBarButtonItem containsView:view storeItem:&item]
|| [self ifBarButtonItem:navItem.rightBarButtonItem containsView:view storeItem:&item]
|| ([navItem respondsToSelector:@selector(leftBarButtonItems)]
&& [self searchBarButtonItems:[(id)navItem leftBarButtonItems] forView:view storeItem:&item])
|| ([navItem respondsToSelector:@selector(rightBarButtonItems)]
&& [self searchBarButtonItems:[(id)navItem rightBarButtonItems] forView:view storeItem:&item]);
}
return item;
}