如何增加UIButton的Tap区域?

时间:2015-06-25 17:16:07

标签: ios objective-c swift uibutton

我使用UIButton进行自动布局。当图像很小时,点击区域也很小。我可以想象几种方法来解决这个问题:

  1. 增加图像尺寸,即在图像周围放置透明区域。这并不好,因为当你定位图像时,你必须记住额外的透明边框。
  2. 使用CGRectInset并增加大小。这对于自动布局不起作用,因为使用自动布局它将回退到原始图像大小。
  3. 除了上述两种方法之外,是否有更好的解决方案来增加UIButton的分接区域?

13 个答案:

答案 0 :(得分:49)

您只需调整按钮的内容插入即可获得所需的尺寸。在代码中,它看起来像这样:

  

button.contentEdgeInsets = UIEdgeInsets(上:12,左:16,下:12,右:16)

     

//或者如果您特别想调整图像,请使用button.imageEdgeInsets

在界面构建器中,它将如下所示:

interface builder

答案 1 :(得分:12)

很简单。创建自定义UIButton类。然后覆盖pointInside ...方法并根据需要更改值。

#import "CustomButton.h"

@implementation CustomButton

-(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGRect newArea = CGRectMake(self.bounds.origin.x - 10, self.bounds.origin.y - 10, self.bounds.size.width + 20, self.bounds.size.height + 20);

    return CGRectContainsPoint(newArea, point);
}
@end

每个方面都需要10点以上的触摸区域。

答案 2 :(得分:8)

您可以在故事板中或通过代码设置按钮EdgeInsets。按钮的大小和高度应该比设置为按钮的图像大。

注意:在Xcode8之后,设置内容inset在尺寸检查中可用 UIEdgeInseton UIButton

或者,您也可以在拍摄图像视图时使用带有敲击手势的图像视图进行操作。确保在故事板上勾选用户图像视图的用户交互,以便手势正常工作。 使图像视图大于要在其上设置的图像并在其上设置图像。 现在将图像视图图像的模式设置为故事板/界面构建器的中心。

Using image view with tap action and image set on it as center mode 您可以点按图片来执行操作。

希望它会有所帮助。

答案 3 :(得分:6)

我确认Syed的解决方案即使使用自动版式也能正常工作。这是Swift 4.x版本:

import UIKit

class BeepSmallButton: UIButton {

    // MARK: - Functions

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let newArea = CGRect(
            x: self.bounds.origin.x - 5.0,
            y: self.bounds.origin.y - 5.0,
            width: self.bounds.size.width + 10.0,
            height: self.bounds.size.height + 20.0
        )
        return newArea.contains(point)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

答案 4 :(得分:4)

Swift 4•Xcode 9

您可以通过编程方式选择

对于图片 -

button.imageEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)

标题 -

button.titleEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)

答案 5 :(得分:3)

基于Syed答案的Swift 5版本(较大区域的负值):

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return bounds.insetBy(dx: -10, dy: -10).contains(point)
}

或者:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return bounds.inset(by: UIEdgeInsets(top: -5, left: -5, bottom: -5, right: -5)).contains(point)
}

答案 6 :(得分:1)

子类UIButton并添加此功能

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let verticalInset = CGFloat(10)
    let horizontalInset = CGFloat(10)

    let largerArea = CGRect(
        x: self.bounds.origin.x - horizontalInset,
        y: self.bounds.origin.y - verticalInset,
        width: self.bounds.size.width + horizontalInset*2,
        height: self.bounds.size.height + verticalInset*2
    )

    return largerArea.contains(point)
}

答案 7 :(得分:1)

我要解决的方法是使用contentEdgeInsets(在按钮内容外的边距)为按钮在小图像周围留出一些额外的空间,同时覆盖alignmentRect属性具有相同的插图,将自动布局使用的矩形返回 in 到图像。这样可以确保自动布局使用较小的图像而不是按钮的完整可点击范围来计算其约束。

class HIGTargetButton: UIButton {
  override init(frame: CGRect) {
    super.init(frame: frame)
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  override func setImage(_ image: UIImage?, for state: UIControl.State) {
    super.setImage(image, for: state)
    guard let image = image else { return }
    let verticalMarginToAdd = max(0, (targetSize.height - image.size.height) / 2)
    let horizontalMarginToAdd = max(0, (targetSize.width - image.size.width) / 2)
    let insets = UIEdgeInsets(top: verticalMarginToAdd,
                                     left: horizontalMarginToAdd,
                                     bottom: verticalMarginToAdd,
                                     right: horizontalMarginToAdd)
    contentEdgeInsets = insets
  }
  
  override var alignmentRectInsets: UIEdgeInsets {
    contentEdgeInsets
  }
  
  private let targetSize = CGSize(width: 44.0, height: 44.0)
}

粉红色按钮的可点击目标较大(此处显示为粉红色,但可能为.clear),图像更小-其前边缘与基于图标的绿色视图的前边缘对齐,而不是整个按钮

Pink tappable button aligned with green view based on a smaller icon

答案 8 :(得分:0)

这应该有效

RouterModule.forRoot([], {useHash:true})

答案 9 :(得分:0)

关于边缘插图答案的一些上下文。

结合使用自动布局和内容边缘插图时,您可能需要更改约束。

假设您有一个10x10的图片,并且希望将其设为30x30,以实现更大的点击范围:

  1. 将自动布局约束设置为所需的较大区域。如果你 立即构建,这将拉伸图像。

  2. 使用内容边缘插图来缩小可用于 图片,使其与正确的尺寸匹配。在此示例中,该值为10 10 10 10.将图像留有10x10的空间以吸引自己。

  3. 胜利。

答案 10 :(得分:0)

子类的一种替代方法是扩展UIControl,为其添加一个touchAreaInsets属性-通过利用objC运行时-并扩展pointInside:withEvent

#import <objc/runtime.h>
#import <UIKit/UIKit.h>

#import "NSObject+Swizzling.h" // This is where the magic happens :)


@implementation UIControl (Extensions)

@dynamic touchAreaInsets;
static void * CHFLExtendedTouchAreaControlKey;

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleSelector:@selector(pointInside:withEvent:) withSelector:@selector(chfl_pointInside:event:) classMethod:NO];
    });
}

- (BOOL)chfl_pointInside:(CGPoint)point event:(UIEvent *)event
{
    if(UIEdgeInsetsEqualToEdgeInsets(self.touchAreaInsets, UIEdgeInsetsZero)) {
        return [self chfl_pointInside:point event:event];
    }
    
    CGRect relativeFrame = self.bounds;
    CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.touchAreaInsets);
    
    return CGRectContainsPoint(hitFrame, point);
}

- (UIEdgeInsets)touchAreaInsets
{
    NSValue *value = objc_getAssociatedObject(self, &CHFLExtendedTouchAreaControlKey);
    if (value) {
        UIEdgeInsets touchAreaInsets; [value getValue:&touchAreaInsets]; return touchAreaInsets;
    }
    else {
        return UIEdgeInsetsZero;
    }
}

- (void)setTouchAreaInsets:(UIEdgeInsets)touchAreaInsets
{
    NSValue *value = [NSValue value:&touchAreaInsets withObjCType:@encode(UIEdgeInsets)];
    objc_setAssociatedObject(self, &CHFLExtendedTouchAreaControlKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

这里是NSObject+Swizzling.h https://gist.github.com/epacces/fb9b8e996115b3bfa735707810f41ec8

这是一个非常通用的界面,可让您减少/增加UIControl s的触摸区域。

#import <UIKit/UIKit.h>

/**
 *  Extends or reduce the touch area of any UIControls
 *
 *  Example (extends the button's touch area by 20 pt):
 *
 *  UIButton *button = [[UIButton alloc] initWithFrame:CGRectFrame(0, 0, 20, 20)]
 *  button.touchAreaInsets = UIEdgeInsetsMake(-10.0f, -10.0f, -10.0f, -10.0f);
 */

@interface UIControl (Extensions)
@property (nonatomic, assign) UIEdgeInsets touchAreaInsets;
@end

答案 11 :(得分:0)

此处介绍的两种解决方案都有效……在正确的情况下。但这里有一些你可能会遇到的问题。 首先是不完全明显的:

  • 点击必须在按钮内,稍微触摸按钮边界不起作用。如果按钮非常小,则很有可能您的大部分手指都位于按钮之外并且无法点击。

特定于上述解决方案:

解决方案 1 @Travis:

使用 contentEdgeInsets 增加按钮大小而不增加图标/文本大小,类似于添加 padding

button.contentEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

这个很简单,增加按钮大小会增加点击区域。

  • 如果您设置了高度/宽度框架或约束,显然这不会有太大作用,只会扭曲或移动您的图标/文本。
  • 按钮尺寸会更大。在布置其他视图时必须考虑这一点。 (必要时抵消其他观点)

解决方案 2 @Syed Sadrul Ullah Sahad:

子类化 UIButton 并覆盖 point(inside point: CGPoint, with event: UIEvent?) -> Bool

class BigAreaButton: UIButton {
    
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        return bounds.insetBy(dx: -20, dy: -20).contains(point)
    }
}

这个解决方案很棒,因为它可以让你在不改变布局的情况下将点击区域扩展到视图边界之外,但这里有一些问题:

  • 父视图需要有背景,否则将按钮放入没有背景的空 ViewController 中是行不通的。
  • 如果按钮是嵌套的,则视图层次结构中的所有视图都需要提供足够的“空间”或覆盖点入点。 例如

---------
|       |
|oooo   |
|oXXo   |
|oXXo   |
|oooo   | Button-X nested in View-o will NOT extend beyond View-o
--------- 

答案 12 :(得分:-3)

我不确定,但应该有一种方法可以为任何UIView指定最小尺寸,这可能会解决您的额外工作问题。但老实说,在图像周围添加额外的透明背景是最佳解决方案。