如何在不挤压背景图像的情况下扩展 UIButton 的 hitTest 区域?

Posted

技术标签:

【中文标题】如何在不挤压背景图像的情况下扩展 UIButton 的 hitTest 区域?【英文标题】:How to expand the hitTest area of a UIButton without extruding it's background image? 【发布时间】:2012-09-06 09:56:28 【问题描述】:

我已经设置了UIButton 的背景图片并在上面加上了一个标题(我使用了setBackgroundImage 方法而不是setImage)。现在我想扩大UIButton 的hitTest 区域而不挤压它的背景图像。

我该怎么做?

【问题讨论】:

【参考方案1】:

更简洁的方法是覆盖 pointInside。

这是一个 Swift 版本:

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool 
    let expandedBounds = CGRectInset(self.bounds, -15, -15)
    return CGRectContainsPoint(expandedBounds, point)

【讨论】:

不要忘记检查您的视图是否可见并且有窗口,否则您可能会得到有趣的结果... @steipete 希望你的评论早点发布......结果不是那么有趣!【参考方案2】:

这是已接受答案的更正版本。我们使用bounds 代替frameCGRectInset 代替CGRectMake。更清洁、更可靠。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
    return CGRectContainsPoint([self expandedBounds], point) ? self : nil;


- (CGRect)expandedBounds 
    return CGRectInset(self.bounds, -20, -20);

【讨论】:

【参考方案3】:

此版本允许您定义所有 UIButton 的最小点击大小。至关重要的是,它还可以处理 UIButtons 被隐藏的情况,许多答案都忽略了这一点。

extension UIButton 
    public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? 
        // Ignore if button hidden
        if self.hidden 
            return nil
        

        // If here, button visible so expand hit area
        let hitSize = CGFloat(56.0)
        let buttonSize = self.frame.size
        let widthToAdd = (hitSize - buttonSize.width > 0) ? hitSize - buttonSize.width : 0
        let heightToAdd = (hitSize - buttonSize.height > 0) ? hitSize - buttonSize.height : 0
        let largerFrame = CGRect(x: 0-(widthToAdd/2), y: 0-(heightToAdd/2), width: buttonSize.width+widthToAdd, height: buttonSize.height+heightToAdd)
        return (CGRectContainsPoint(largerFrame, point)) ? self : nil
    

【讨论】:

【参考方案4】:

好吧,你可以扩展 UIButton 并覆盖 UIView 的 hitTest 方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
    int expandMargin = 20;
    CGRect extendedFrame = CGRectMake(0 - expandMargin , 0 - expandMargin , self.bounds.size.width + (expandMargin * 2) , self.bounds.size.height + (expandMargin * 2));
    return (CGRectContainsPoint(extendedFrame , point) == 1) ? self : nil;

【讨论】:

这是不正确的。 point 在本地坐标系中,如果 vieworigin 不等于 CGPointZero,则使用 frame 代替 bounds 会导致问题。 CGRectInset 会比疯狂的 CGRectMake 更好【参考方案5】:

我为此创建了library。

您可以选择使用一个类别,不需要子类化

@interface UIView (KGHitTesting)
- (void)setMinimumHitTestWidth:(CGFloat)width height:(CGFloat)height;
@end

或者您可以将您的 UIView 或 UIButton 子类化并设置 minimumHitTestWidth 和/或 minimumHitTestHeight。然后,您的按钮命中测试区域将由这 2 个值表示。

就像其他解决方案一样,它使用- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法。该方法在 ios 执行命中测试时被调用。 This 博客文章很好地描述了 iOS 命中测试的工作原理。

https://github.com/kgaidis/KGHitTestingViews

@interface KGHitTestingButton : UIButton <KGHitTesting>

@property (nonatomic) CGFloat minimumHitTestHeight; 
@property (nonatomic) CGFloat minimumHitTestWidth;

@end

您也可以直接继承并使用 Interface Builder 而无需编写任何代码:

【讨论】:

以上是关于如何在不挤压背景图像的情况下扩展 UIButton 的 hitTest 区域?的主要内容,如果未能解决你的问题,请参考以下文章

UIButton:设置图像视图的背景(不设置图像)

如何在不缩放图像子视图的情况下缩放图像?迅速的ios

在 iPhone / iPad 上,如何在不使用图像的情况下制作带有凹槽的 Button?

如何在不使用背景图像的情况下获得背景磨砂玻璃效果

UIButton 图像大小

如何在不检查路径扩展名的情况下检查文件是不是为图像文件?