UIBarButtonItem的CustomView是UIButton,如何从UIButton中访问它所在的UIBarButtonItem?

7
我有一个带有自定义视图的UIBarButtonItem,其中包含UIButtonUIButton上有一个addTarget:action:。在该操作中,我呈现一个弹出窗口。我目前是从sender.frame(即UIButton的框架)进行呈现,我想改为从UIBarButtonItem进行呈现。
如何从UIButton访问UIBarButtonItem
5个回答

10
如果您将UIButton设置为UIBarButtonItemcustomView,或者将其作为自定义视图的子视图,则可以从按钮开始向上遍历视图层次结构,直到找到包含该按钮的UIToolbarUINavigationBar ,然后在该栏的项目中搜索其自定义视图是该按钮(或该按钮的祖先)的项目。

以下是我完全未经测试的代码。 您可以调用[[self class] barButtonItemForView:myButton]来获取包含您的按钮的项。

+ (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;
}

你太棒了!由于我正在使用UIToolBar,所以我无法测试NavigationItems。这非常有效!谢谢! - scooter133

6

首先,你为什么需要访问UIBarButtonItem

然后,要创建一个带有自定义视图的UIBarButtonItem,我建议您创建一个类别扩展,如下所示(在此情况下,自定义视图是一个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];    
}

使用方法如下(首先导入UIBarButtonItem+Extension.h):

UIBarButtonItem* backBarButtonItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:@"YoutImageName"] title:@"YourTitle" target:self action:@selector(doSomething:)];

在这里,选择器是一个在类(目标)内部实现的方法,该方法使用了该bar button item。

现在,在doSomething:选择器中,您可以执行以下操作:

- (void)doSomething:(id)sender

    UIButton* senderButton = (UIButton*)sender;    
    [popover presentPopoverFromRect:senderButton.bounds inView:senderButton...];
}

在这种情况下,使用一个类别不重复地利用代码是有益的。希望这可以帮到你。
P.S. 我没有使用ARC,但同样的考虑也可以应用。
编辑:
访问条形按钮项的简单解决方案如下:
1. 子类化UIButton 2. 在其中创建一个类型为UIBarButtonItem的属性(弱引用以避免保留循环) 3. 在创建UIButton时将条形按钮项目注入UIButton中。
也许这个方法可以工作,但我更喜欢第一个方法。

我有类似于你的代码,不过我使用了UIBarButtonItem的子类作为按钮的外观和样式都是自定义的。问题在于[popover presentPopoverFromRect:senderButton.bounds inView:senderButton...];'这一行需要相当多的工作来重新定位弹出窗口,以适应旋转、键盘等。如果将弹出窗口从UIBarButtonItem中呈现,则可以消除所有这些问题,并始终将其锚定到UIBarButtonItem上。 - scooter133

0
假设您已将UIButton aButton添加到UIBarButtonItem info_Button中,如下所示:
//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;

现在,在showInfo方法中,使用presentPopoverFromBarButtonItem:info_Button(而不是presentPopoverFromBarButtonItem:sender),这将传递UIBarButtonItem info_button以使弹出窗口正常工作:
- (IBAction)showInfo:(id)sender
{   
    [self.flipsidePopoverController presentPopoverFromBarButtonItem:info_Button   permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

}

0

不确定您是否需要UIButton。如果您要执行操作,甚至是使用图像自定义按钮,则UIBarButtonItem应该足够。

如果您想从UIButton中获取框架以实现演示效果,我认为您最好估计UIBarButtonItem的位置。这不应该太困难,特别是如果它是您的UINavigationItemleftBarButtonItemrightBarButtonItem之一。

我通常遵循KISS(保持简单,傻瓜!)的原则。即使是苹果也是如此...当您从Springboard启动应用程序时,应用程序总是从屏幕中心扩展,而不是从应用程序的图标扩展。

只是一个建议。

编辑

好的,我刚刚阅读了UIPopoverController reference(我从未使用过)。我认为你想要的是presentPopoverFromBarButtonItem:permittedArrowDirections:animated:,并将你的BBI作为第一个参数传递进去。这个方法存在的原因就是为了解决你的问题——BBI没有框架,因为它们不是NSView的子类。苹果知道你想做这种事情,并提供了这个方法。此外,我认为如果你使用这个方法,你的自动旋转也会起作用。我可能错了,试试看吧。

至于你的自定义布局,我认为如果你在UIView中复制它,并使BBI与之自定义,效果会更好。当然,这取决于你自己。

无论哪种方式,你都可以通过将其连接为NIB的IBOutlet或在代码中创建时保存引用来获得对BBI的引用。然后只需将该引用传递给我上面描述的弹出窗口方法即可。我认为这可能适合你。

更多

BBI只是您类中的一个成员 - 一个iVar,它具有强引用属性,可能已链接到您的NIB作为IBOutlet。然后,您可以从类中的任何方法访问它。

示例:(不确定我是否正确处理了弹出控制器的内存管理)

@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

真正的问题是我的Popover是从UIButton的操作中呈现的。当我旋转屏幕时,Popover没有锚定到UIBarButtonItem,而是锚定到操作发送者的框架,即UIButton。我需要UIButton作为我的UIToolBar是自定义的,按钮在底部横向为84x48,3个按钮,弹性空间,4个按钮。UIButton允许我在84x48的特定位置具有Custom Graphic和Label的BartButtonItem。 - scooter133
由于从BarButtonItems呈现的Popovers没有定位问题,我想我可以在UIButton上使用sender.superView并获取UIBarButtonItem,而不是UIToolBar。但是我得到了UIToolBar。我有按钮的位置作为标记,我可以遍历UIToolBar以获取UIBarButtonItem吗? - scooter133
没错!我如何访问UIBarButtonItem?我的BarButtonItem是使用CusomView初始化的,它是一个UIButton。UIButton上有addTarget。因此,当按下按钮时,它是操作的发送者。进入操作后,我想访问UIBarButtonItem。我想到的唯一方法是子类化UIButton,添加一个ivar用于UIBarButtonItem,并在初始化按钮时设置它。 - scooter133
抱歉,我没有看完你的编辑。我需要考虑使用UIView或UIImageView作为我的自定义视图,而不是UIButton。我只是喜欢UIButton,因为它可以用高亮、选定、正常、禁用等方式来渲染图像。我使用不同的颜色来表示状态。我还在UIButton中添加了一个UILabel作为子视图,以便在不同位置显示标题文本。当禁用时,该文本也会变暗。 - scooter133
我需要将 UIBarButtonItemUIButton 相连接,因为我现在有七个 UIBarButtonItem - scooter133
显示剩余2条评论

0
// 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];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接