Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's up with the size of UIButton's Touch Drag/Exit hit area?

Well, I guess it is best to show what I mean:

Animation of a UIButton reacting to dragging motions

You can clearly see that once we've touched the button and moved out of it, a consequent move-in event triggers the button state change from far away.

While this behavior is natural for all UIButtons, I couldn't google a solution to alter it.

Is there a way to reduce the hit area for this type of UIButton sensitivity? I want it reduced, because I feel that the button is large enough as it is, and it will provide a better user experience along with up/down sound effects.

UPD: The following override code for UIButton was posted in another thread:

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    CGFloat boundsExtension = 25.0f;
    CGRect outerBounds = CGRectInset(self.bounds, -1 * boundsExtension, -1 * boundsExtension);

    BOOL touchOutside = !CGRectContainsPoint(outerBounds, [touch locationInView:self]);
    if(touchOutside)
    {
        BOOL previousTouchInside = CGRectContainsPoint(outerBounds, [touch previousLocationInView:self]);
        if(previousTouchInside)
        {
            NSLog(@"Sending UIControlEventTouchDragExit");
            [self sendActionsForControlEvents:UIControlEventTouchDragExit];
        }
        else
        {
            NSLog(@"Sending UIControlEventTouchDragOutside");
            [self sendActionsForControlEvents:UIControlEventTouchDragOutside];
        }
    }
    return [super continueTrackingWithTouch:touch withEvent:event];
}

It alters the hit area extension used by Drag In/Drag Out events, yet button Up/Down states switch exactly the same way as they did before.

like image 842
Kai Avatar asked Apr 30 '13 18:04

Kai


1 Answers

A Swift version:

  private let _boundsExtension: CGFloat = 0 // Adjust this as needed

  override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
    let outerBounds: CGRect = CGRectInset(bounds, -1 * _boundsExtension, -1 * _boundsExtension)
    let currentLocation: CGPoint = touch.locationInView(self)
    let previousLocation: CGPoint = touch.previousLocationInView(self)

    let touchOutside: Bool = !CGRectContainsPoint(outerBounds, currentLocation)
    if touchOutside {
      let previousTouchInside: Bool = CGRectContainsPoint(outerBounds, previousLocation)
      if previousTouchInside {
        sendActionsForControlEvents(.TouchDragExit)
      } else {
        sendActionsForControlEvents(.TouchDragOutside)
      }
    } else {
      let previousTouchOutside: Bool = !CGRectContainsPoint(outerBounds, previousLocation)
      if previousTouchOutside {
        sendActionsForControlEvents(.TouchDragEnter)
      } else {
        sendActionsForControlEvents(.TouchDragInside)
      }
    }

    return true
  }

  override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch: UITouch = touches.first!
    let outerBounds: CGRect = CGRectInset(bounds, -1 * _boundsExtension, -1 * _boundsExtension)
    let currentLocation: CGPoint = touch.locationInView(self)

    let touchInside: Bool = CGRectContainsPoint(outerBounds, currentLocation)
    if touchInside {
      return sendActionsForControlEvents(.TouchUpInside)
    } else {
      return sendActionsForControlEvents(.TouchUpOutside)
    }
  }
like image 122
mcm Avatar answered Sep 21 '22 15:09

mcm