Search code examples
iphoneobjective-cuislider

Custom UISlider: avoid updating when dragging outside


I'm quite new to iPhone development and I'm building my first app :) In one of my view controllers I built a customSlider that should behave as the native "slide to unlock" slider.

My doubt right now is how to implement the "drag outside" behaviour. As said, I would like to have it exactly as the native slider, that means that when the finger is dragging the slider if it moves out the slider, the slider should be going to zero.

My doubt is not on the animation part (I'm already using animation block successfully), but on control events part. Which control event should I use?

I'm using:

[customSlider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];

to handle the sliding part (finger sliding the cursor), and

[customSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventTouchUpInside];

to handle the release part, but the problem is that if I release the finger outside, the sliderAction function it's not called.

EDIT: I've tried to implement the solution @Bruno Domingues gave me, but I'm realizing that the issue is that by default UISlider keep getting updated even if the finger is dragged outside of it (try to open for example the Brightness section in System Preferences and you'll see that the slider will keep on updating even if you drag outside of it). So my question could be redefined: How to avoid this default behaviour and have the slider updating only when the finger is moving on it?


Solution

  • Simply interrupt the touches methods in your custom subclass and only forward the touches you want acted on to the superclass, like so:

    in .h:

    @interface CustomSlider : UISlider
    @end
    

    in .m:

    #import "CustomSlider.h"
    @implementation CustomSlider
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        CGPoint touchLocation = [[touches anyObject] locationInView:self];
        if (touchLocation.x < 0 || touchLocation.y<0)return;
        if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
        [super touchesBegan:touches withEvent:event];
    }
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
        CGPoint touchLocation = [[touches anyObject] locationInView:self];
        if (touchLocation.x < 0 || touchLocation.y<0)return;
        if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
        [super touchesMoved:touches withEvent:event];
    }
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
        CGPoint touchLocation = [[touches anyObject] locationInView:self];
        if (touchLocation.x < 0 || touchLocation.y<0)return;
        if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
        [super touchesEnded:touches withEvent:event];
    }
    -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
        CGPoint touchLocation = [[touches anyObject] locationInView:self];
        if (touchLocation.x < 0 || touchLocation.y<0)return;
        if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
        [super touchesCancelled:touches withEvent:event];
    }
    @end
    

    Please note that this implementation will start updating the control if your finger moves back to the control. To eliminate this, simply set a flag if a touch is received outside of the view then check that flag in subsequent touches methods.