如何增加UIButton的Tap区域?

Sve*_*uer 50 objective-c uibutton ios swift

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

  1. 增加图像大小,即在图像周围放置一个透明区域.这并不好,因为当你定位图像时,你必须记住额外的透明边框.
  2. 使用CGRectInset并增加大小.这对于自动布局不起作用,因为使用自动布局它将回退到原始图像大小.

除了上述两种方法之外,是否有更好的解决方案来增加UIButton的抽头区域?

Tra*_*vis 53

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

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

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

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

界面构建器

  • 这是行不通的。AutoLayout 将尊重插图,移动按钮的位置,这不是我们想要的。相反,我们只希望命中检测受到影响,而不是布局受到影响。 (8认同)
  • 这根本不是一个好的答案。它可能有效 - 我什至不确定 - 但它会改变按钮的其他属性,这些属性对于实现此结果来说不是必需的。 (3认同)
  • 如果给它负值或请解释 (2认同)
  • 我很惊讶这是被接受的答案。它不起作用,在使图像失真的同时帧保持不变。 (2认同)

Sye*_*had 24

很容易.创建自定义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
Run Code Online (Sandbox Code Playgroud)

每侧需要10点以上的触摸区域.

  • 请始终使用有意义的名称,例如 LargeTapAreaButton (6认同)

小智 9

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

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

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

使用带有点击操作的图像视图,并将图像设置为中心模式 您可以点按图片来执行操作.

希望它会有所帮助.


Gle*_*enn 8

我确认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")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。效果很棒,只需处理按钮周围的较大区域并允许其接收事件 (2认同)

小智 7

这应该工作

import UIKit

@IBDesignable
class GRCustomButton: UIButton {

    @IBInspectable var margin:CGFloat = 20.0
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        //increase touch area for control in all directions by 20

        let area = self.bounds.insetBy(dx: -margin, dy: -margin)
        return area.contains(point)
    }

}
Run Code Online (Sandbox Code Playgroud)


Kev*_*usi 6

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

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

假设您有一个 10x10 的图像,并且希望将其设为 30x30 以获取更大的点击区域:

  1. 将自动布局约束设置为所需的更大区域。如果你现在就构建,这会拉伸图像。

  2. 使用内容边缘插图缩小图像可用的空间,使其匹配正确的尺寸。在本例中,结果为 10 10 10 10。为图像留出 10x10 的空间来绘制自身。

  3. 赢。

  • 超级有帮助。感谢您“修复”上面的解决方案。这对我来说是一个更好的解决方案,因为它不涉及自定义类。通过示例再次强调第一点:如果您之前将此按钮固定在距离左上角 32 像素的位置,则还应该从顶部和左侧约束中减去 10,将这两个常量减少到 22 (2认同)

小智 5

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

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

或者:

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)
}
Run Code Online (Sandbox Code Playgroud)


Zoë*_*ith 5

我的方法是在小图像周围为按钮提供一些额外的空间contentEdgeInsets(其作用就像按钮内容之外的边距),但也alignmentRect使用相同的插图覆盖该属性,这会将自动布局使用的矩形带回来到图像中。这确保自动布局使用较小的图像而不是按钮的完整可点击范围来计算其约束。

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)
}
Run Code Online (Sandbox Code Playgroud)

粉色按钮有一个更大的可点击目标(此处显示为粉色,但也可以是.clear)和一个较小的图像 - 它的前缘根据图标而不是整个按钮与绿色视图的前缘对齐。

粉色可点击按钮与基于较小图标的绿色视图对齐


Su-*_*ang 5

这里提出的两种解决方案都可以工作......在适当的情况下。但这里有一些您可能会遇到的问题。首先是一些不完全明显的事情:

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

具体针对上述解决方案:

解决方案1 ​​@Travis:

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

button.contentEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
Run Code Online (Sandbox Code Playgroud)

这个很简单,增加按钮尺寸会增加点击面积。

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

解决方案 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案很棒,因为它允许您将点击区域扩展到视图边界之外,而无需更改布局,但有以下问题:

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

---------
|       |
|oooo   |
|oXXo   |
|oXXo   |
|oooo   | Button-X nested in View-o will NOT extend beyond View-o
--------- 
Run Code Online (Sandbox Code Playgroud)