source

UIButton:적중 영역을 기본 적중 영역보다 크게 만들기

manysource 2023. 7. 19. 21:26

UIButton:적중 영역을 기본 적중 영역보다 크게 만들기

UIButton과 히트 지역에 대한 질문이 있습니다.인터페이스 작성기에서 Info Dark 버튼을 사용하고 있지만 일부 사용자가 손가락을 사용할 수 있을 정도로 히트 영역이 크지 않습니다.

InfoButton 그래픽의 크기를 변경하지 않고 프로그래밍 방식으로 또는 Interface Builder에서 버튼의 적중 영역을 늘릴 수 있는 방법이 있습니까?

배경 이미지를 사용하고 있기 때문에 이러한 솔루션 중 어느 것도 제대로 작동하지 않았습니다.여기 재미있는 목적-c 마술을 하고 최소한의 코드로 솔루션을 떨어뜨리는 솔루션이 있습니다.

를 먼범를에추니다에 합니다.UIButton히트 테스트를 재정의하고 히트 테스트 프레임을 확장하기 위한 속성을 추가합니다.

UIButton+확장자.h

@interface UIButton (Extensions)

@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;

@end

UIButton+확장자.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" 에서. 당신의 수업에서.

인터페이스 작성기에서 이미지 가장자리 설정 값을 설정하기만 하면 됩니다.

Swift에서 Extensions를 사용하는 우아한 솔루션이 있습니다.Apple의 휴먼 인터페이스 지침(https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/) 에 따라 모든 UI 버튼에 최소 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
    }
}

스위프트 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:)다음과 같은 것으로:

스위프트 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);
}

Chase의 UIButton+입니다.Swift 3.0의 확장 기능.


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)

사용자 지정 유형의 UI 버튼을 정보 버튼 중앙에 배치하는 것이 좋습니다.사용자 지정 버튼의 크기를 히트 영역에 적용할 크기로 조정합니다.여기서 두 가지 옵션을 사용할 수 있습니다.

  1. 사용자 지정 버튼의 'Show touch on highlight' 옵션을 선택합니다.정보 버튼 위에 흰색 빛이 나타나지만 대부분의 경우 사용자 손가락이 이 부분을 가리므로 외부의 빛만 볼 수 있습니다.

  2. 정보 버튼용 IBO 아울렛과 사용자 지정 버튼용 2개의 IBA 조치를 설정합니다. 하나는 '터치 다운'용이고 다른 하나는 '터치 업 인사이드'용입니다.그런 다음 Xcode에서 터치다운 이벤트가 정보 버튼의 강조 표시된 속성을 YES로 설정하고 터치업 내부 이벤트가 강조 표시된 속성을 NO로 설정하도록 합니다.

backgroundImage이미지로 하세요.imageView소유물.또한, 당신이 가지고 있는 것을 확인하세요.imageView.contentMode을 겨냥한.UIViewContentModeCenter.

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

제시된 답변에는 아무런 문제가 없지만, 다른 컨트롤(예: SearchBar)과 동일한 문제에 가치를 더할 수 있는 놀라운 잠재력을 가지고 있기 때문에 jlarjlar의 답변을 확장하고 싶었습니다.그 이유는 그 이후로내부는 UI 뷰에 부착되어 있으며, 터치 영역을 개선하기 위해 모든 컨트롤을 하위 분류할 수 있습니다.이 답변에는 전체 솔루션을 구현하는 방법에 대한 전체 샘플도 나와 있습니다.

단추(또는 모든 컨트롤)에 대한 새 하위 클래스 만들기

#import <UIKit/UIKit.h>

@interface MNGButton : UIButton

@end

다음은 점을 재정의합니다.하위 클래스 구현의 내부 메서드

@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 버튼을 속성 IO Butlet에 연결하면 하위 클래스에 정의된 영역에 맞게 터치 영역이 확장됩니다.

을 무시하는 것 외에도내부 방법은 CGRectInset CGRectContainsPoint 방법과 함께 UIView 하위 클래스의 직사각형 터치 영역을 확장하기 위한 CGeometry를 검토하는 데 시간을 할애해야 합니다.NSHipster에서는 CG 기하학 사용 사례에 대한 몇 가지 유용한 팁도 찾을 수 있습니다.

예를 들어 위에서 언급한 방법을 사용하여 터치 영역을 불규칙하게 만들거나 단순히 폭 터치 영역을 수평 터치 영역보다 두 배 더 크게 만들 수 있습니다.

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

NB: UI 클래스 컨트롤을 대체하면 다른 컨트롤(또는 UIImageView 등의 UIView 하위 클래스)에 대한 터치 영역을 확장할 때 유사한 결과를 얻을 수 있습니다.

이것은 나에게 도움이 됩니다.

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

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

좀 더 일반적인 접근법을 사용하고 있습니다.-[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}}.

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

UIButton을 투명하고 약간 큰 UIView 안에 배치한 다음 UIButton에서처럼 UIView 인스턴스에서 터치 이벤트를 캡처할 수 있습니다.이렇게 하면 버튼은 그대로 유지되지만 터치 영역은 더 넓어집니다.사용자가 버튼 대신 보기를 누르면 버튼에서 선택되고 강조 표시된 상태를 수동으로 처리해야 합니다.

다른 방법으로는 UI 단추 대신 UI 이미지를 사용할 수 있습니다.

프로그램적으로 정보 버튼의 적중 영역을 늘릴 수 있었습니다."i" 그래픽은 축척을 변경하지 않고 새 단추 프레임의 중앙에 유지됩니다.

info 버튼의 크기는 Interface Builder에서 18x19[*]로 고정되어 있는 것 같습니다.IBO 아울렛에 연결하여 코드로 프레임 크기를 문제없이 변경할 수 있었습니다.

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에서는 정보 버튼의 적중 영역이 증가한 것으로 보입니다.

이것은 저의 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
    }
}

솔직히.2023년의 올바른 코드:

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

이것은 모든 유형의 UIView에서 완벽하게 작동합니다.

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

그게 전부야.

UIButton의 하위 클래스로 구현된 Chase의 맞춤형 적중 테스트입니다.목표-C로 작성됨.

두 가지 모두에 효과가 있는 것 같습니다.init그리고.buttonWithType:건설업자내가 필요로 하는 것은 완벽하지만, 하위 분류 이후로.UIButton털이 많을 수 있습니다. 누구든 그것에 대해 잘못이 있는지 알고 싶습니다.

사용자 지정 적중 영역 버튼.h

#import <UIKit/UIKit.h>

@interface CustomHitAreaButton : UIButton

- (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets;

@end

사용자 지정 적중 영역 단추

#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

상속된 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)
    }
}

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

WJB 배경삽입 단추.h

#import <UIKit/UIKit.h>

@interface WJBackgroundInsetButton : UIButton {
    UIEdgeInsets backgroundEdgeInsets_;
}

@property (nonatomic) UIEdgeInsets backgroundEdgeInsets;

@end

WJB 배경삽입 단추

#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

범주의 메서드를 재정의하지 마십시오.하위 클래스 버튼 및 재정의- pointInside:withEvent:예를 들어 버튼 측면이 44px(최소 탭 가능 영역으로 권장됨)보다 작은 경우 다음을 사용합니다.

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

바로 이 목적을 위해 도서관을 만들었습니다.

사용하도록 선택할 수 있습니다.UIView범주, 하위 분류가 필요하지 않습니다.

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

또는 하고 "UIView " UIButton"을 할 수 있습니다.minimumHitTestWidth 및/는minimumHitTestHeight그러면 버튼 히트 테스트 영역이 이 두 값으로 표시됩니다.

다른 솔루션과 마찬가지로 다음을 사용합니다.- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event방법.이 메소드는 iOS가 히트 테스트를 수행할 때 호출됩니다.이 블로그 게시물은 iOS 히트 테스트가 어떻게 작동하는지에 대한 좋은 설명을 담고 있습니다.

https://github.com/kgaidis/KGHitTestingViews

@interface KGHitTestingButton : UIButton <KGHitTesting>

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

@end

하지 않고 .enter image description here

tableviewcell.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];
}

방금 swift 2.2에서 @Chase 솔루션의 포트를 수행했습니다.

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

이렇게 사용할 수 있는 an

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

기타 참고 자료는 https://stackoverflow.com/a/13067285/1728552 를 참조하십시오.

저는 정확하지 않거나 추가할 고정 삽입물을 지정해야 하는 솔루션을 많이 보고 있습니다.간단한 솔루션은 다음과 같습니다.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)
}

나는 Chase의 반응을 따라왔고 그것은 잘 작동합니다. 단 하나의 문제는 당신이 버튼이 선택 해제되는 구역보다 너무 크고, 버튼이 선택 해제되는 구역보다 클 때(구역이 더 크지 않은 경우) 그것은 UIControlEvent의 셀렉터를 호출하지 않습니다.TouchUpInside 이벤트를 누릅니다.

어떤 방향이든 크기는 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 이미지를 로드하고 배경 이미지를 설정하고 있습니다.저는 UI 이미지를 기반으로 프레임을 설정하고 망막에 대해 50% 스케일링을 하고 있습니다.좋아요, 아마도 당신은 위의 것에 동의하거나 동의하지 않을지도 모르지만, 만약 당신이 히트 지역을 더 크게 만들고 당신 자신을 두통거리로 만들고 싶다면:

제가 하는 일은, 포토샵으로 이미지를 열고 캔버스 크기를 120%로 늘리고 절약하는 것입니다.효과적으로 투명 픽셀로 이미지를 더 크게 만들었습니다.

하나의 접근법입니다.

@jlajlar의 위의 대답은 좋고 직설적으로 보였지만 Xamarin과 일치하지 않습니다.iOS, 그래서 저는 그것을 사마린으로 변환했습니다.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를 재정의하는 클래스에 추가할 수 있습니다.이것은 잘 작동했습니다 :)

배경 이미지와 제목을 단추에 사용하기 때문에 어떤 대답도 완벽하게 맞지 않습니다.또한 화면 크기가 변경되면 버튼의 크기가 조정됩니다.

대신, 저는 png 투명 영역을 크게 하여 탭 영역을 확대합니다.

언급URL : https://stackoverflow.com/questions/808503/uibutton-making-the-hit-area-larger-than-the-default-hit-area