UIButton: увеличение области попадания по умолчанию - PullRequest
183 голосов
/ 30 апреля 2009

У меня есть вопрос, касающийся UIButton и его области попадания. Я использую кнопку Info Dark в конструкторе интерфейсов, но обнаружил, что область попадания недостаточно велика для пальцев некоторых людей.

Есть ли способ увеличить область нажатия кнопки программно или в Интерфейсном Разработчике без изменения размера графического элемента InfoButton?

Ответы [ 34 ]

2 голосов
/ 17 мая 2015

Я сделал библиотеку для этой цели.

Вы можете использовать категорию 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 без написания какого-либо кода: enter image description here

2 голосов
/ 06 июня 2014

Никогда не отменять метод в категории. Кнопка подкласса и переопределение - 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));
}
1 голос
/ 08 августа 2012

Я использую этот трюк для кнопки внутри 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];
}
1 голос
/ 19 марта 2019

@ ответ Антуана в формате Swift 4

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 голос
/ 03 августа 2016

Я только что сделал порт решения @Chase в 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

0 голосов
/ 20 марта 2015

@ ответ 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 голосов
/ 22 февраля 2019

По аналогии с Жансериком, с расширением переменной и обновленным для 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 голосов
/ 12 апреля 2013

Я следил за реакцией Чейза, и она отлично работает, одна проблема, когда вы создаете слишком большую область, большую, чем зона, в которой кнопка отменяется (если зона не была больше), она не вызывает селектор для событие UIControlEventTouchUpInside.

Я думаю, что размер больше 200 в любом направлении или что-то в этом роде.

0 голосов
/ 19 июля 2017
  • Без переопределения в категориях или расширениях
  • 44x44 минимум за рекомендации

Мой дубль:

open class MinimumTouchAreaButton: UIButton {
    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard !self.isHidden, self.isUserInteractionEnabled, self.alpha > 0 else { return nil }
        let expandedBounds = bounds.insetBy(dx: min(bounds.width - 44, 0), dy: min(bounds.height - 44, 0))
        return expandedBounds.contains(point) ? self : nil
    }
} 
0 голосов
/ 24 мая 2017
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)
}
...