9

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.

4

2 回答 2

2

斯威夫特版本:

  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)
    }
  }
于 2016-07-12T17:53:15.400 回答
1

我不知道您是否仍然遇到同样的问题,但我能够通过在 touchesEnded:withEvent: 方法中使用类似的代码来修复它。

我还更改了该方法以添加 touchEnter 和 dragInside,因为在当前代码中,那些事件仍然使用相同的边界。此外,我使每个案例都返回 YES 以便不调用 super (这会导致内部的触摸拖动被过早调用)。

这是我最终得到的最终代码,有两种方法:

- (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) {
            [self sendActionsForControlEvents:UIControlEventTouchDragExit];
            return YES;
        }
        else
        {
            [self sendActionsForControlEvents:UIControlEventTouchDragOutside];
            return YES;
        }
    }
    else {
        BOOL previousTouchOutside = !CGRectContainsPoint(outerBounds, [touch previousLocationInView:self]);
        if (previousTouchOutside) {
            [self sendActionsForControlEvents:UIControlEventTouchDragEnter];
            return YES;
        }
        else {
            [self sendActionsForControlEvents:UIControlEventTouchDragInside];
            return YES;
        }
    }
    return [super continueTrackingWithTouch:touch withEvent:event];
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGFloat boundsExtension = 25.0f;
    CGRect outerBounds = CGRectInset(self.bounds, -1 * boundsExtension, -1 * boundsExtension);

    BOOL touchInside = CGRectContainsPoint(outerBounds, [touch locationInView:self]);
    if (touchInside) {
        return [self sendActionsForControlEvents:UIControlEventTouchUpInside];
    }
    else {
        return [self sendActionsForControlEvents:UIControlEventTouchUpOutside];
    }
    return [super endTrackingWithTouch:touch withEvent:event];
}

注意:没有必要在最后返回方法的 super,但为了完整起见,我把它留在了那里。

于 2013-07-24T20:28:59.447 回答