web-dev-qa-db-ja.com

UIButton:ヒットエリアをデフォルトのヒットエリアより大きくする

UIButtonとそのヒット領域を扱う質問があります。インターフェイスビルダーの[情報の暗闇]ボタンを使用していますが、ヒット領域が一部の人の指に十分な大きさでないことがわかりました。

InfoButtonグラフィックのサイズを変更せずに、プログラムで、またはInterface Builderでボタンのヒット領域を増やす方法はありますか?

184
Kevin Bomberry

背景画像を使用しているため、これらのソリューションはどれもうまくいきませんでした。これは、いくつかの楽しい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")をインポートしてください。

138
Chase

インターフェースビルダーで画像のエッジインセット値を設定するだけです。

75
Samuel Goodwin

Swiftの拡張機能を使用したエレガントなソリューションを次に示します。 Appleのヒューマンインターフェイスガイドライン( https://developer.Apple.com/ios/human-interface-guidelines/visual-design/layout/ に従って、すべてのUIButtonに少なくとも44x44ポイントのヒット領域を与えます=)

スイフト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
    }
}
61
giaset

UIButtonまたはカスタムUIViewをサブクラス化し、point(inside:with:)を次のようなものでオーバーライドすることもできます。

Swift

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);
}
46
jlajlar

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

それを使用するには、次のことができます。

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

画像にbackgroundImageプロパティを設定しないで、imageViewプロパティを設定してください。また、imageView.contentModeUIViewContentModeCenterに設定されていることを確認してください。

19
Maurizio

情報ボタンの上にカスタムタイプのUIButtonを配置することをお勧めします。カスタムボタンのサイズをヒットエリアのサイズに変更します。そこから2つのオプションがあります。

  1. カスタムボタンの[ハイライト時にタッチを表示]オプションをオンにします。情報ボタンの上に白い輝きが表示されますが、ほとんどの場合、ユーザーの指がこれを覆い、表示されるのは外側の周りの輝きだけです。

  2. 情報ボタンにIBOutletを、カスタムボタンに2つのIBActionsを設定します。1つは「Touch Down」用、もう1つは「Touch Up Inside」用です。次に、Xcodeで、touchdownイベントで情報ボタンの強調表示されたプロパティをYESに設定し、touchupinsideイベントで強調表示されたプロパティをNOに設定します。

18
Kyle

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

提示された回答に問題はありません。ただし、 jlarjlarの答え を拡張したかったのは、他のコントロール(SearchBarなど)で同じ問題に価値を追加できる驚くべき可能性があるためです。これは、pointInsideがUIViewにアタッチされているため、サブクラスany controlを使用してタッチ領域を改善できるためです。この回答は、完全なソリューションを実装する方法の完全なサンプルも示しています。

ボタン(または任意のコントロール)の新しいサブクラスを作成します

#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

ストーリーボード/ xibファイルで、問題のコントロールを選択し、IDインスペクターを開いて、カスタムクラスの名前を入力します。

mngbutton

ボタンを含むシーンのUIViewControllerクラスで、ボタンのクラスタイプをサブクラスの名前に変更します。

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

ストーリーボード/ xibボタンをプロパティIBOutletにリンクすると、タッチ領域がサブクラスで定義された領域に合わせて拡張されます。

pointInside methodCGRectInset および CGRectContainsPoint メソッドとともにオーバーライドすることに加えて、 CGGeometry を調べるのに時間がかかるはずです。 = UIViewサブクラスの長方形のタッチ領域を拡張します。 NSHipster でCGGeometryのユースケースに関する素敵なヒントを見つけることもできます。

たとえば、上記の方法を使用してタッチ領域を不規則にしたり、幅のタッチ領域を水平のタッチ領域の2倍にしたりすることができます。

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

NB:UIクラスコントロールを置き換えると、異なるコントロール(またはUIImageViewなどのUIViewサブクラス)のタッチ領域を拡張する際に同様の結果が得られます。

12
Tommie C.

これは私のために働く:

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
Olof

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

-[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も4つのCGFloatを持つ構造体で構成されているため、[Rect]を選択して次のように値を入力することで問題なく動作します: {{top, left}, {bottom, right}}

7
Ortwin Gentz

さて、透明でわずかに大きいUIView内にUIButtonを配置し、UIButtonのようにUIViewインスタンスでタッチイベントをキャッチできます。そうすれば、ボタンは引き続き使用できますが、タッチ領域は大きくなります。ユーザーがボタンではなくビューにタッチした場合、ボタンで選択および強調表示された状態を手動で処理する必要があります。

他の可能性には、UIButtonの代わりにUIImageを使用することが含まれます。

5

これは私のSwift 3ソリューションです(このブログ投稿に基づいて: http://bdunagan.com/2010/03/01/iphone- tip-larger-hit-for-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
    }
}
5
user2104363

Interface Builderプロパティを有効にしてマージンを調整するために、Swiftで次のクラスを使用しています。

@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)
    }
}
5
Antoine

情報ボタンのヒット領域をプログラムで増やすことができました。 「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の以降のバージョンでは、情報ボタンのヒット領域が増加しているようです。

5
otto

オーバーライド継承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)
    }
}
3
dimpiax

UIButtonのサブクラスとして実装されたChaseのカスタムヒットテスト。 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
3
Phillip Martin

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
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));
}
2
user500

上記の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
    }
}
2
Nhon Nguyen

この目的のために library を作成しました。

UIViewカテゴリーを使用することを選択できますサブクラス化は不要です

@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を使用することもできます。 enter image description here

2
kgaidis

Swift 4用にフォーマットされた@antoineの回答

class ExtendedHitButton: UIButton
{
    override func point( inside point: CGPoint, with event: UIEvent? ) -> Bool
    {
        let relativeFrame = self.bounds
        let hitTestEdgeInsets = UIEdgeInsetsMake( -44, -44, -44, -44 ) // Apple recommended hit target
        let hitFrame = UIEdgeInsetsInsetRect( relativeFrame, hitTestEdgeInsets )
        return hitFrame.contains( point );
    }
}
1
spfursich

@Chaseの移植を行いました solution in Swift 2.2

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 を参照してください

1
Serluca

Uitableviewcell.accessoryビュー内のボタンにこのトリックを使用して、タッチ領域を拡大します

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

この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
    }
}
0
Crashalot

上記の@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をオーバーライドするクラスにこのメソッドを追加できます。これはうまくいきました:)

0
Has AlTaiar

私はチェイスの応答を追跡しましたが、ボタンが選択解除されるゾーンよりも大きいエリアを作成する場合、ゾーンが大きくない場合、UIControlEventTouchUpInsideイベントのセレクターを呼び出しません。 。

どんな方向やそのようなものでも、サイズは200を超えると思います。

0
Unreal Dragon

私はこのゲームにとても遅れていますが、あなたの問題を解決するかもしれない簡単なテクニックに重きを置きたかったのです。これが私にとって典型的なプログラムによる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%スケーリングします。 OK、多分あなたは上記に同意するかどうか、しかし、もしあなたがヒットエリアをより大きくして頭痛を省いたければ:

私がしていることは、Photoshopで画像を開き、キャンバスサイズを120%に増やして保存するだけです。事実上、透明ピクセルを使用して画像を大きくしただけです。

ただ一つのアプローチ。

0
chrisallick

ボタンには背景画像とタイトルを使用しているため、答えはどれも私には最適ではありません。さらに、画面サイズが変更されると、ボタンのサイズが変更されます。

代わりに、pngの透過領域を大きくすることにより、タップ領域を拡大します。

0
Jakehao

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

}
0
MT1asli8ghwoi