使用自定义图像的UIButtons创建自定义UIBarButtonItems - 是否可以使触摸目标更大?

16

我正在按照以下方式创建UIBarButton:

// Create "back" UIBarButtonItem
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
backButton.frame = CGRectMake(0, 0, 28, 17);
[backButton addTarget:self action:@selector(backButtonTapped) forControlEvents:UIControlEventTouchUpInside];
backButton.showsTouchWhenHighlighted = YES;

UIImage *backButtonImage = [UIImage imageNamed:@"back-button.png"];
[backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];

UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

[toolBarItems addObject:backBarButtonItem];

然而,触摸目标很小。更确切地说,它们的大小等于自定义图像的大小。(这些图像又是非常小的。) 有没有办法增加它们的触摸目标大小?

(注:修改UIButtons的frame属性只会拉伸图像。)


您能提供图片以便我们复制/查看确切问题吗? - TomSwift
10个回答

26

只需要对你的代码进行小的更改即可完成:

需要更改的内容:

  • 我假设backButtonImage的大小为{28,17},并将按钮框架设置为CGRectMake(0, 0, 48, 37)
  • 移除 backGroundImage 并使用 setImage:
  • 将属性 imageEdgeInsets 设置为 UIEdgeInsetsMake(10, 10, 10, 10)

你的代码将变成这样:

UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
backButton.frame = CGRectMake(0, 0, 48, 37);
[backButton addTarget:self action:@selector(backButtonTapped) forControlEvents:UIControlEventTouchUpInside];
backButton.showsTouchWhenHighlighted = YES;

UIImage *backButtonImage = [UIImage imageNamed:@"back-button.png"];
[backButton setImage:backButtonImage forState:UIControlStateNormal];

backButton.imageEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10);

UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

[toolBarItems addObject:backBarButtonItem];
你可以根据你的需求更改frameimageEdgeInsets的值。
这段代码对我有效。

5
您可以更改UIBarButtonItem的宽度属性。
backBarButtonItem.width = x;

不幸的是,您无法更改高度,因为没有高度属性。

然而,您可以通过使用initWithCustomView将有定义框架的UIButton传递给UIBarButtonItem

例如:

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

    UIImage *backButtonImage = [UIImage imageNamed:@"back-button.png"];

   [button setBackgroundImage:backButtonImage forState:UIControlStateNormal];

    button.frame = CGRectMake(0, 0, width, height);

    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];

如果您的图片看起来被拉伸了,请确保您保持相同的宽高比!或者确保图像的大小完全相同。


我也需要高度。 - Doug Smith
@DougSmith 我已经更新了我的答案,希望现在更有帮助。 - VGruenhagen

3

1)为您的UIButton添加一个分类
2)为该分类添加新属性
3)添加初始化后退按钮的方法
4)覆盖-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
5)子类化toolBarItems

@implementation UIButton (yourButtonCategory)
@dynamic shouldHitTest; // boolean
@dynamic hitTestRect; // enlarge rect of the button
@dynamic buttonPressedInterval; // interval of press, sometimes its being called twice

-(id)initBackBtnAtPoint:(CGPoint)_point{
// we only need the origin of the button since the size and width are fixed so the image won't be stretched
    self = [self initWithFrame:CGRectMake(_point.x, _point.y, 28, 17)];   
    [self setBackgroundImage:[UIImage imageNamed:@"back-button.png"]forState:UIControlStateNormal];

    self.shouldHitTest = CGRectMake(self.frame.origin.x - 25, self.frame.origin.y-10, self.frame.size.width+25, self.frame.size.height+25); // this will be the enlarge frame of the button
    self.shouldHitTest = YES;
    return self;
}


-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
BOOL shouldReturn = [super pointInside:point withEvent:event];
NSSet *touches = [event allTouches];
BOOL shouldSendTouches = NO;

for (UITouch *touch in touches) {
    switch (touch.phase) {
        case UITouchPhaseBegan:
            shouldSendTouches = YES;
            break;
        default:
            shouldSendTouches = NO;
            break;
    }
}


if(self.shouldHitTest){

    double elapse = CFAbsoluteTimeGetCurrent();
    CGFloat totalElapse = elapse - self.buttonPressedInterval;
    if (totalElapse < .32) {
        return NO;
        // not the event we were interested in
    } else {
                    // use this call

        if(CGRectContainsPoint(self.hitTestRect, point)){
            if(shouldSendTouches){
                self.buttonPressedInterval = CFAbsoluteTimeGetCurrent();

                [self sendActionsForControlEvents:UIControlEventTouchUpInside];
            }

            return NO;
        }
    }

}

return shouldReturn;

}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject]; 
CGPoint touch_point = [touch locationInView:self];
[self pointInside:touch_point withEvent:event];
}

@end

假设触摸事件未被触发,我们需要调用其所在的视图中的按钮,因此在toolBarItems中,我们需要执行以下操作:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    for(id subs in self.subviews){
        if([subs isKindOfClass:[UIButton class]]){
            [subs touchesBegan:touches withEvent:event];
        }
    }


}

那么就这样了。我们扩大框架而不扩大实际按钮。

您只需要像这样初始化您的按钮:UIButton *btn = [[UIButton alloc]initBackBtnAtPoint:CGPointMake(0,0)];

希望能帮到您。


1
将按钮框架改为大型并更改您的行。
[backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];

至:

[backButton setImage:backButtonImage forState:UIControlStateNormal];

1

您需要更改三行代码,并希望它能正常工作。

1)根据您的需求更改后退按钮的宽度。

2)设置后退按钮的图像,而不是后退按钮的背景图像

[backButton setImage:backButtonImage forState:UIControlStateNormal];

3) 同时设置 backButton 的 contentHorizontalAlignment

backButton.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft;

1

只需使用

[button setImage:backButtonImage forState:UIControlStateNormal];

替代

[backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];

将框架设置为您喜欢的任何样式。然后图像将居中,按钮将在给定框架内接收触摸。

类似于这样:

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(50, 50, 50, 50);
[button setImage:backButtonImage forState:UIControlStateNormal];

这对我不起作用。图像总是拉伸以填充整个按钮。 - user3344977
@user3344977 因为你需要为按钮设置正确的 contentMode。默认值可能是 UIViewContentModeScaleToFill,将其设置为你需要的值(也许是 UIViewContentModeCenter)。 - Lukas Kukacka

1
当您调用initWithCustomView时,UIBarButtonItem将事件处理委托给自定义视图,而在这种情况下,它是UIButton。然后,UIButton从图像获取其边界,并且作为背景图像,预计根据需要拉伸以填充新边界。

相反,直接在UIBarButtonItem上设置imageimageInsets属性。这些属性是在父类UIBarItem上声明的。您可能还希望设置它们的横向变体。


1

嗯...为了实现你所需的功能——不拉伸图像——有一个简单的方法:

1)使用Gimp或Photoshop,在图像下方创建一个透明层,使其与您想要的大小相匹配。

2)合并图像并创建视网膜和非视网膜图像。

3)更新您的代码,以反映新的图像大小。

4)分配图像,然后运行您的代码。

这样,由于其边界将考虑到其透明部分,原始图像就不会被拉伸。

除此之外,你可能可以通过编程来完成所有这些操作,但我怀疑这不是一个好主意,除非你计划出于其他原因深入理解UIGraphics。


0

增加UIButton的框架大小(最多到您想要的点击大小)

      button.frame = CGRectMake(0, 0, 56, 20);

如果您想查看完整按钮的图像,请编写以下方法。
      [backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];

如果您想查看图像的实际大小(不用担心,点击大小与您的按钮大小相同)

      [backButton setImage:backButtonImage forState:UIControlStateNormal];

最后

      UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

希望它能帮到你!

“完整按钮”是什么意思? - Doug Smith
如果您设置的按钮大小大于图像实际大小,则图像将被拉伸并显示为完整的按钮。 - Ganee....

0

你应该能够在按钮上设置边距。

所以将按钮的框架设置为比图像大,然后在按钮上设置边距。

因此,要在每侧扩展10个像素的宽度,类似于以下内容应该可以行得通

backButton.frame = CGRectMake(0,0,28,37);
backButton.imageEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);

那仍然会拉伸图像。 - Doug Smith

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