2010-05-11 14 views
7

Estoy implementando un UISlider que un usuario puede manipular para establecer una distancia. Nunca he usado CocoaTouch UISlider, pero he usado otros controles deslizantes de marcos, generalmente hay una variable para configurar el "paso" y otras propiedades de "ayuda".¿Cómo hacer que UISlider produzca bonitos números redondeados exponencialmente?

La documentación para el UISlider trata solo con un valor máximo y mínimo, y la salida es siempre un flotante decimal 6 con una relación lineal con la posición del "control deslizante nob". Supongo que tendré que implementar la funcionalidad deseada paso a paso.

Para el usuario, los valores mín/máx variar de 10 a 999 m Km, estoy tratando de implementar esto en una forma exponencial, que se sentirá natural para el usuario. Es decir. el usuario experimenta una sensación de control sobre los valores, grandes o pequeños. También que el "resultado" tiene valores razonables. Valores como 10m, 200m, 2.5km, 150 km, etc. en lugar de 1.2342356 o 108.93837756 km.

Me gustaría que el tamaño del escalón aumente en 10 m durante los primeros 200 m, luego en 50 m hasta 500 m, luego al pasar el valor de 1000 m, comienza a ocupar Kilómetros, entonces es paso tamaño = 1 km hasta 50 km, luego tal vez pasos de 25 km, etc.

De cualquier forma que vaya sobre esto termino haciendo un montón de redondeos y muchos cálculos envueltos en forrest de declaraciones if y conversiones NSString/Number , cada vez que el usuario mueve el control deslizante solo un poco.

Esperaba que alguien me pudiera prestar un poco de inspiración/ayuda matemática o hacerme consciente de un enfoque más sencillo para resolver este problema.

Mi última idea es poblar y una matriz con valores de 100 de cuerda, y luego tener el valor deslizador int corresponden a una cadena, esto no es muy flexible, pero factible.

Gracias de antemano por cualquier ayuda dada :)

+1

Por ahora he usado Números de manzanas para hacer los números reales que quería (ingresando algunos números, seleccione y arrastre. Esto hace que Numbers genere números en el mismo intervalo) Hice unos 75 números. Luego exporté como CSV, lo ejecuté a través de Textmate e hice algunas expresiones regulares para obtener los números como cadenas @ "number", Pegué el número en una matriz e hice que el control deslizante los extraiga: \t NSInteger numberLookup = lroundf ([control deslizante valor]); \t NSString * distanceString = [sliderNumbers objectAtIndex: numberLookup]; Configuré mi control deslizante mínimo a 0 y máximo a [conteo de números deslizantes]. Todavía estoy interesado en escuchar ideas :) – RickiG

Respuesta

0

La respuesta más fácil es utilizar un control segmentado con los diferentes tamaños de paso ''. Dependiendo de la opción que el usuario seleccione, es qué tamaño de paso tendrá su control deslizante. Incluso recomendaría que esta sea una forma más amigable para el usuario :).

Siéntase libre de usar Dapp para jugar con el aspecto de su aplicación y ver cómo un control segmentado podría encajar con el diseño.

Sin embargo ... que quería un enfoque más delgado;).

empecé a escribir los 10 o más pasos necesarios, pero se detuvo cuando me di cuenta de que probablemente ya había llegado a una solución similar. La idea de matriz de cadenas está bien, supongo que simplemente convertirás el valor del control deslizante a un número entero y obtendrás el índice relevante de la matriz.

veces como programadores que ir demasiado lejos con nuestras aproximaciones a un problema. Sí, una matriz de cadenas no es la solución más flexible, ¡pero es rápida! Y, yo diría que incluso una solución matemática genial no es tan flexible como usted puede pensar. Además, si usted no planea cambiar los valores en el corto plazo y no necesitan diferentes rangos deslizante en diferentes controles deslizantes, entonces tiene sentido que acaba de crear una matriz estática.

¡Buena suerte!:)

+0

Hola Raal y gracias. "A veces, como programadores, vamos demasiado lejos con nuestros enfoques de un problema" resume perfectamente las últimas horas :) Me tomó 25 minutos reunir la solución que describí en mi comentario anterior con la matriz. Funciona perfectamente, solo trato con 75 valores, por lo que el tiempo de búsqueda/sobrecarga casi no existe. Hice que mi control deslizante pasara de 0-74 y redondeé/fundí los números a ints y simplemente busqué el valor correspondiente. Elegí un valor de índice 21 para cuando cambió de metros a kilómetros. Voy a publicar el código en una respuesta si alguien puede beneficiarse de ello. – RickiG

5

Una solución de escalamiento rápido implica tres métodos especiales:

- (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; 
} 

reaccionando a la barra cambia sería algo así como:

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

Puede init con el siguiente código:

mySlider.maximumValue = [self unscaleValue:999000]; 
mySlider.minimumValue = [self unscaleValue:10]; 

Hice todo lo anterior para trabajar sin ningún problema, pero podría usar algunas a prueba de balas Los métodos scaleValue: y unscaleValue: deben verificar los valores no admitidos y estoy seguro de que el método sliderChange: podría ser más eficiente.

En términos de velocidad, estoy seguro de que esto es más rápido que sacar objetos de una matriz. El comportamiento del control deslizante real puede parecer un poco inestable y no hay mucho control sobre exactamente qué valores están disponibles con el control deslizante, por lo que puede que no haga exactamente lo que usted desea. Usar poderes realmente altos pareció hacerlo más útil.

+0

¡Gracias MrHen! Ese es un enfoque bastante delgado para valores verdaderamente exponenciales. Cuando empiece a optimizar voy a probar cómo se compara con la búsqueda de array. Gracias de nuevo. – RickiG

0
+ (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; 

} 

anteriormente se carga en una matriz durante la instanciación:

Establecer el control deslizante:

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

El método llamado en 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; 
    } 
} 

Esto incluso se encarga de formato la cadena para la interfaz de usuario, por ejemplo "200 m" o "1.5 km" y actualiza al delegado con el número de distancia en metros para usar al ordenar mis resultados con un predicado.

0

Para hacer esto bien, desea que su control deslizante represente exponentes. Entonces, por ejemplo, si quiere una escala de 10-100000, querrá que su control deslizante tenga un rango de 1 (10 = 10^1) a 5 (100,000 = 10^5). Puede configurar el escalonamiento del control deslizante en una fracción para darle la precisión que desea. Para mi ejemplo, usaré un mínimo de 20,000 y un máximo de 20,000,000 para la salida (porque eso es lo que necesitaba cuando me senté para resolver esto). Como básicamente voy a aumentar 1000 veces de mínimo a máximo, quiero 10^3, por lo que mi control deslizante va a ser 0-3 (10^0 se evalúa a 1). Usaré pasos de .001 para que haya un total de 3000 posiciones posibles. Aquí está el código que necesita para lograr esto:

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

Suponga que desea la inversa de esta función, por lo que puede establecer la posición de la corredera dado alguna entrada externa. A continuación, desea utilizar logaritmo:

var amtVal = parseFloat($("#amount").val()); 

    $('#amount-slider').slider('value', ((Math.log(amtVal/20000)/Math.log(10)))); 

Tendrá que ajustar el 20000 en ambas funciones para que coincida con la cantidad de su base. Si su máximo debe derivarse por exponentes de algo que no sea 10, cambie 10 así como el máximo (exponente). La vida será más fácil si subes por poderes de diez.

+2

Esto no es una pregunta de javascript ... –

+0

Gracias, útil para jQuery pero no Cocoa Touch! –

2

También puede establecer un "tamaño de paso" para construir su deslizador de la siguiente manera:

- (void) sliderChanged:(UISlider *)slider 
{ 
    int value = (int)[slider value]; 
    int stepSize = 500.0f; 

    value = value - value%stepSize; 

    [km setText:[NSString stringWithFormat:@"%d Km",value]]; 
} 

Junto con esta solución, puede colocar dos botones (+ y -) para ajustar el valor del control deslizante:

- (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]; 
} 
1

Aquí es una opción si tiene una matriz de longitud variable de opciones que desea que el control deslizante para elegir:

-(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]; 

} 
Cuestiones relacionadas