Tengo una pregunta lidiar con UIButton y su zona activa. Yo estoy usando la Información Oscuro botón en el interface builder, pero me estoy encontrando que el área afectada no es lo suficientemente grande para algunas personas los dedos.

Hay una manera de aumentar el área del golpe de botón, ya sea de programación o en Interface Builder, sin cambiar el tamaño de la InfoButton gráfico?

  • en algunos de los diseños: tener cuidado de que no estás accidentalmente de pasar A LA SIGUIENTE CELDA: esta puede ser una molestia cuando usted tiene un botón en el decir de fondo de una celda; muy a menudo el tamaño del botón, se pasa al siguiente frickin’ de la célula.
  • Si no hay nada de trabajo, sólo tiene que añadir un UIButton sin una imagen y, a continuación, poner un ImageView superposición !

34 Comentarios

  1. 138

    Ya que estoy usando una imagen de fondo, ninguna de estas soluciones funcionado bien para mí. Aquí es una solución que tenga un poco de diversión objective-c magia y ofrece una gota en solución con un mínimo de código.

    Primero, agregar una categoría UIButton que anula el éxito de la prueba y también se agrega una propiedad para la ampliación de la prueba visitas marco.

    UIButton+Extensiones.h

    @interface UIButton (Extensions)
    
    @property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
    
    @end

    UIButton+Extensiones.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

    Una vez que esta clase se agrega, todo lo que necesita hacer es ajustar el borde de los márgenes de su botón. Tenga en cuenta que elegí para agregar los recuadros así que si usted desea hacer el golpe área más grande, usted debe utilizar los números negativos.

    [button setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)];

    Nota: Recuerde importar la categoría (#import "UIButton+Extensions.h") en sus clases.

    • Reemplazar un método en una categoría es una mala idea. Lo que si otra categoría también intenta reemplazar pointInside:withEvent:? Y la llamada a [super pointInside:withEvent] no llamar UIButton la versión de la llamada (si tiene uno), pero UIButton padres de la versión.
    • Tienes razón y Apple no recomienda hacer esto. Dicho esto, siempre y cuando este código no es una biblioteca compartida y sólo local para su aplicación, usted debería estar bien.
    • Hola Chase, ¿hay alguna desventaja de la utilización de una subclase en su lugar?
    • subclases UIButton es complicado, porque el inicializador es un método estático. La adición de métodos con una categoría significa que usted no tiene que utilizar explícitamente la clase personalizada que puede ser conveniente.
    • Estás haciendo mucho trabajo innecesario, se pueden hacer dos simples líneas de código: [[backButton imageView] setContentMode: UIViewContentModeCenter]; [backButton setImage:[UIImage imageNamed:@"arrow.png"] forState:UIControlStateNormal]; Y acaba de establecer el ancho de la & altura de lo que usted necesita:` backButton.frame = CGRectMake(5, 28, 45, 30);`
    • Me gusta este enfoque, funciona como un encanto para mí. Gracias
    • él está usando la imagen de fondo, lo que significa que su enfoque no funciona.
    • Mientras que su reclamo de que el código no está en una biblioteca compartida, en teoría, es cierto, si Apple un día decide implementar su código en una categoría, aún no hay garantía de que uno de vosotros es el que gana. El uso de categorías para sustituir cosas es un malo de estilo y b) no es confiable. Y esto es exactamente lo que las subclases son para. Cambie el uso de una subclase.
    • No estoy en desacuerdo. Este tipo de monkey patching no es lo ideal. No tengo una subclase de la solución por desgracia. Siéntase libre de proporcionar uno.
    • No me di cuenta de lo genial de una solución a esto fue hasta que me sumergí en el código de más. Entonces, ya no era esto habla de la creación de subclases, traté de mi mano en una subclase de la implementación stackoverflow.com/a/37866101/4403600.
    • Usted salvó mi día! Tan elegante! Gracias!

  2. 75

    Acaba de establecer la imagen de borde de los valores de margen en el interface builder.

    • No funciona para backgroundImage
    • Pensé que esto no iba a funcionar para mí, porque las imágenes de fondo no responden a las inserciones, pero he cambiado a la configuración de la imagen de la propiedad y, a continuación, establece un negativo a la izquierda del borde del recuadro de mi título de la anchura de la imagen y estaba de nuevo en la parte superior de mi imagen.
    • Esto también puede ser hecho en el código. Por ejemplo, button.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10);
    • Dave Ross respuesta es genial. Para aquellos que no pueden el de la imagen, la imagen de los golpes de su título más a la derecha de la misma (al parecer), por lo que usted puede hacer clic en el pequeño botón en IB para mover de nuevo (o en el código, como se ha expuesto). Lo único negativo que veo es que no puedo usar el elástico de las imágenes. Un pequeño precio a pagar IMO
    • cómo hacerlo sin tener que estirar la imagen?
    • Establecer el modo de contenido a algo así como el Centro y no se nada de Escala.
    • gracias. funcionaba perfecto y super fácil
    • Actualización de Xcode 8.1. Hay un error en la previsualización en los márgenes cuando el uso de Guión gráfico. stackoverflow.com/a/40570294/1363634

  3. 61

    Aquí una elegante solución con el uso de Extensiones en Swift. Les da a todos los UIButtons un área activa de, al menos, 44×44 puntos, como por Apple Human Interface Guidelines (https://developer.apple.com/ios/human-interface-guidelines/visual-design/layout/)

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

    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
        }
    }
    • Usted necesita para poner a prueba si el botón está actualmente oculto. Si es así, devuelve nil.
    • Gracias, se ve muy bien. Cualquier posibles inconvenientes a tener en cuenta?
    • Me gusta esta solución, no requiere de mí para configurar propiedades adicionales en todos los botones. Gracias
    • La desventaja es que si el UIButton está oculto, este seguirá devolviendo true! Hay que ver si el botón está oculto o no.
    • Si Apple directrices recomiendan estas dimensiones para el área del golpe, por qué nos tenemos que fijar en su fracaso a la hora de implementar? Está dirigida en la tarde del SDK?
    • Gracias a dios tenemos Desbordamiento de la Pila, y sus colaboradores. Porque ciertamente no podemos confiar en la Manzana útiles, información oportuna y precisa sobre cómo solucionar sus más comunes, los problemas básicos.
    • Muy bonito solución!
    • No estoy seguro de reemplazar las funciones de extensión es una buena idea…
    • Yo sólo soy la adición de esta subclase de UIButton y utilizarla. gracias!

  4. 46

    También podría subclase UIButton o una costumbre UIView y reemplazar point(inside:with:) con algo como:

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

    Objective-C

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
        CGFloat margin = 5.0;
        CGRect area = CGRectInset(self.bounds, -margin, -margin);
        return CGRectContainsPoint(area, point);
    }
    • gracias genial solución sin ningún tipo de categorías adicionales. También me sencilla de integrar con los botones de mi que tengo en XIB y funciona bien. gran respuesta
    • Esta es una gran solución que puede ser extendida a todos los otros controles! He encontrado que es útil para crear subclases de una UISearchBar por ejemplo. El pointInside método en cada una de UIView desde iOS 2.0. BTW – Swift: func pointInside(_ point: CGPoint, withEvent evento: UIEvent!) -> Bool.
    • Gracias por esto! Como dijeron otros, esto puede ser realmente útil con otros controles!
    • Esto es genial!! Gracias, usted me salvó un montón de tiempo! 🙂
  5. 33

    Aquí Chase UIButton+Extensiones en 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)
        }
    }

    Para usarlo, usted puede:

    button.touchAreaEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
    • Buen trabajo. Me pareció muy útil.
    • En realidad, debemos extender UIControl, no UIButton.
    • Y la variable pTouchAreaEdgeInsets debe ser Int, no UIEdgeInsets. (En realidad, puede ser de cualquier tipo, pero Int es el más genérico y simple, por lo que es común en este tipo de construcción). (Por eso me encanta este enfoque en general. Gracias, dcRay!)
    • Intenté titleEdgeInsets, imageEdgeInsets, contentEdgeInset todos no trabajo para mí esta extensión está funcionando bien. Gracias por publicar esto !! Gran trabajo !!
  6. 19

    No establece la backgroundImage propiedad con su imagen, establecer la imageView de la propiedad. También, asegúrese de que usted tiene imageView.contentMode establecido en UIViewContentModeCenter.

    • Más específicamente, ajuste el botón del contentMode propiedad UIViewContentModeCenter. A continuación, puede hacer que el botón del marco más grande que la imagen. A mí me funciona!
    • FYI, UIButton no respeta UIViewContentMode: stackoverflow.com/a/4503920/361247
    • lo siento, se me han aclarado lo Api de que estaba hablando. El imageView.contentMode se puede establecer como UIContentModeCenter.
    • Esto funciona perfectamente, gracias por los consejos! [[backButton imageView] setContentMode: UIViewContentModeCenter]; [backButton setImage:[UIImage imageNamed:@"image.png"] forState:UIControlStateNormal];
    • El mencionado comentario no funciona para mí :/ La imagen es posible cambiar el tamaño a las más grandes que el tamaño del marco.
    • ok, voy a tratar de dar más detalles, funciona cuando se establece más Ancho & Altura en CGRectMake, así que haga su botón un poco más grande, te recomiendo que lo hagas con imagen personalizada, aquí está mi ejemplo de trabajo: ` UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; backButton.frame = CGRectMake(100, 100, 45, 35); [[backButton imageView] setContentMode: UIViewContentModeCenter]; [backButton setImage:[UIImage imageNamed:@»de la flecha.png»] forState:UIControlStateNormal]; // «arror.png» tamaño es 19×34 [auto.ver addSubview:backButton]; //el área afectada es 45×35`
    • He intentado hacer lo mismo, pero no podía llegar a trabajar. Finalmente anuló UIButton y se ha cambiado bajo el método hitTest. Su trabajo está bien ahora.

  7. 18

    Recomiendo la colocación de un UIButton con el tipo Personalizado centrado sobre el botón info. Cambiar el tamaño del botón personalizado para el tamaño que desea que el área afectada sea. A partir de ahí tienes dos opciones:

    1. Marcar la casilla ‘Mostrar toque en resaltar la opción del botón personalizado. El brillo blanco aparecerá sobre el botón info, pero en la mayoría de los casos los usuarios dedo cubre este y todo lo que se ve es el resplandor alrededor de la parte exterior.

    2. Establecer un IBOutlet para el botón info y dos IBActions para el botón personalizar uno para ‘Touch Down’ y una para el «Touch Up Inside’. A continuación, en Xcode hacer el touchdown caso establezca la propiedad que haya resaltado el botón info para SÍ y la touchupinside caso establezca la propiedad resaltada a NO.

  8. 13

    Mi solución en 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)
        }
    }
  9. 12

    No hay nada de malo con las respuestas presentadas; sin embargo, yo quería extender jlarjlar la respuesta como se tiene increíble potencial que puede agregar valor para el mismo problema con otros controles (por ejemplo, barra de búsquedas). Esto es así porque desde pointInside está conectado a una UIView, uno es capaz de subclase cualquier control para mejorar el área táctil. Esta respuesta también se muestra una completa muestra de cómo implementar la solución completa.

    Crear una nueva subclase para el botón (o control)

    #import <UIKit/UIKit.h>
    
    @interface MNGButton : UIButton
    
    @end

    Siguiente anular la pointInside método en la subclase aplicación

    @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

    En el guión gráfico/xib archivo seleccione el control en cuestión y abrir la identidad del inspector y escriba el nombre de la clase personalizada.

    UIButton: Hacer que el área del golpe mayor que el valor predeterminado área afectada

    En su UIViewController de la clase de escena que contiene el botón, cambiar el tipo de clase para el botón con el nombre de la subclase.

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

    Link de su guión gráfico/xib botón a la propiedad IBOutlet y su área táctil será ampliado para adaptarse a la zona definida en la subclase.

    Además a la sustitución de la pointInside método junto con el CGRectInset y CGRectContainsPoint métodos, uno debe tomar tiempo para examinar la CGGeometry para la ampliación de las rectangular de área táctil de cualquier UIView subclase. Usted también puede encontrar algunos buenos consejos sobre CGGeometry casos de uso en NSHipster.

    Por ejemplo, uno podría hacer que el área táctil irregulares usando los métodos mencionados anteriormente, o simplemente elegir para hacer que el ancho del área táctil de dos veces tan grande como la horizontal de la superficie táctil:

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

    NB: la Sustitución de cualquier interfaz de usuario de control de la Clase debe producir resultados similares en la ampliación de la superficie táctil para los diferentes controles (o cualquier UIView subclase, como UIImageView, etc).

    • Esto es realmente genial. Yo no tenía idea que se podía hacer eso. Gran solución!!!
    • genial!!!!!!!
  10. 10

    Esto funciona para mí:

    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)];
    • Esto funciona, pero tenga cuidado si usted necesita para crear una imagen y un título de un botón. En ese caso, usted tendrá que utilizar setBackgoundImage que la escala de la imagen en el nuevo marco del botón.
  11. 7

    No cambiar el comportamiento de 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
    • Si el botón de 40×40, este método no será llamado si los números negativos son mayores – como alguien ha mencionado. De lo contrario, esto funciona.
    • Esto no funciona para mí. hitFrame aparece calculado correctamente, pero pointInside: nunca es llamado cuando el punto está fuera de sí.límites. si (CGRectContainsPoint(hitFrame, punto) && !CGRectContainsPoint(auto.los límites, de punto)){ NSLog(@»el Éxito!»); // Nunca llamó }
  12. 7

    Estoy usando un enfoque más genérico por swizzling -[UIView pointInside:withEvent:]. Esto me permite modificar golpe de pruebas de comportamiento en cualquier UIView, no sólo UIButton.

    A menudo, un botón que se coloca dentro de un recipiente de vista que también limita el éxito de la prueba. Por ejemplo, cuando un botón está en la parte superior de un contenedor de vista y desea extender el toque de diana hacia arriba, usted también tiene que extender el toque de destino del contenedor de vista.

    @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

    La cosa buena acerca de este enfoque se puede utilizar incluso en los Storyboards mediante la adición de un Usuario Define el tiempo de ejecución de Atributo. Lamentablemente, UIEdgeInsets no está directamente disponible como un tipo ahí, pero desde CGRect también consta de una estructura con cuatro CGFloat funciona a la perfección por la elección de «Rect» y rellenando los valores como: {{top, left}, {bottom, right}}.

  13. 5

    Bien, usted puede colocar su UIButton dentro de una transparente y ligeramente más grande UIView, y luego coger los eventos de toque en el UIView instancia como en la UIButton. De esa manera, usted todavía va a tener su botón, pero con una mayor área táctil. De forma manual, tienen que lidiar con el seleccionado & destacó los estados con el botón si el usuario toca la vista en lugar del botón.

    Otra posibilidad consiste en utilizar una UIImage en lugar de un UIButton.

  14. 5

    He sido capaz de aumentar el área activa del botón de información mediante programación. El «yo» gráfico no cambiar de escala y permanece centrada en el botón nuevo marco.

    El tamaño del botón de información parece ser fijado a 18×19[*] en el Interface Builder. Mediante la conexión de un IBOutlet, yo era capaz de cambiar su tamaño de fotograma en el código sin problemas.

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

    [*]: En las últimas versiones de iOS parecen haber incrementado el área activa del botón info.

  15. 5

    Estoy usando la siguiente clase en Swift, habilitar también un Generador de Interfaz de propiedad para ajustar el margen:

    @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)
    }
    }
    • cómo cambiar manualmente este pointInside? Si he creado un UIButton con viejos margen, pero ahora quiero cambiar?
  16. 5

    Este es mi Swift 3 Solución(basado en este blogpost: 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
    }
    }
  17. 3

    Persecución de la costumbre de golpear pruebas llevadas a cabo como una subclase de UIButton. Escrito en Objective-C.

    Parece que funciona tanto para init y buttonWithType: constructores. Para mis necesidades es perfecto, pero desde la creación de subclases UIButton puede ser peludo, me interesaría saber si alguien tiene un fallo con ella.

    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. 3

    La aplicación a través de anular heredado 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)
    }
    }
  19. 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
  20. 2

    Nunca reemplazar el método en la categoría. Botón de la subclase y reemplazar - pointInside:withEvent:. Por ejemplo, si el botón del lado es menor que 44 px (se recomienda como mínimo para tocar área) uso este:

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

    Hice un biblioteca para este mismo propósito.

    Usted puede elegir el uso de UIView categoría, no subclases requiere:

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

    O puede subclase su UIView o UIButton y establecer el minimumHitTestWidth y/o minimumHitTestHeight. Su botón hit-área de prueba va a ser representado por esos 2 valores.

    Al igual que otras soluciones, se utiliza el - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event método. El método es llamado cuando iOS realiza pruebas de posicionamiento. Este blog tiene una buena descripción de cómo iOS hit-prueba de obras.

    https://github.com/kgaidis/KGHitTestingViews

    @interface KGHitTestingButton : UIButton <KGHitTesting>
    @property (nonatomic) CGFloat minimumHitTestHeight; 
    @property (nonatomic) CGFloat minimumHitTestWidth;
    @end

    También puede subclase y utilizar el Constructor de interfaces sin necesidad de escribir ningún código:
    UIButton: Hacer que el área del golpe mayor que el valor predeterminado área afectada

    • Terminé la instalación de este solo para el bien de velocidad. La adición de IBInspectable fue una gran idea, gracias por poner esto juntos.
  22. 2

    Base en giaset la respuesta de la anterior (la que he encontrado la solución más elegante), aquí es el swift 3 versión:

    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
    }
    }
  23. 1

    Estoy de utilizar este truco para botón dentro de tableviewcell.accessoryView para ampliar su área táctil

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

    Yo solo hice el puerto de la @Chase solución en 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)
    }
    }

    una se puede utilizar como esto

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

    Para cualquier otra referencia ver https://stackoverflow.com/a/13067285/1728552

  25. 1

    @antoine respuesta de formato para el 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 );
    }
    }
  26. 0

    He seguido Chase respuesta y funciona muy bien, un solo problema al crear el conjunto demasiado grande, más grande que la zona donde el botón se pone la selección (si la zona no era más grande) no llama el selector para el UIControlEventTouchUpInside evento.

    Creo que el tamaño es de más de 200 en cualquier dirección o algo así.

  27. 0

    Soy tan tarde a este juego, pero quería opinar sobre una técnica simple que podría resolver sus problemas. Aquí es un típico programática UIButton fragmento para mí:

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

    Estoy carga de una Imagen png transparente para mi botón y ajuste de la imagen de fondo. Estoy configurando el marco basado en la UIImage y la escala en un 50% para la retina. OK, tal vez usted está de acuerdo con lo anterior o no, PERO si quieres hacer que el área del golpe más GRANDE y se ahorrará un dolor de cabeza:

    Lo que quiero hacer es abrir la imagen en photoshop y simplemente aumentar el tamaño del lienzo a 120% y guardar. Efectivamente usted acaba de hacer la imagen más grande con píxeles transparentes.

    Sólo una aproximación.

  28. 0

    @jlajlar ‘s respuesta anterior parecía buena y sencilla, pero no coincide con Xamarin.iOS, así que me la ha convertido en Xamarin.
    Si usted está buscando una solución en un Xamarin iOS, hay aquí va:

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

    Usted puede agregar este método a la clase donde se están anulando UIView o UIImageView. Esto funcionó muy bien 🙂

  29. 0

    Ninguna de las respuestas que funciona perfecto para mí, porque yo uso la imagen de fondo y un título en ese botón. Por otra parte, el botón cambiará de tamaño como cambios de tamaño de pantalla.

    Lugar, puedo ampliar el área de rozamiento, haciendo que el png transparente área más grande.

  30. 0

    Esta Swift versión permite definir un mínimo de aciertos de tamaño para todos UIButtons. Lo más importante es que también se encarga del caso cuando UIButtons están ocultos, que muchas de las respuestas negligencia.

    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
    }
    }
  31. 0

    Similar a Zhanserik, con variable extensión y actualizado para 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)
    }
    }
  32. -1

    Swift:

    override func viewWillAppear(animated: Bool) {
    self.sampleButton.frame = CGRectInset(self.sampleButton.frame, -10, -10);
    }
  33. -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)
    }
  34. -1
    • Sin primordial en categorías o extensiones de
    • 44×44 mínimo de acuerdo a las pautas

    Mi opinión:

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

Dejar respuesta

Please enter your comment!
Please enter your name here