4

Can i realise a horizontal slider with tick marks on iOS? There is not such option like in MacOS.

Eugene Trapeznikov
  • 3,220
  • 6
  • 47
  • 74

3 Answers3

3

I actually wrote a tutorial on how to do this on my company's website:

http://fragmentlabs.com/blog/making-talking2trees-part-3-77

The quick answer for this is a custom method I wrote, and which is explained in the article above:

-(UIView*)tickMarksViewForSlider:(UISlider*)slider View:(UIView *)view
{
// set up vars
int ticksDivider = (slider.maximumValue > 10) ? 10 : 1;
int ticks = (int) slider.maximumValue / ticksDivider;
int sliderWidth = 364;
float offsetOffset = (ticks < 10) ? 1.7 : 1.1;
offsetOffset = (ticks > 10) ? 0 : offsetOffset;
float offset = sliderWidth / ticks - offsetOffset;
float xPos = 0;

// initialize view to return
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y+1,
    slider.frame.size.width, slider.frame.size.height);
view.backgroundColor = [UIColor clearColor];

// make a UIImageView with tick for each tick in the slider
for (int i=0; i < ticks; i++)
{
    if (i == 0) {
        xPos += offset+5.25;
    }
    else
    {
        UIView *tick = [[UIView alloc] initWithFrame:CGRectMake(xPos, 3, 2, 16)];
        tick.backgroundColor = [UIColor colorWithWhite:0.7 alpha:1];
        tick.layer.shadowColor = [[UIColor whiteColor] CGColor];
        tick.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
        tick.layer.shadowOpacity = 1.0f;
        tick.layer.shadowRadius = 0.0f;
        [view insertSubview:tick belowSubview:slider];
        xPos += offset - 0.4;
    }
}

// return the view
return view;
}

You'd basically apply this method to a view that sits behind your UISlider, and it creates the tick marks at the appropriate intervals. You can change how many ticks are visible by modifying the ticksDivider variable. The above example creates one per slider value unless the slider's maximumValue property is greater than 10 (my sliders had values of 5, 40 and 120), in which case I divided the value by 10.

You have to play with the offsetOffset value and the float value after xPos += offset+... to account for the width of your thumb image and slider cap insets (so the thumb button appears to be centered over each).

ctlockey
  • 333
  • 3
  • 12
  • The great thing about this response is it actually proposes a replacement. Unfortunately the thumb-tick alignment is imperfect at best. The fact that one has to fiddle with parameters (of questionable provenance) to get the alignment even close is *ad hoc* and unscientific (esp. with the discrepancy with [your code](http://fragmentlabs.com/files/blogs/CustomSliderDemo_Part3.zip): 2.7/1.7). After all, this *should* be **geometry** we're talking about! Need to look into basing it off of real parameters, say, from `[slider thumbRectForBounds:trackRect:value:]`. – JohnK May 05 '13 at 00:11
2

While ctlockey's solution certainly works, I wanted something simpler but also that allowed the thumb to snap to whichever tick mark it is closest to when the user releases it.

My solution is below. It is a subclass of UISlider that only overrides two methods. It is bare bones but would be a foundation for a more complex version.

-(void)endTrackingWithTouch:(UITouch *)touch
                  withEvent:(UIEvent *)event
{
    [super endTrackingWithTouch:touch withEvent:event];
    if (self.value != floorf(self.value))
    {
        CGFloat roundedFloat = (float)roundf(self.value);
        [self setValue:roundedFloat animated:YES];
    }
}

-(void)drawRect:(CGRect)rect
{    
    CGFloat thumbOffset = self.currentThumbImage.size.width / 2.0f;
    NSInteger numberOfTicksToDraw = roundf(self.maximumValue - self.minimumValue) + 1;
    CGFloat distMinTickToMax = self.frame.size.width - (2 * thumbOffset);
    CGFloat distBetweenTicks = distMinTickToMax / ((float)numberOfTicksToDraw - 1);

    CGFloat xTickMarkPosition = thumbOffset; //will change as tick marks are drawn across slider
    CGFloat yTickMarkStartingPosition = self.frame.size.height / 2; //will not change
    CGFloat yTickMarkEndingPosition = self.frame.size.height; //will not change
    UIBezierPath *tickPath = [UIBezierPath bezierPath];
    for (int i = 0; i < numberOfTicksToDraw; i++)
    {
        [tickPath moveToPoint:CGPointMake(xTickMarkPosition, yTickMarkStartingPosition)];
        [tickPath addLineToPoint:CGPointMake(xTickMarkPosition, yTickMarkEndingPosition)];
        xTickMarkPosition += distBetweenTicks;
    }
    [tickPath stroke];
}
paulmrest
  • 414
  • 4
  • 14
  • 1
    The above implementation works fine in iOS 6, but in iOS 7+ the value of self.currentThumImage.size.width is no longer accessible with the default thumb image. The solution is that you have to set a custom image for the thumb if you want to access the image's size. See: http://stackoverflow.com/questions/21313796/how-to-read-size-of-uislider-thumb-image – paulmrest Aug 02 '15 at 19:45
2

From @CarlGoldsmith - You'd have to program that yourself; UISliders on iOS don't have that functionality.

Eugene Trapeznikov
  • 3,220
  • 6
  • 47
  • 74