I have a 2-d button array in my main view and want to be able to move them by dragging inside this view. The buttons are pretty narrow, around 23x23 pixels. I have added targets for control events touchDragExit
and touchDragOutside
but they were fired many pixels after I moved my finger outside the button. Hence I subclassed UIButton
and overrode continueTrackingWithTouch
with some code I found in SO. Notice I don't call the superclass implementation.
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
CGFloat boundsExtension = 5.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];
}
else
{
[self sendActionsForControlEvents:UIControlEventTouchDragOutside];
}
}
return self.tracking;
}
Both events are fired correctly but my UIControlEventTouchDragOutside
action receives wrong touch info when its action is called. Here is the first part of my implementation:
-(void) dragOutside:(UIButton *) sender withEvents:(UIEvent *) event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint relativeLocToInitialCell = [touch locationInView:sender];
NSLog(@"Translation %f %f NumTouch %d",relativeLocToInitialCell.x, relativeLocToInitialCell.y, [[event allTouches] count]);
...
}
[[event allTouches] count]
still returns 0 until I move my finger much away from the button. Then of course relativeLocToInitialCell
fields x
and y
are both 0, which are the values I use for later computations. Any idea what might be the problem?
EDIT: A deleted answer suggested using UIPanGestureRecognizer
instead of touchdrag events, but I couldn't use it because it was firing too rapidly before handling one event I got another one.
I ended up having to handle also UIControlEventTouchDragInside
event. Though this looks a workaround, thinking on the fundamentals of these touch drag events makes it a reasonable solution. All drag events are sourced from touchesBegan, touchesMoved and touchesEnded
. Apple added some threshold before UIControlEventTouchDragOutside
is sent, but up until that threshold UIControlEventTouchDragInside
event is certainly sent.
My solution was to remove overridden continueTrackingWithTouch
method and go on with its default implementation. Then I handled both inside and outside events, and moved my initialization code from exit event to inside event, and added control statements in inside event. Abstracted code looks like below:
-(void) dragInside:(UIButton *) sender withEvents:(UIEvent *) event
{
if(stillInsideOriginalControl) return;
UITouch *touch = [[event allTouches] anyObject];
CGPoint relativeLocToInitialCell = [touch locationInView:sender];
[self processTouch];
}
-(void) dragOutside:(UIButton *) sender withEvents:(UIEvent *) event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint relativeLocToInitialCell = [touch locationInView:sender];
[self processTouch];
}