Programing

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

lottogame 2020. 5. 16. 09:59
반응형

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


UIButton과 히트 영역을 다루는 질문이 있습니다. 인터페이스 빌더에서 Info Dark 버튼을 사용하고 있지만 히트 영역이 일부 사람들의 손가락에 비해 충분히 크지 않다는 것을 알았습니다.

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


배경 이미지를 사용하고 있기 때문에 이러한 솔루션 중 어느 것도 나를 위해 잘 작동하지 않았습니다. 다음은 재미있는 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"수업에서 카테고리 ( ) 를 가져와야합니다 .


인터페이스 빌더에서 이미지 가장자리 삽입 값을 설정하십시오.


다음은 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
    }
}

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

다음은 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)

backgroundImage이미지로 속성을 설정하지 말고 속성을 설정하십시오 imageView. 또한, 메이크업은 확실히 당신이 한 imageView.contentMode설정 UIViewContentModeCenter.


정보 버튼 위에 맞춤 유형의 UIButton을 배치하는 것이 좋습니다. 맞춤 검색 버튼의 크기를 적중 영역의 크기로 조정하십시오. 거기에는 두 가지 옵션이 있습니다.

  1. 맞춤 검색 버튼의 '강조 표시시 터치'옵션을 확인하십시오. 정보 버튼 위에 흰색 광선이 표시되지만 대부분의 경우 사용자 손가락으로이 부분을 덮을 수 있으며 외부 주변의 광선 만 보입니다.

  2. 정보 버튼에 IBOutlet을 설정하고 커스텀 버튼에 대해 'Touch Down'에 대한 하나와 'Touch Up Inside'에 대한 두 개의 IBAction을 설정하십시오. 그런 다음 Xcode에서 터치 다운 이벤트가 정보 버튼의 강조 표시된 속성을 YES로 설정하고 touchupinside 이벤트가 강조 표시된 속성을 NO로 설정하십시오.


스위프트 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의 답변 을 확장하고 싶었습니다 . 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

스토리 보드 / xib 파일에서 문제의 컨트롤을 선택하고 신원 관리자를 열고 사용자 정의 클래스 이름을 입력하십시오.

mngbutton

버튼이 포함 된 장면의 UIViewController 클래스에서 버튼의 클래스 유형을 서브 클래스의 이름으로 변경하십시오.

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

스토리 보드 / xib 버튼을 IBOutlet 속성에 연결하면 터치 영역이 하위 클래스에 정의 된 영역에 맞게 확장됩니다.

CGRectInsetCGRectContainsPoint 메소드 함께 pointInside 메소드 를 대체하는 것 외에도 UIView 서브 클래스의 직사각형 터치 영역을 확장 하기 위해 CGGeometry 를 검사하는 데 시간이 걸립니다 . 당신은 또한에 CGGeometry의 사용 사례에 대한 몇 가지 좋은 팁 찾을 수 있습니다 NSHipster을 .

예를 들어 위에서 언급 한 방법을 사용하여 터치 영역을 불규칙하게 만들거나 가로 터치 영역보다 너비 터치 영역을 두 배 크게 만들도록 선택할 수 있습니다.

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

swizzling으로보다 일반적인 접근 방식을 사용하고 있습니다 -[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직접 유형으로 사용할 수는 없지만 CGRect4가있는 구조체로 구성되어 있기 때문에 CGFloat"Rect"를 선택하고 다음과 같은 값을 입력하면 완벽하게 작동합니다 {{top, left}, {bottom, right}}.


글쎄, UIButton을 투명하고 약간 더 큰 UIView 안에 넣은 다음 UIButton에서와 같이 UIView 인스턴스에서 터치 이벤트를 잡을 수 있습니다. 이렇게하면 여전히 단추가 있지만 더 큰 터치 영역이 있습니다. 사용자가 버튼 대신보기를 터치하면 버튼을 선택하고 강조 표시 한 상태를 수동으로 처리해야합니다.

다른 가능성은 UIButton 대신 UIImage를 사용하는 것입니다.


정보 버튼의 적중 영역을 프로그래밍 방식으로 늘릴 수있었습니다. "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에서는 정보 버튼의 적중 영역이 증가한 것으로 보입니다.


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 솔루션입니다 (이 블로그 게시물을 기반으로 : 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의 사용자 정의 적중 테스트는 UIButton의 하위 클래스로 구현되었습니다. Objective-C로 작성되었습니다.

for 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

상속 된 UIButton을 재정 의하여 구현합니다.

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

범주의 메소드를 대체하지 마십시오. 서브 클래스 버튼 및 재정의 - 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

코드를 작성하지 않고 서브 클래스를 작성하고 Interface Builder를 사용할 수도 있습니다. 여기에 이미지 설명을 입력하십시오


위의 giaset의 답변을 바탕으로 (가장 우아한 해결책을 찾았습니다) 다음은 신속한 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
    }
}

터치 영역을 확대하기 위해 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];
}

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

이처럼 사용할 수 있습니다

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

다른 참조에 대해서는 https://stackoverflow.com/a/13067285/1728552를 참조하십시오.


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

체이스의 응답을 따랐으며 버튼이 선택 해제 된 영역 (영역이 크지 않은 경우)보다 큰 영역을 너무 크게 만들면 UIControlEventTouchUpInside 이벤트에 대한 선택기를 호출하지 않습니다. .

나는 크기가 어떤 방향이나 그와 비슷한 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 이미지를로드하고 배경 이미지를 설정하고 있습니다. UIImage를 기반으로 프레임을 설정하고 망막의 배율을 50 %로 조정하고 있습니다. 좋아, 어쩌면 위의 내용에 동의하든 그렇지 않든 적중 영역을 더 크게 만들고 두통을 피하려면 :

내가하는 일, 포토샵에서 이미지를 열고 캔버스 크기를 120 %로 늘리고 저장하십시오. 사실상 투명 픽셀로 이미지를 더 크게 만들었습니다.

한 가지 접근법.


위의 @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를 재정의하는 클래스에이 메서드를 추가 할 수 있습니다. 이것은 잘 작동했습니다 :)


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

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


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

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

반응형