Vern Jensen
Vern Jensen

Reputation: 3570

Can I 'flush' UIGestureRecognizer events?

In iOS 7, Apple seems to have changed the way the gesture recognizers behave. Consider UIPinchGestureRecognizer as an example. If I do a slow redrawing operation in UIGestureRecognizerStateChanged, this used to work fine under old versions of iOS, but in newer versions, my redrawing typically doesn't get rendered to the screen before the pinch gesture is called again with another StateChanged update, and the slow drawing operation is invoked again. This happens repeatedly many times before the system actually updates the visible portion of the screen with my changes to the views.

I've found one solution is to call:

[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];

whenever I get a UIGestureRecognizerStateChanged event. This way drawing gets rendered on the screen, each time it's done. But there is still an issue of "event lag" where a series of pinch events gets queued up, such that the images keep scaling in size even long after I've stopped pinching the screen.

My question is if there's a way to "flush" the queued up pinch events, so whenever I get a UIGestureRecognizerStateChanged event, I can do my slow drawing operation, then flush all other pinch events, so only the most recent one gets processed. Anyone know if this is possible? I guess I could build a system that looks at the time of a UIGestureRecognizerStateChanged event, and throws out events too close to the most recent redraw, but that seems like a hack.

- (void) handleGlobalPinchGesture:(UIPinchGestureRecognizer*)_pinchGesture
{
    if (  _pinchGesture.state == UIGestureRecognizerStateBegan )
    {
       // stuff
    return;
    }

    if ( _pinchGesture.state == UIGestureRecognizerStateEnded || _pinchGesture.state == UIGestureRecognizerStateCancelled )
    {
       // end stuff
        return;
    }

    if (_pinchGesture.state == UIGestureRecognizerStateChanged )
    {
    doSlowRedrawingOperationHere();
    }
}

Upvotes: 4

Views: 209

Answers (2)

Andrew Romanov
Andrew Romanov

Reputation: 5076

I do not think, that it is the gesture recogniser's problem, I've had same problem with moving a transformed view. And I've solved it, by add to view drawRect method, and call -(void)setNeedsDisplay method before change the centre of the view: In view:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
}

In a gesture recogniser's action:

[CATransaction begin];
[CATransaction setValue:@(YES) forKey:kCATransactionDisableActions];

_destinationIndicatorView.center = center;
[self.frameView setNeedsDisplay];
self.frameView.center = center;

[CATransaction commit];

It works for me.

Upvotes: 1

Vern Jensen
Vern Jensen

Reputation: 3570

I never did find a way to 'flush' these events, but I did find a 'hack' that ensures every render is reflected on-screen, so the user sees your gesture actions in real-time, even if such redrawing operations are slow. My solution is to call:

[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];

to 'give the OS time' to do the redrawing on-screen. I do this from within my gesture recognizer callback, and only if running on iOS 7 or later.

The above call must be added to the callback for all your gesture recognizers (after the new content is rendered). Hope this helps someone!

Too bad that this 'hack' currently seems to be required in iOS 7 when you have slow rendering that takes place as a direct response to a gesture, if you want a good user experience.

Upvotes: 0

Related Questions