2
votes

To sum up my question beforehand: I'm trying to determine where on the slider I can place the image based upon knowing only the UISlider's duration, and having an array of times to loop through, placing the images accordingly.

I've been reading through the Apple Docs on UISlider, and it appears that there is no native way to add "Tick marks" on a UISlider based upon an array of floats. "Tick marks" meaning lines upon a slider, such as those used to place advertisements on scrubbers. Here is a visualization: enter image description here

Now, I have an array full of floats; Floats in which I will use to drop the tick marks based upon the UISlider's value. The values of the floats in the array will be different every time. I would like to loop through the .value property of my UISlider, dropping the UIImages accordingly. The UIImage's are the tick marks that are just little png's assets I created. What I cannot figure out is the logic behind looping through the .value property of the UISlider and placing the UIImage in accordance with the UISlider's future position. The values of the floats in the array will be different every time, so I can't place them statically. Does anyone know where to start? I'm still a little new to Objective-C programming.

I know that it may be possible utilize retrieving the slider's beginning X coordinate on the screen, like so:

- (float)xPositionFromSliderValue:(UISlider *)aSlider;
{
    float sliderRange = aSlider.frame.size.width - aSlider.currentThumbImage.size.width;
    float sliderOrigin = aSlider.frame.origin.x + (aSlider.currentThumbImage.size.width / 2.0);

    float sliderValueToPixels = (((aSlider.value-aSlider.minimumValue)/(aSlider.maximumValue-aSlider.minimumValu‌​e)) * sliderRange) + sliderOrigin);

    return sliderValueToPixels;
}

Maybe I could add in a calculation in the for loop to place the image in accordance to that instead. I'm just not too sure where even to begin here...

2
UISliders do not have a duration property. Do you mean the value property? If so, please edit your question.user3821934
Yes I meant duration, my variable's name is duration. OP edited.Henry F
Also, the width of the slider has nothing to do with its minimum or maximum values. It is determined by the frame property of the slider. However, their is no way to get the exact positions of the start and end of the slider.user3821934
@MichaelL True, I updated OP with potential code that could be used in place of that.Henry F

2 Answers

3
votes

The methods trackRectForBounds and thumbRectForBounds are provided for subclassing UISlider, but you can call them directly, and they will get your tick centers up front.

- (float)sliderThumbCenter:(UISlider *)slider forValue:(float)value{
    CGRect trackRect = [slider trackRectForBounds:slider.bounds];
    CGRect thumbRect = [slider thumbRectForBounds:slider.bounds trackRect:trackRect value:value];
    CGFloat centerThumb = CGRectGetMidX(thumbRect);
    return centerThumb;
}

And it might be easier to do a custom view to draw the track rather than Image views, then just put the slider on top of it and hide the track. Just make the slider frame equal to the TickView's bounds. Really I suppose a UISlider subclass would be better, but this works!

@interface TickView : UIView
@property UIColor *tickColor;
@property int tickCount;
@property CGFloat tickHeight;
@property (weak) UISlider *slider;
@property float *ticks;
-(void)setTicks:(float *)ticks count:(int)tickCount;
@end


@implementation TickView{
    __weak UISlider *_slider;
}

-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.tickColor = [UIColor grayColor];
        self.backgroundColor = [UIColor clearColor];
        self.tickCount = 7;
        self.ticks = malloc(sizeof(float) * self.tickCount);
        self.tickHeight = 10;
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetStrokeColorWithColor(context, self.tickColor.CGColor);

    CGContextBeginPath(context);
    CGFloat centerY = rect.size.height / 2;
    CGContextMoveToPoint(context, 0, centerY);
    CGContextAddLineToPoint(context, rect.size.width, centerY);
    CGFloat tickTop = centerY - self.tickHeight / 2;
    CGFloat tickBottom = centerY + self.tickHeight / 2;
    CGFloat tickX = 0;

    if (self.slider) {
        for (int i = 0; i < self.tickCount; i++) {
            tickX = [self sliderThumbCenter:self.slider forValue:self.ticks[i]];
            CGContextMoveToPoint(context, tickX, tickTop);
            CGContextAddLineToPoint(context, tickX, tickBottom);
        }
    }
    else{
        CGFloat tickSpacing = rect.size.width / (self.tickCount - 1);
        for (int i = 0; i < self.tickCount; i++) {
            CGContextMoveToPoint(context, tickX, tickTop);
            CGContextAddLineToPoint(context, tickX, tickBottom);
            tickX += tickSpacing;
        }
    }
    CGContextStrokePath(context);
}

-(void)setTicks:(float *)ticks count:(int)tickCount{
    free(_ticks);
    _ticks = malloc(sizeof(float) * tickCount);
    memcpy(_ticks, ticks, sizeof(float) * tickCount);
    _tickCount = tickCount;
    [self setNeedsDisplay];
}
- (float)sliderThumbCenter:(UISlider *)slider forValue:(float)value{
    CGRect trackRect = [slider trackRectForBounds:slider.bounds];
    CGRect thumbRect = [slider thumbRectForBounds:slider.bounds trackRect:trackRect value:value];
    CGFloat centerThumb = CGRectGetMidX(thumbRect);
    return centerThumb;
}
-(void)setSlider:(UISlider *)slider{
    _slider = slider;
}
-(UISlider *)slider{
    return _slider;
}
-(void)dealloc{
    free(_ticks);
}
@end
0
votes

I think you will have trouble positioning the tick marks. However, if the parent view of your UISlider is "view", you add a subview like this:

[view addSubView:myTickView];

The position of the added subview is determined by its frame property, which is in the parent's view coordinate space. To remove a view, you do this:

[myTickView removeFromSuperView];

You can also loop through your tick views and change there frames, but these changes will be animated, so the ticks will appear to slide if you do that, unless you turn animations off.