How to make UISlider output beautiful rounded numbers exponentially?

I am implementing a UISlider that the user can manipulate to set the distance. I have never used CocoaTouch UISlider, but I used other framework sliders, usually there is a variable for setting the "step" properties and other "helpers".

The documentation for UISlider deals only with the maximum and minimum values, and the output is always 6 decimal numbers with a linear relationship to the "nob slider" position. I assume that I will need to complete the desired functionality step by step.

For the user, the min / max values ​​range from 10 m to 999 km, I'm trying to implement this exponentially, which will be natural for the user. That is, the user experiences a sense of control over values, large or small. Also, that "exit" has reasonable meanings. Values ​​such as 10 m 200 m 2.5 km 150 km, etc. Instead of 1.2342356 m or 108.93837756 km.

I would like the step size to increase by 10 m for the first 200 m, and then, possibly, by 50 m to 500 m, then when transmitting the value of 1000 m, it starts working with kilometers, so this is a step of size = 1 km to 50 km, then maybe 25 km of steps, etc.

In any case, I’m talking about this, I end up doing a lot of rounding and a lot of calculations wrapped in forrest if expressions and NSString / Number conversions, every time the user moves the slider slightly.

I was hoping someone could lend me some inspiration / math help or tell me about a more frugal approach to solving this problem.

My last idea is to fill and massage with 100 string values, then the value of the int slider corresponds to the string, it is not very flexible, but doable.

Thank you in advance for the help :)

+7
iphone uislider
source share
6 answers

The simplest answer is to use Segmented Control with different step sizes. Depending on which option the user chooses, what step size your slider will have. I even recommend that this is perhaps a more user-friendly way to get closer to this :).

You can use Dapp to play with the look of your application and see how segmented controls can fit the design.

However ... you need a more compact approach;).

I started writing 10 or so necessary steps, but I stopped when I realized that you probably had already come to a similar solution. The idea of ​​a string array is fine, I suppose, you just convert the value of the slider to an integer and grab the corresponding index from the array?

Sometimes, as programmers, we go too far with our approaches to the problem. Yes, the string array is not the most flexible, but it is fast! And, I would say that even a brilliant mathematical solution is not as flexible as you think. In addition, if you do not plan to change the values ​​in the near future and do not need different ranges of sliders on different sliders, then it makes sense to just create a static array.

Good luck! :)

0
source share

The fast scaling solution includes 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; } 

The reaction to the slider changes will look something like this:

 - (void)sliderChange:(id)sender { CGFloat value = mySlider.value; value = [self scaleValue:value]; value = [self roundValue:value]; valueLabel.text = [NSString stringWithFormat:@"%f", value]; } 

You can run using 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 can use some bulletproof. The scaleValue: and unscaleValue: should check for unsupported values, and I'm sure the sliderChange: method can be more efficient.

As for speed, I'm sure this is faster than pulling objects from an array. The actual behavior of the slider may feel a little awkward, and there is no control over what values ​​are available with the slider, so it may not do exactly what you want. Using really high power seemed to make it more useful.

+5
source share

You can also set the “step size” to create the slider as follows:

 - (void) sliderChanged:(UISlider *)slider { int value = (int)[slider value]; int stepSize = 500.0f; value = value - value%stepSize; [km setText:[NSString stringWithFormat:@"%d Km",value]]; } 

With this solution, you can put two buttons (+ and -) to adjust the value of the slider:

 - (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]; } 
+2
source share

Here's an option if you have a range of variable-length options that you want to select a slider:

 -(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]; } 
+1
source share
 + (NSArray*) getSliderNumbers { NSArray *sliderNumbers = [NSArray arrayWithObjects:@"10", @"20", @"30", @"40", @"50", @"60", @"70", @"80", @"90", @"100", @"150", @"200", @"250", @"300", @"350", @"400", @"450", @"500", @"600", @"700", @"800", @"900", @"1", @"1.5", @"2.0", @"2.5", @"3.0", @"3.5", @"4", @"4.5", @"5", @"5.5", @"6", @"6.5", @"7", @"7.5", @"8", @"8.5", @"9", @"9.5", @"10", @"15", @"20", @"25", @"30", @"35", @"40", @"45", @"50", @"55", @"60", @"65", @"70", @"75", @"80", @"85", @"90", @"95", @"100", @"200", @"300", @"400", @"500", @"600", @"700", @"800", @"900", nil]; return sliderNumbers; } 

above is loaded into the array after creating the instance:

Set up the slider:

  customSlider.minimumValue = 0.0f; customSlider.maximumValue = (CGFloat)[sliderNumbers count] - 1; customSlider.continuous = YES; customSlider.value = customSlider.maximumValue; 

Method called by UIControlEventValueChanged

 - (void) sliderMove:(UISlider*) theSlider { NSInteger numberLookup = lroundf([theSlider value]); NSString *distanceString = [sliderNumbers objectAtIndex:numberLookup]; CGFloat distanceInMeters; if (numberLookup > 21) { [self.indicator.indicatorLabel setText:[NSString stringWithFormat:@"%@ km", distanceString]]; distanceInMeters = [distanceString floatValue] * 1000; } else { [self.indicator.indicatorLabel setText:[NSString stringWithFormat:@"%@ m", distanceString]]; distanceInMeters = [distanceString floatValue]; } if (oldDistanceInMeters != distanceInMeters) { [self.delegate distanceSliderChanged:distanceInMeters]; oldDistanceInMeters = distanceInMeters; } } 

It even requires string formatting for the user interface, for example. “200 m” or “1.5 km” and updates the delegate with the distance number in meters to use when sorting my results with a predicate.

0
source share

To do this correctly, you want your slider to represent exhibitors. For example, if you need a scale of 10-100000, you want your slider to have a range from 1 (10 = 10 ^ 1) to 5 (100,000 = 10 ^ 5). You can adjust the step on the slider to a fraction to give you the accuracy you need. For my example, I will use a minimum of 20,000 and a maximum of 20,000,000 for output (because this is what I need when I sat down to figure this out). Since I'm basically going to increase 1000 times from min to max, I want 10 ^ 3, so my slider will be 0-3 (10 ^ 0 is evaluated to 1). I will use .001 stepping, so there are only 3,000 possible positions. Here is the code you need to execute:

  $("#amount-slider").slider({ value:20000, min: 0, max: 3, step:.001, slide: function(event, ui) { $("#amount").val(Math.pow(10, ui.value)*20000); } }); 

Suppose you want to invert this function so that you can set the position of the slider using some external input. Then you want to use the logarithm:

  var amtVal = parseFloat($("#amount").val()); $('#amount-slider').slider('value', ((Math.log(amtVal/20000)/Math.log(10)))); 

You will need to set up 20,000 in both functions according to your base amount. If your max should be output by exhibitors of something other than 10, change 10 as well as max (exponent). Life will be easier if you rise by the forces of ten.

0
source share

All Articles