I am implementing a UISlider a user can manipulate to set a distance. I have never used the CocoaTouch UISlider, but have used other frameworks sliders, usually there is a variable for setting the "step" and other "helper" properties.
The documentation for the UISlider deals only with a max and min value, and the output is always a 6 decimal float with a linear relation to the position of the "slider nob". I guess I will have to implement the desired functionality step by step.
To the user, the min/max values range from 10 m to 999 Km, I am trying to implement this in an exponential way, that will feel natural to the user. I.e. the user experiences a feeling of control over the values, big or small. Also that the "output" has reasonable values. Values like 10m 200m 2.5km 150 km etc. instead of 1.2342356 m or 108.93837756 km.
I would like for the step size to increase by 10m for the first 200m, then maybe by 50m up to 500m, then when passing the 1000 m value, it starts to deal with Kilometers, so then it is step size = 1 km up until 50 km, then maybe 25 km steps etc.
Any way I go about this I end up doing a lot of rounding and a lot of calculations wrapped in a forrest of if statements and NSString/Number conversions, each time the user moves the slider just a little.
I was hoping someone could lend me a bit of inspiration/math help or make me aware of a more lean approach to solving this problem.
My last idea is to populate and array with a 100 string values, then have the slider int value correspond to a string, this is not very flexible, but doable.
Thank you in advance for any help given:)
A quick scaling solution involves three special methods:
- (CGFloat)scaleValue:(CGFloat)value {
return pow(value, 10);
}
- (CGFloat)unscaleValue:(CGFloat)value {
return pow(value, 1.0 / 10.0);
}
- (CGFloat)roundValue:(CGFloat)value {
if(value <= 200) return floor(value / 10) * 10;
if(value <= 500) return floor(value / 50) * 50;
if(value <= 1000) return floor(value / 100) * 100;
if(value <= 50000) return floor(value / 1000) * 1000;
return floor(value / 25000) * 25000;
}
Reacting to the slider changes would be something like:
- (void)sliderChange:(id)sender {
CGFloat value = mySlider.value;
value = [self scaleValue:value];
value = [self roundValue:value];
valueLabel.text = [NSString stringWithFormat:@"%f", value];
}
You can init with the following code:
mySlider.maximumValue = [self unscaleValue:999000];
mySlider.minimumValue = [self unscaleValue:10];
I got all of the above to work without any problems but it could use some bulletproofing. The scaleValue:
and unscaleValue:
methods should check for unsupported values and I am sure sliderChange:
method could be more efficient.
In terms of speed, I am sure that this is faster than pulling objects out of an array. The actual slider behavior may feel a little wonky and there isn't much control over exactly what values are available with the slider, so it may not do exactly what you want. Using really high powers seemed to make it more useful.
You can also set a "step size" to build your slider like this :
- (void) sliderChanged:(UISlider *)slider
{
int value = (int)[slider value];
int stepSize = 500.0f;
value = value - value%stepSize;
[km setText:[NSString stringWithFormat:@"%d Km",value]];
}
Together with this solution, you can place two buttons (+ and -) to adjust your slider value :
- (void) incrementKm:(UIButton *)button
{
[kmSlider setValue:[kmSlider value] + 500.0f animated:YES];
[self sliderChanged:kmSlider];
}
- (void) decrementKm:(UIButton *)button
{
[kmSlider setValue:[kmSlider value] - 500.0f animated:YES];
[self sliderChanged:kmSlider];
}
Here's an option if you have an variable-length array of options you want the slider to choose:
-(void)setupSlider {
//this has to be (scale - 1) from sliderChanged selector to avoid index out of bounds error
_sldOptionPicker.maximumValue = 99;
//should be zero
_sldOptionPicker.minimumValue = 0;
//can be any value 0 to 99
_sldOptionPicker.value = 0;
}
-(IBAction)sliderChanged:(id)sender {
float scale = 100 / [optionsArray count];
int index = (int)(_sldOptionPicker.value / scale);
Option *mySelectedOption = (Option*)[optionsArray objectForIndex:index];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With