Как заставить UISlider выводить красивые округленные числа в геометрической прогрессии? - PullRequest
7 голосов
/ 11 мая 2010

Я реализую UISlider, которым пользователь может манипулировать, чтобы установить расстояние. Я никогда не использовал CocoaTouch UISlider, но использовал другие слайдеры фреймворков, обычно есть переменная для установки свойств «step» и других «helper».

Документация для UISlider имеет дело только с максимальными и минимальными значениями, а на выходе всегда находится 6-значное десятичное число с плавающей запятой, линейно зависящее от положения «ползунка». Думаю, мне придется шаг за шагом реализовать желаемую функциональность.

Для пользователя минимальные / максимальные значения находятся в диапазоне от 10 м до 999 км, я пытаюсь реализовать это экспоненциально, что будет естественно для пользователя. То есть пользователь испытывает чувство контроля над ценностями, большими или маленькими. Также, что «вывод» имеет разумные значения. Такие значения, как 10 м 200 м 2,5 км 150 км и т. Д. Вместо 1,2342356 м или 108,93837756 км.

Я бы хотел, чтобы размер шага увеличивался на 10 м в течение первых 200 м, затем, может быть, на 50 м до 500 м, затем при прохождении значения 1000 м он начинает работать с километрами, поэтому размер шага = 1 км до 50 км, затем, может быть, 25 км шагов и т. д.

Как бы то ни было, я заканчиваю большим округлением и множеством вычислений Форрест операторов if и преобразований NSString / Number, каждый раз, когда пользователь немного перемещает ползунок.

Я надеялся, что кто-нибудь может оказать мне немного вдохновения / математическую помощь или заставить меня осознать более бережный подход к решению этой проблемы.

Моя последняя идея - заполнить и массив 100 строковыми значениями, а затем задать ползунку int значение, соответствующее строке, это не очень гибко, но выполнимо.

Заранее благодарю за любую помощь:)

Ответы [ 6 ]

5 голосов
/ 13 мая 2010

Решение быстрого масштабирования включает три специальных метода:

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

Реакция на изменения ползунка будет выглядеть примерно так:

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

Вы можете начать со следующего кода:

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

Я получил все вышеперечисленное, чтобы работать без проблем, но он мог использовать некоторую пуленепробиваемость. Методы scaleValue: и unscaleValue: должны проверять неподдерживаемые значения, и я уверен, что метод sliderChange: может быть более эффективным.

С точки зрения скорости, я уверен, что это быстрее, чем вытаскивать объекты из массива. Реальное поведение ползунка может показаться немного странным, и нет большого контроля над тем, какие именно значения доступны с помощью ползунка, поэтому он может делать не совсем то, что вам нужно. Использование действительно высоких способностей, казалось, сделало его более полезным.

2 голосов
/ 27 июля 2011

Вы также можете установить «размер шага», чтобы построить свой слайдер следующим образом:

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

    value = value - value%stepSize;

    [km setText:[NSString stringWithFormat:@"%d Km",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]; 
}
1 голос
/ 07 марта 2012

Вот вариант, если у вас есть массив параметров переменной длины, которые вы хотите, чтобы ползунок выбрал:

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

}
0 голосов
/ 22 июля 2010

Чтобы сделать это правильно, вы хотите, чтобы ваш ползунок представлял экспоненты. Так, например, если вы хотите масштабировать 10-100000, вы бы хотели, чтобы ваш слайдер имел диапазон от 1 (10 = 10 ^ 1) до 5 (100 000 = 10 ^ 5). Вы можете установить степпинг на ползунке на дробь, чтобы дать вам необходимую точность. Для моего примера я буду использовать минимум 20 000 и максимум 20 000 000 для вывода (потому что это то, что мне было нужно, когда я сел, чтобы выяснить это). Поскольку я в основном собираюсь увеличить в 1000 раз от минимума до максимума, я хочу 10 ^ 3, поэтому мой ползунок будет 0-3 (10 ^ 0 оценивается в 1). Я буду использовать .001 степпинг, так что в общей сложности 3000 возможных позиций. Вот код, который вам понадобится для этого:

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

Предположим, вы хотите инвертировать эту функцию, чтобы вы могли установить положение ползунка с учетом внешнего входа. Тогда вы хотите использовать логарифм:

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

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

Вам нужно будет настроить 20000 в обеих функциях, чтобы они соответствовали вашей базовой сумме. Если ваш максимум должен быть получен показателями, отличными от 10, измените 10, а также максимум (показатель степени). Жизнь станет проще, если ты умножишься на десять.

0 голосов
/ 14 мая 2010
+ (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;

}

выше загружается в массив при создании экземпляра:

Установить ползунок:

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

Метод, вызванный на 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;
    }
}

Это даже заботится о форматировании строки для пользовательского интерфейса, например «200 м» или «1,5 км» и обновляет делегата с помощью номера расстояния в метрах для использования при сортировке результатов с помощью предиката.

0 голосов
/ 11 мая 2010

Самый простой ответ - использовать сегментированный элемент управления с разными «размерами шагов». В зависимости от того, какую опцию выберет пользователь, будет ли размер шага вашего слайдера. Я бы даже порекомендовал, что это, возможно, более удобный способ подойти к нему:).

Не стесняйтесь использовать Dapp , чтобы поиграть с внешним видом вашего приложения и посмотреть, как сегментированный элемент управления может соответствовать дизайну.

Однако ... вы хотели более бережного подхода;).

Я начал писать примерно 10 необходимых шагов, но остановился, когда понял, что вы, вероятно, уже пришли к подобному решению. Ваша идея строкового массива в порядке, я предполагаю, что вы просто конвертируете значение ползунка в целое число и извлекаете соответствующий индекс из массива?

Иногда, как программисты, мы заходим слишком далеко со своими подходами к проблеме. Да, строковый массив - не самое «гибкое» решение, но он быстрый! И я бы сказал, что даже гениальное математическое решение не так гибко, как вы думаете. Кроме того, если вы не планируете изменять значения в ближайшее время и вам не нужны разные диапазоны ползунков на разных ползунках, то имеет смысл просто создать статический массив.

Удачи! :)

...