UIButton:使命中区域大于默认命中区域

我有一个关于UIButton及其命中的问题。 我在界面生成器中使用了Info Darkbutton,但是我发现命中区域对于某些人的手指来说还不够大。

有没有一种方法来增加button的命中区域,无论是编程或在界面生成器,而不改变信息buttongraphics的大小?

由于我使用的是背景图片,这些解决scheme都不适合我。 这是一个有趣的Objective-C魔法解决scheme,只需要最less的代码就可以解决问题。

首先,向UIButton添加一个覆盖命中testing的类别,并添加一个属性来扩展命中testing框架。

的UIButton + Extensions.h

 @interface UIButton (Extensions) @property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; @end 

的UIButton + Extensions.m

 #import "UIButton+Extensions.h" #import <objc/runtime.h> @implementation UIButton (Extensions) @dynamic hitTestEdgeInsets; static const NSString *KEY_HIT_TEST_EDGE_INSETS = @"HitTestEdgeInsets"; -(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets { NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)]; objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(UIEdgeInsets)hitTestEdgeInsets { NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS); if(value) { UIEdgeInsets edgeInsets; [value getValue:&edgeInsets]; return edgeInsets; }else { return UIEdgeInsetsZero; } } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) { return [super pointInside:point withEvent:event]; } CGRect relativeFrame = self.bounds; CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets); return CGRectContainsPoint(hitFrame, point); } @end 

一旦添加了这个类,你所需要做的就是设置你的button的边界。 请注意,我select添加插图,所以如果您想要增加点击面积,您必须使用负数。

 [button setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)]; 

注意:请记得在类中导入类别( #import "UIButton+Extensions.h" )。

只需在界面构build器中设置图像边缘插入值即可。

这是一个在Swift中使用扩展的优雅解决scheme。 它根据苹果的人机界面指南( https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/ )为所有UIButtons提供至less44x44的点数,

Swift 2:

 private let minimumHitArea = CGSizeMake(44, 44) extension UIButton { public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { // if the button is hidden/disabled/transparent it can't be hit if self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 { return nil } // increase the hit frame to be at least as big as `minimumHitArea` let buttonSize = self.bounds.size let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0) let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0) let largerFrame = CGRectInset(self.bounds, -widthToAdd / 2, -heightToAdd / 2) // perform hit test on larger frame return (CGRectContainsPoint(largerFrame, point)) ? self : nil } } 

Swift 3:

 fileprivate let minimumHitArea = CGSize(width: 100, height: 100) extension UIButton { open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // if the button is hidden/disabled/transparent it can't be hit if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil } // increase the hit frame to be at least as big as `minimumHitArea` let buttonSize = self.bounds.size let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0) let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0) let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2) // perform hit test on larger frame return (largerFrame.contains(point)) ? self : nil } } 

你也可以UIButton或一个自定义的UIView并覆盖point(inside:with:) ,如下所示:

Swift 3

 override func point(inside point: CGPoint, with _: UIEvent?) -> Bool { let margin: CGFloat = 5 let area = self.bounds.insetBy(dx: -margin, dy: -margin) return area.contains(point) } 

Objective-C的

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { CGFloat margin = 5.0; CGRect area = CGRectInset(self.bounds, -margin, -margin); return CGRectContainsPoint(area, point); } 

这里是Chase在Swift 3.0中的UIButton + Extensions。

 import UIKit private var pTouchAreaEdgeInsets: UIEdgeInsets = .zero extension UIButton { var touchAreaEdgeInsets: UIEdgeInsets { get { if let value = objc_getAssociatedObject(self, &pTouchAreaEdgeInsets) as? NSValue { var edgeInsets: UIEdgeInsets = .zero value.getValue(&edgeInsets) return edgeInsets } else { return .zero } } set(newValue) { var newValueCopy = newValue let objCType = NSValue(uiEdgeInsets: .zero).objCType let value = NSValue(&newValueCopy, withObjCType: objCType) objc_setAssociatedObject(self, &pTouchAreaEdgeInsets, value, .OBJC_ASSOCIATION_RETAIN) } } open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { if UIEdgeInsetsEqualToEdgeInsets(self.touchAreaEdgeInsets, .zero) || !self.isEnabled || self.isHidden { return super.point(inside: point, with: event) } let relativeFrame = self.bounds let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.touchAreaEdgeInsets) return hitFrame.contains(point) } } 

要使用它,您可以:

 button.touchAreaEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) 

不要设置backgroundImage属性与您的图像,设置imageView属性。 此外,请确保你有在UIViewContentModeCenter设置UIViewContentModeCenter

我build议在你的信息button上放置一个types为Custom的UIButton。 将自定义button的大小调整为您想要的区域的大小。 从那里你有两个select:

  1. 选中自定义button上的“突出显示突出显示”选项。 信息button上方会出现白色的光芒,但在大多数情况下,用户的手指会覆盖这些,他们将看到的是周围的光线。

  2. 为信息button设置一个IBOutlet,为“Touch Down”设置两个IBAction自定义button,另一个用于“Touch Up Inside”。 然后在Xcode中,触发事件将信息button的突出显示的属性设置为YES,并且touchupinside事件将突出显示的属性设置为NO。

提出的答案没有错, 但是我想扩展jlarjlar的答案,因为它具有惊人的潜力,可以增加与其他控件(例如SearchBar)相同的问题的价值。 这是因为,因为pointInside附加到UIView,所以可以对任何控件进行子类化以提高触摸面积。 这个答案也显示了如何实现完整解决scheme的完整示例。

为您的button(或任何控件)创build一个新的子类

 #import <UIKit/UIKit.h> @interface MNGButton : UIButton @end 

接下来在你的子类实现中覆盖pointInside方法

 @implementation MNGButton -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { //increase touch area for control in all directions by 20 CGFloat margin = 20.0; CGRect area = CGRectInset(self.bounds, -margin, -margin); return CGRectContainsPoint(area, point); } @end 

在storyboard / xib文件上,select有问题的控件,然后打开身份检查器并input自定义类的名称。

mngbutton

在包含button的场景的UIViewController类中,将button的类types更改为您的子类的名称。

 @property (weak, nonatomic) IBOutlet MNGButton *helpButton; 

将故事板/ xibbutton链接到属性IBOutlet,您的触摸区域将被展开以适应子类中定义的区域。

除了重写pointInside方法以及CGRectInset和CGRectContainsPoint方法外,还需要花时间检查CGGeometry以扩展任何UIView子类的矩形触摸区域。 你也可以在NSHipster的CGGeometry用例上find一些不错的提示。

例如,可以使用上述方法使触摸区域不规则,或者简单地select使宽度触摸区域为横向触摸区域的两倍大:

 CGRect area = CGRectInset(self.bounds, -(2*margin), -margin); 

注意:replace任何UI类控件应该为扩展不同控件(或任何UIView子类,如UIImageView等)的触摸区域产生类似的结果。

这适用于我:

 UIButton *button = [UIButton buttonWithType: UIButtonTypeCustom]; // set the image (here with a size of 32 x 32) [button setImage: [UIImage imageNamed: @"myimage.png"] forState: UIControlStateNormal]; // just set the frame of the button (here 64 x 64) [button setFrame: CGRectMake(xPositionOfMyButton, yPositionOfMyButton, 64, 64)]; 

我正在使用更通用的方法-[UIView pointInside:withEvent:] 。 这使我可以修改任何UIView命中testing行为,而不仅仅是UIButton

通常情况下,button被放置在容器视图内,这也限制了命中testing。 例如,当button位于容器视图的顶部,并且想要向上延伸触摸目标时,还必须扩展容器视图的触摸目标。

 @interface UIView(Additions) @property(nonatomic) UIEdgeInsets hitTestEdgeInsets; @end @implementation UIView(Additions) + (void)load { Swizzle(self, @selector(pointInside:withEvent:), @selector(myPointInside:withEvent:)); } - (BOOL)myPointInside:(CGPoint)point withEvent:(UIEvent *)event { if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || self.hidden || ([self isKindOfClass:UIControl.class] && !((UIControl*)self).enabled)) { return [self myPointInside:point withEvent:event]; // original implementation } CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets); hitFrame.size.width = MAX(hitFrame.size.width, 0); // don't allow negative sizes hitFrame.size.height = MAX(hitFrame.size.height, 0); return CGRectContainsPoint(hitFrame, point); } static char hitTestEdgeInsetsKey; - (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets { objc_setAssociatedObject(self, &hitTestEdgeInsetsKey, [NSValue valueWithUIEdgeInsets:hitTestEdgeInsets], OBJC_ASSOCIATION_RETAIN); } - (UIEdgeInsets)hitTestEdgeInsets { return [objc_getAssociatedObject(self, &hitTestEdgeInsetsKey) UIEdgeInsetsValue]; } void Swizzle(Class c, SEL orig, SEL new) { Method origMethod = class_getInstanceMethod(c, orig); Method newMethod = class_getInstanceMethod(c, new); if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); else method_exchangeImplementations(origMethod, newMethod); } @end 

这种方法的好处是你可以通过添加一个用户定义的运行时属性来在故事板中使用它。 不幸的是, UIEdgeInsets并不是直接可用的types,但是由于CGRect也包含一个具有四个CGFloat的结构,它可以通过select“Rect”并填充如下值来完美地工作: {{top, left}, {bottom, right}}

不要改变UIButton的行为。

 @interface ExtendedHitButton: UIButton + (instancetype) extendedHitButton; - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; @end @implementation ExtendedHitButton + (instancetype) extendedHitButton { return (ExtendedHitButton *) [ExtendedHitButton buttonWithType:UIButtonTypeCustom]; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { CGRect relativeFrame = self.bounds; UIEdgeInsets hitTestEdgeInsets = UIEdgeInsetsMake(-44, -44, -44, -44); CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets); return CGRectContainsPoint(hitFrame, point); } @end 

我在Swift 3上的解决scheme:

 class MyButton: UIButton { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { let relativeFrame = self.bounds let hitTestEdgeInsets = UIEdgeInsetsMake(-25, -25, -25, -25) let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets) return hitFrame.contains(point) } } 

那么,你可以把你的UIButton放在一个透明的稍微大一些的UIView中,然后像在UIButton中那样捕获UIView实例上的touch事件。 这样,你仍然有你的button,但有一个更大的触摸面积。 如果用户触摸视图而不是button,则手动必须处理选定的和突出显示的状态。

其他可能涉及使用UIImage而不是UIButton。

我已经能够以编程方式增加信息button的命中区域。 “我”graphics不会改变比例,并保持居中在新的button框架中。

信息button的大小似乎在Interface Builder中被固定为18×19 [*]。 通过连接到一个IBOutlet,我能够改变它的代码框架大小没有任何问题。

 static void _resizeButton( UIButton *button ) { const CGRect oldFrame = infoButton.frame; const CGFloat desiredWidth = 44.f; const CGFloat margin = ( desiredWidth - CGRectGetWidth( oldFrame ) ) / 2.f; infoButton.frame = CGRectInset( oldFrame, -margin, -margin ); } 

[*]:更高版本的iOS似乎增加了信息button的命中区域。

我在Swift中使用了下面的类,来启用Interface Builder属性来调整边距:

 @IBDesignable class ALExtendedButton: UIButton { @IBInspectable var touchMargin:CGFloat = 20.0 override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { var extendedArea = CGRectInset(self.bounds, -touchMargin, -touchMargin) return CGRectContainsPoint(extendedArea, point) } } 

这是我的Swift 3解决scheme(基于这个博客: http : //bdunagan.com/2010/03/01/iphone-tip-larger-hit-area-for-uibutton/ )

 class ExtendedHitAreaButton: UIButton { @IBInspectable var hitAreaExtensionSize: CGSize = CGSize(width: -10, height: -10) override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let extendedFrame: CGRect = bounds.insetBy(dx: hitAreaExtensionSize.width, dy: hitAreaExtensionSize.height) return extendedFrame.contains(point) ? self : nil } } 

Chase的自定义命中testing是作为UIButton的子类实现的。 写在Objective-C中。

它似乎同时适用于initbuttonWithType:构造函数。 为了我的需要,这是完美的,但由于UIButton子类可能是毛茸茸的,我有兴趣知道是否有人有错。

CustomeHitAreaButton.h

 #import <UIKit/UIKit.h> @interface CustomHitAreaButton : UIButton - (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets; @end 

CustomHitAreaButton.m

 #import "CustomHitAreaButton.h" @interface CustomHitAreaButton() @property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; @end @implementation CustomHitAreaButton - (instancetype)initWithFrame:(CGRect)frame { if(self = [super initWithFrame:frame]) { self.hitTestEdgeInsets = UIEdgeInsetsZero; } return self; } -(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets { self->_hitTestEdgeInsets = hitTestEdgeInsets; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) { return [super pointInside:point withEvent:event]; } CGRect relativeFrame = self.bounds; CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets); return CGRectContainsPoint(hitFrame, point); } @end 

通过覆盖实现inheritanceUIButton。

Swift 2.2

 // don't forget that negative values are for outset _button.hitOffset = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) ... class UICustomButton: UIButton { var hitOffset = UIEdgeInsets() override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { guard hitOffset != UIEdgeInsetsZero && enabled && !hidden else { return super.pointInside(point, withEvent: event) } return UIEdgeInsetsInsetRect(bounds, hitOffset).contains(point) } } 

WJBackgroundInsetButton.h

 #import <UIKit/UIKit.h> @interface WJBackgroundInsetButton : UIButton { UIEdgeInsets backgroundEdgeInsets_; } @property (nonatomic) UIEdgeInsets backgroundEdgeInsets; @end 

WJBackgroundInsetButton.m

 #import "WJBackgroundInsetButton.h" @implementation WJBackgroundInsetButton @synthesize backgroundEdgeInsets = backgroundEdgeInsets_; -(CGRect) backgroundRectForBounds:(CGRect)bounds { CGRect sup = [super backgroundRectForBounds:bounds]; UIEdgeInsets insets = self.backgroundEdgeInsets; CGRect r = UIEdgeInsetsInsetRect(sup, insets); return r; } @end 

切勿覆盖类别中的方法。 子类button和覆盖- pointInside:withEvent: 例如,如果你的button的边小于44像素(推荐作为最小可点击区域),使用这个:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return (ABS(point.x - CGRectGetMidX(self.bounds)) <= MAX(CGRectGetMidX(self.bounds), 22)) && (ABS(point.y - CGRectGetMidY(self.bounds)) <= MAX(CGRectGetMidY(self.bounds), 22)); } 

我为这个目的build了一个图书馆 。

您可以select使用UIView类别, 不需要子类

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

或者你可以minimumHitTestWidth你的UIView或UIButton并设置minimumHitTestWidth和/或minimumHitTestHeight 。 您的button命中testing区域将由这两个值表示。

就像其他解决scheme一样,它使用- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event方法。 该方法在iOS执行命中testing时被调用。 这篇博文对iOS命中testing如何工作有很好的描述。

https://github.com/kgaidis/KGHitTestingViews

 @interface KGHitTestingButton : UIButton <KGHitTesting> @property (nonatomic) CGFloat minimumHitTestHeight; @property (nonatomic) CGFloat minimumHitTestWidth; @end 

你也可以在不写任何代码的情况下inheritance和使用Interface Builder: 在这里输入图像说明

我使用tableviewcell.accessoryView中的button来增加它的触摸面积

 #pragma mark - Touches - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self]; CGRect accessoryViewTouchRect = CGRectInset(self.accessoryView.frame, -15, -15); if(!CGRectContainsPoint(accessoryViewTouchRect, location)) [super touchesBegan:touches withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self]; CGRect accessoryViewTouchRect = CGRectInset(self.accessoryView.frame, -15, -15); if(CGRectContainsPoint(accessoryViewTouchRect, location) && [self.accessoryView isKindOfClass:[UIButton class]]) { [(UIButton *)self.accessoryView sendActionsForControlEvents:UIControlEventTouchUpInside]; } else [super touchesEnded:touches withEvent:event]; } 

我刚刚在swift 2.2中做了@Chase 解决scheme的端口

 import Foundation import ObjectiveC private var hitTestEdgeInsetsKey: UIEdgeInsets = UIEdgeInsetsZero extension UIButton { var hitTestEdgeInsets:UIEdgeInsets { get { let inset = objc_getAssociatedObject(self, &hitTestEdgeInsetsKey) as? NSValue ?? NSValue(UIEdgeInsets: UIEdgeInsetsZero) return inset.UIEdgeInsetsValue() } set { let inset = NSValue(UIEdgeInsets: newValue) objc_setAssociatedObject(self, &hitTestEdgeInsetsKey, inset, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { guard !UIEdgeInsetsEqualToEdgeInsets(hitTestEdgeInsets, UIEdgeInsetsZero) && self.enabled == true && self.hidden == false else { return super.pointInside(point, withEvent: event) } let relativeFrame = self.bounds let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets) return CGRectContainsPoint(hitFrame, point) } } 

你可以像这样使用

 button.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10) 

有关其他参考,请参阅https://stackoverflow.com/a/13067285/1728552

基于上面的giaset的答案(我发现最优雅的解决scheme),这里是快速的3版本:

 import UIKit fileprivate let minimumHitArea = CGSize(width: 44, height: 44) extension UIButton { open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // if the button is hidden/disabled/transparent it can't be hit if isHidden || !isUserInteractionEnabled || alpha < 0.01 { return nil } // increase the hit frame to be at least as big as `minimumHitArea` let buttonSize = bounds.size let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0) let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0) let largerFrame = bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2) // perform hit test on larger frame return (largerFrame.contains(point)) ? self : nil } } 

我遵循了Chase的反应,它的效果很好,当你创build的arrea太大,比取消selectbutton的区域大(如果区域不大),它不会调用UIControlEventTouchUpInside事件的select器。

我认为任何方向或任何方向的大小超过200。

我对这个游戏太晚了,但是想用一个简单的技术来解决你的问题。 这里是一个典型的编程UIButton片段对我来说:

 UIImage *arrowImage = [UIImage imageNamed:@"leftarrow"]; arrowButton = [[UIButton alloc] initWithFrame:CGRectMake(15.0, self.frame.size.height-35.0, arrowImage.size.width/2, arrowImage.size.height/2)]; [arrowButton setBackgroundImage:arrowImage forState:UIControlStateNormal]; [arrowButton addTarget:self action:@selector(onTouchUp:) forControlEvents:UIControlEventTouchUpOutside]; [arrowButton addTarget:self action:@selector(onTouchDown:) forControlEvents:UIControlEventTouchDown]; [arrowButton addTarget:self action:@selector(onTap:) forControlEvents:UIControlEventTouchUpInside]; [arrowButton addTarget:self action:@selector(onTouchUp:) forControlEvents:UIControlEventTouchDragExit]; [arrowButton setUserInteractionEnabled:TRUE]; [arrowButton setAdjustsImageWhenHighlighted:NO]; [arrowButton setTag:1]; [self addSubview:arrowButton]; 

我正在加载一个透明的PNG图像我的button,并设置背景图像。 我设置基于UIImage框架和缩放50%的视网膜。 好的,也许你同意以上或者不同意,但是如果你想让“BIGGER”成为热门话题,

我所做的,在Photoshop中打开图像,并简单地将canvas大小增加到120%,并保存。 实际上,您只是通过透明像素使图像更大。

只有一个办法。

上面的@jlajlar的回答看起来不错,直接,但与Xamarin.iOS不匹配,所以我把它转换成Xamarin。 如果你在Xamarin iOS上寻找解决scheme,那么这里有:

 public override bool PointInside (CoreGraphics.CGPoint point, UIEvent uievent) { var margin = -10f; var area = this.Bounds; var expandedArea = area.Inset(margin, margin); return expandedArea.Contains(point); } 

您可以将此方法添加到您重写UIView或UIImageView的类中。 这工作很好:)

没有一个答案对我来说是完美的,因为我使用背景图片和那个button上的标题。 此外,button将随着屏幕大小的改变而resize。

相反,我通过使png透明区域变大来扩大水龙头区域。

这个Swift版本可以让你为所有的UIButtons定义一个最小的命中大小。 至关重要的是,它也处理的情况下,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 } } 
  • 不用重写类别或扩展名
  • 每个准则至less为44×44

我的拿:

 open class MinimumTouchAreaButton: UIButton { open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard !self.isHidden, self.isUserInteractionEnabled, self.alpha > 0 else { return nil } let expandedBounds = bounds.insetBy(dx: min(bounds.width - 44, 0), dy: min(bounds.height - 44, 0)) return expandedBounds.contains(point) ? self : nil } }