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

时间:2009-04-30 18:54:52

标签: ios cocoa-touch uibutton uikit

我有一个关于UIButton及其命中区域的问题。我正在使用界面构建器中的Info Dark按钮,但我发现命中区域对于某些人的手指来说不够大。

有没有办法以编程方式或在Interface Builder中增加按钮的命中区域而不改变InfoButton图形的大小?

36 个答案:

答案 0 :(得分:137)

由于我使用的是背景图片,因此这些解决方案都不适合我。这是一个解决方案,可以实现一些有趣的Objective-c魔术,并提供最少代码的解决方案。

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

<强> 的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 setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)];

注意:请务必在班级中导入类别(#import "UIButton+Extensions.h")。

答案 1 :(得分:75)

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

答案 2 :(得分:61)

这是使用Swift中的Extensions的优雅解决方案。根据Apple的人机界面指南(https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/),它为所有UIButton提供了至少44x44点的命中区域

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
    }
}

斯威夫特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
    }
}

答案 3 :(得分:46)

您还可以将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)
}

<强>目标C

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

答案 4 :(得分:32)

这是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)

答案 5 :(得分:19)

不要将backgroundImage属性设置为您的图片,请设置imageView属性。另外,请确保imageView.contentMode设置为UIViewContentModeCenter

答案 6 :(得分:18)

我建议在信息按钮上放置一个自定义类型的UIButton。将自定义按钮的大小调整为您希望命中区域的大小。从那里你有两个选择:

  1. 选中自定义按钮的'突出显示触摸'选项。白色发光将出现在信息按钮上,但在大多数情况下,用户的手指将覆盖这一点,他们将看到的只是外面的光晕。

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

答案 7 :(得分:12)

我在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)
    }
}

答案 8 :(得分:12)

所提出的答案没有错;但是我想扩展jlarjlar's answer因为它具有惊人的潜力,可以为其他控件(例如SearchBar)增加相同问题的价值。这是因为因为pointInside附加到UIView,所以可以将任何控件子类化以改善触摸区域。这个答案还展示了如何实施完整解决方案的完整示例。

为您的按钮(或任何控件)创建一个新的子类

#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文件中选择有问题的控件并打开身份检查器并输入自定义类的名称。

mngbutton

在包含按钮的场景的UIViewController类中,将按钮的类类型更改为子类的名称。

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

将您的storyboard / xib按钮链接到属性IBOutlet,您的触摸区域将扩展为适合子类中定义的区域。

除了使用pointInside methodCGRectInset方法覆盖CGRectContainsPoint之外,还需要花时间检查CGGeometry以扩展任何UIView的矩形触摸区域子类。您还可以在NSHipster找到关于CGGeometry用例的一些很好的提示。

例如,可以使用上述方法使触摸区域不规则,或者只选择将宽度触摸区域设置为水平触摸区域的两倍:

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

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

答案 9 :(得分:10)

这对我有用:

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)];

答案 10 :(得分:7)

我通过调动-[UIView pointInside:withEvent:]使用更通用的方法。这允许我修改任何UIView上的命中测试行为,而不仅仅是UIButton

通常,按钮放置在容器视图内,这也限制了命中测试。例如,当按钮位于容器视图的顶部并且您想要向上扩展触摸目标时,您还必须扩展容器视图的触摸目标。

@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不能直接作为那里的类型使用,但由于CGRect也包含一个带有四个CGFloat的结构,它通过选择“Rect”并填充如下值来完美地工作: {{top, left}, {bottom, right}}

答案 11 :(得分:7)

不要改变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

答案 12 :(得分:5)

我已经能够以编程方式增加信息按钮的命中区域。 “i”图形不会改变比例并保持在新按钮框的中心。

在Interface Builder中,信息按钮的大小似乎固定为18x19 [*]。通过将其连接到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的更高版本似乎增加了信息按钮的命中区域。

答案 13 :(得分:5)

这是我的 Swift 3 解决方案(基于此博文: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
    }
}

答案 14 :(得分:5)

好吧,您可以将UIButton置于透明且略大的UIView中,然后像在UIButton中一样捕获UIView实例上的触摸事件。这样,您仍然可以使用按钮,但触摸区域更大。您将手动处理所选的&amp;如果用户触摸视图而不是按钮,则突出显示状态为按钮。

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

答案 15 :(得分:5)

我在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)
    }
}

答案 16 :(得分:3)

通过覆盖继承UIButton实现。

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)
    }
}

答案 17 :(得分:3)

Chase的自定义命中测试作为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

答案 18 :(得分:2)

就这么简单:

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

这就是全部。

答案 19 :(得分:2)

基于giaset上面的答案(我找到了最优雅的解决方案),这里是swift 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
    }
}

答案 20 :(得分:2)

为此目的我做了library

您可以选择使用UIView类别,无需子类化

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

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

与其他解决方案一样,它使用- (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: enter image description here

答案 21 :(得分:2)

永远不要覆盖类别中的方法。子类按钮并覆盖- pointInside:withEvent:。例如,如果您的按钮侧小于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));
}

答案 22 :(得分:2)

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

答案 23 :(得分:1)

我刚刚在swift 2.2中完成了@Chase solution的端口

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

答案 24 :(得分:1)

我在tableviewcell.accessoryView中使用此技巧来扩大其触摸区域

#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];
}

答案 25 :(得分:0)

这些答案都不适合我,因为我在该按钮上使用了背景图片和标题。此外,随着屏幕尺寸的变化,按钮会调整大小。

相反,我通过使png透明区域变大来扩大拍子区域。

答案 26 :(得分:0)

@jlajlar上面的回答似乎很好,很简单,但与Xamarin.iOS不符,所以我把它转换成了Xamarin。 如果您在Xamarin iOS上寻找解决方案,请点击此处:

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的类中。这非常有效:)

答案 27 :(得分:0)

我对这个游戏这么晚了,但是想要权衡一个可以解决你的问题的简单技术。以下是我的典型程序化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图像并设置背景图像。我正在根据UIImage设置框架,并为视网膜缩放50%。好吧,也许你同意上述与否,但是如果你想让命中区域更大并且让自己头疼:

我做什么,在photoshop中打开图像,只需将画布大小增加到120%即可保存。实际上,你只是通过透明像素使图像更大。

只是一种方法。

答案 28 :(得分:0)

此Swift版本允许您为所有UIButton定义最小命中大小。至关重要的是,当UIButton被隐藏时,它也会处理这种情况,很多人都会忽略这一点。

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
    }
}

答案 29 :(得分:0)

我已经跟踪了Chase的回复并且它工作得很好,当你创建的arrea太大,比取消选择按钮的区域大时(如果区域不大)它不会调用选择器UIControlEventTouchUpInside事件。

我认为任何方向或类似的东西都超过200个。

答案 30 :(得分:0)

类似于Zhanserik,具有可变扩展名并已针对Swift 4.2进行了更新:

class ButtonWithExtendedHitArea: UIButton {

    var extention: CGFloat

    required init(extendBy: CGFloat) {
        extention = extendBy

        super.init(frame: .zero)
    }

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

    override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let relativeFrame = self.bounds
        let hitTestEdgeInsets = UIEdgeInsets(top: -extention, left: -extention, bottom: -extention, right: -extention)
        let hitFrame = relativeFrame.inset(by: hitTestEdgeInsets)
        return hitFrame.contains(point)
    }

}

答案 31 :(得分:0)

@antoine的答案格式为Swift 4

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    static Pattern PATTERN = Pattern.compile("(\\d*\\.\\d*)\\.(\\d*)");

    public static void main(String[] args) {
        System.out.println(removeSecondDot("1341.1241"));
        System.out.println(removeSecondDot("2.4"));
        System.out.println(removeSecondDot("552"));
        System.out.println(removeSecondDot("1"));
        System.out.println(removeSecondDot(".2341.1525"));
        System.out.println(removeSecondDot("1235.446.634"));
        System.out.println(removeSecondDot("51524.24."));
    }

    private static String removeSecondDot(String input) {
        Matcher m = PATTERN.matcher(input);
        if (m.matches()) {
            return m.group(1) + m.group(2);
        }
        return input;
    }
}

答案 32 :(得分:0)

我看到了很多解决方案,它们要么没有达到目标,要么需要指定一些固定的插入项来添加。这是一个简单的UIView子类的解决方案,该子类将视图的点击区域扩展到至少 44 x 44;如果任何一个尺寸都已经大于该尺寸,则不会人为填充该尺寸。

这可确保按钮始终具有建议的最小触摸尺寸44 x 44,而无需任何手动配置,计算或图像填充:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let minimumTouchSize: CGFloat = 44.0
    let center: CGPoint = .init(x: self.bounds.midX, y: self.bounds.midY)

    let minimumHitRect: CGRect =
        .init(center: center, size: .zero)
        .insetBy(
            dx: -minimumTouchSize / 2.0,
            dy: -minimumTouchSize / 2.0
        )

    let fullHitRect = self.bounds.union(minimumHitRect)

    return fullHitRect.contains(point)
}

答案 33 :(得分:-1)

夫特:

override func viewWillAppear(animated: Bool) {
        self.sampleButton.frame = CGRectInset(self.sampleButton.frame, -10, -10);
    }

答案 34 :(得分:-1)

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let inset = UIEdgeInsets(top: -adjustHitY * 0.5, left: -adjustHitX * 0.5, bottom: -adjustHitY * 0.5, right: -adjustHitX * 0.5)
    return UIEdgeInsetsInsetRect(bounds, inset).contains(point)
}

答案 35 :(得分:-1)

  • 不覆盖类别或扩展名
  • 每条指南最低44x44

我的看法:

// remove the upsells action using the same priority as original add_action()
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_upsell_display', 15 );
// add my custom upsells function in the same place
add_action( 'woocommerce_after_single_product_summary', 'custom_upsell_display', 15 );
function custom_upsell_display() {
// default parameters:
// woocommerce_upsell_display( $limit = '-1', $columns = 4, $orderby = 'rand', $order = 'desc' )
// call it with my parameters
woocommerce_upsell_display( -1, 4, 'price', 'asc' );
}