ios UIButton:使命中区域大于默认命中区域
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/808503/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UIButton: Making the hit area larger than the default hit area
提问by Kevin Bomberry
I have a question dealing with UIButton and its hit area. I am using the Info Dark button in interface builder, but I am finding that the hit area is not large enough for some people's fingers.
我有一个关于 UIButton 及其命中区域的问题。我正在使用界面构建器中的 Info Dark 按钮,但我发现点击区域对于某些人的手指来说不够大。
Is there a way to increase the hit area of a button either programmatically or in Interface Builder without changing the size of the InfoButton graphic?
有没有办法以编程方式或在 Interface Builder 中增加按钮的点击区域而不改变 InfoButton 图形的大小?
回答by Chase
Since I am using a background image, none of these solutions worked well for me. Here is a solution that does some fun objective-c magic and offers a drop in solution with minimal code.
由于我使用的是背景图像,因此这些解决方案都不适合我。这是一个解决方案,它执行了一些有趣的 Objective-C 魔法,并提供了一个代码最少的解决方案。
First, add a category to UIButton
that overrides the hit test and also adds a property for expanding the hit test frame.
首先,添加一个类别以UIButton
覆盖命中测试,并添加一个用于扩展命中测试框架的属性。
UIButton+Extensions.h
UIButton+扩展.h
@interface UIButton (Extensions)
@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end
UIButton+Extensions.m
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
Once this class is added, all you need to do is set the edge insets of your button. Note that I chose to add the insets so if you want to make the hit area larger, you must use negative numbers.
添加此类后,您需要做的就是设置按钮的边缘插入。请注意,我选择添加插图,因此如果您想让命中区域更大,则必须使用负数。
[button setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)];
Note: Remember to import the category (#import "UIButton+Extensions.h"
) in your classes.
注意:请记住#import "UIButton+Extensions.h"
在您的类中导入类别 ( )。
回答by Samuel Goodwin
Just set the image edge inset values in interface builder.
只需在界面构建器中设置图像边缘插入值。
回答by giaset
Here's an elegant solution using Extensions in Swift. It gives all UIButtons a hit area of at least 44x44 points, as per Apple's Human Interface Guidelines (https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/)
这是在 Swift 中使用扩展的优雅解决方案。根据 Apple 的人机界面指南 ( https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/) ,它为所有 UIButton 提供了至少 44x44 点的命中区域
Swift 2:
斯威夫特 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:
斯威夫特 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
}
}
回答by jlajlar
You could also subclass UIButton
or a custom UIView
and override point(inside:with:)
with something like:
您还可以使用以下内容进行子类化UIButton
或自定义UIView
并覆盖point(inside:with:)
:
Swift 3
斯威夫特 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
目标-C
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGFloat margin = 5.0;
CGRect area = CGRectInset(self.bounds, -margin, -margin);
return CGRectContainsPoint(area, point);
}
回答by dcRay
Here's Chase's UIButton+Extensions in Swift 3.0.
这是 Swift 3.0 中 Chase 的 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)
}
}
To use it, you can:
要使用它,您可以:
button.touchAreaEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
回答by Maurizio
Don't set the backgroundImage
property with your image, set the imageView
property. Also, make sure you have imageView.contentMode
set at UIViewContentModeCenter
.
不要backgroundImage
用你的图像设置imageView
属性,设置属性。另外,请确保您已imageView.contentMode
设置为UIViewContentModeCenter
。
回答by Kyle
I recommend placing a UIButton with type Custom centered over your info button. Resize the custom button to the size you want the hit area to be. From there you have two options:
我建议在您的信息按钮上方放置一个自定义类型的 UIButton。将自定义按钮调整为您希望点击区域的大小。从那里你有两个选择:
Check the 'Show touch on highlight' option of the custom button. The white glow will appear over the info button, but in most cases the users finger will cover this and all they will see is the glow around the outside.
Set up an IBOutlet for the info button and two IBActions for the custom button one for 'Touch Down' and one for the 'Touch Up Inside'. Then in Xcode make the touchdown event set the highlighted property of the info button to YES and the touchupinside event set the highlighted property to NO.
选中自定义按钮的“高亮显示触摸”选项。白光会出现在信息按钮上,但在大多数情况下,用户的手指会覆盖它,他们只会看到外面的光。
为信息按钮设置一个 IBOutlet,为自定义按钮设置两个 IBAction,一个用于“Touch Down”,另一个用于“Touch Up Inside”。然后在 Xcode 中使 touchdown 事件将信息按钮的突出显示属性设置为 YES,并将 touchupinside 事件设置为突出显示的属性为 NO。
回答by Zhanserik
My solution on Swift 3:
我在 Swift 3 上的解决方案:
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)
}
}
回答by Tommie C.
There is nothing wrong with the answers presented; however I wanted to extend jlarjlar's answeras it holds amazing potential that can add value to the same problem with other controls (e.g. SearchBar). This is because since pointInside is attached to a UIView, one is able to subclass any controlto improve the touch area. This answer also shows a full sample of how to implement the complete solution.
所提供的答案没有任何问题;但是我想扩展jlarjlar 的答案,因为它具有惊人的潜力,可以为其他控件(例如 SearchBar)的相同问题增加价值。这是因为由于 pointInside 附加到 UIView,因此可以对任何控件进行子类化以改善触摸区域。此答案还显示了如何实施完整解决方案的完整示例。
Create a new subclass for your button (or any control)
为您的按钮(或任何控件)创建一个新的子类
#import <UIKit/UIKit.h>
@interface MNGButton : UIButton
@end
Next override the pointInside method in your subclass implementation
接下来覆盖子类实现中的 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
On your storyboard/xib file select the control in question and open the identity inspector and type in the name of your custom class.
在您的故事板/xib 文件中,选择有问题的控件并打开身份检查器并输入您的自定义类的名称。
In your UIViewController class for scene containing the button, change the class type for the button to the name of your subclass.
在包含按钮的场景的 UIViewController 类中,将按钮的类类型更改为子类的名称。
@property (weak, nonatomic) IBOutlet MNGButton *helpButton;
Link your storyboard/xib button to the property IBOutlet and your touch area will be expanded to fit the area defined in the subclass.
将您的故事板/xib 按钮链接到属性 IBOutlet,您的触摸区域将被扩展以适应子类中定义的区域。
In addition to overriding the pointInside methodtogether with the CGRectInsetand CGRectContainsPointmethods, one should take time to examine the CGGeometryfor extending the rectangular touch area of any UIView subclass. You may also find some nice tips on CGGeometry use-cases at NSHipster.
除了重写pointInside方法与一起CGRectInset和CGRectContainsPoint方法,应该需要时间来检查CGGeometry用于延长任何UIView子类的矩形触摸区域。您也可以找到关于CGGeometry使用情况在一些不错的提示NSHipster。
For example one could make the touch area irregular using the methods mentioned above or simply choose to make the width touch area twice as large as the horizontal touch area:
例如,可以使用上述方法使触摸区域不规则,或者简单地选择将宽度触摸区域设为水平触摸区域的两倍:
CGRect area = CGRectInset(self.bounds, -(2*margin), -margin);
NB: Substituting any UI Class control should produce similar results on extending the touch area for different controls (or any UIView subclass, like UIImageView, etc).
注意:替换任何 UI 类控件应该在扩展不同控件(或任何 UIView 子类,如 UIImageView 等)的触摸区域方面产生类似的结果。
回答by Olof
This works for me:
这对我有用:
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)];