Question

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.

Was it helpful?

Solution

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];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top