如何使 UISlider 以指数方式输出漂亮的舍入数字?
Posted
技术标签:
【中文标题】如何使 UISlider 以指数方式输出漂亮的舍入数字?【英文标题】:How to make UISlider output nice rounded numbers exponentially? 【发布时间】:2010-05-11 15:12:23 【问题描述】:我正在实现一个 UISlider,用户可以操纵它来设置距离。 我没用过CocoaTouch UISlider,但用过其他框架的滑块,通常有一个变量来设置“step”和其他“helper”属性。
UISlider 的文档只处理最大值和最小值,输出始终是 6 位小数浮点数,与“滑块 nob”的位置呈线性关系。我想我必须逐步实现所需的功能。
对于用户来说,最小/最大值范围从 10 m 到 999 Km,我试图以指数方式实现这一点,这对用户来说会感觉很自然。 IE。用户体验到对值的控制感,无论大小。此外,“输出”具有合理的价值。 10m 200m 2.5km 150 km 等值,而不是 1.2342356 m 或 108.93837756 km。
我想让步长在前 200m 增加 10m,然后可能增加 50m 到 500m,然后当通过 1000m 值时,它开始处理公里,所以它是步长 = 1公里直到 50 公里,然后可能是 25 公里的台阶等。
无论如何,我最终都会进行大量舍入和大量计算 if 语句和 NSString/Number 转换的 forrest,每次用户移动滑块一点点。
我希望有人能给我一些灵感/数学帮助,或者让我意识到 解决这个问题的更精简的方法。
我的最后一个想法是用 100 个字符串值填充和排列,然后让滑块 int 值对应一个字符串,这不是很灵活,但可行。
提前感谢您提供的任何帮助:)
【问题讨论】:
现在我使用 Apples Numbers 来制作我想要的实际数字(通过输入几个数字,选择并拖动。这使得 Numbers 以相同的间隔生成数字)我做了大约 75 个数字。然后我导出为 CSV,通过 Textmate 运行它并执行一些正则表达式以将数字作为字符串 @“number”,将数字粘贴到数组中并让滑块将它们拉出:NSInteger numberLookup = lroundf([slider value]); NSString *distanceString = [sliderNumbers objectAtIndex:numberLookup];我将滑块最小值设置为 0,最大值设置为 [slideNumbers count]。我仍然有兴趣听到想法:) 【参考方案1】:快速扩展解决方案涉及三种特殊方法:
- (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:
方法可能更有效。
在速度方面,我确信这比从数组中拉出对象要快。实际的滑块行为可能感觉有点不稳定,并且对于滑块可用的确切值没有太多控制,因此它可能无法完全按照您的意愿行事。使用非常高的功率似乎更有用。
【讨论】:
谢谢 MrHen!对于真正的指数值,这是一种非常精简的方法。当我开始优化时,我将尝试它如何与数组查找进行比较。再次感谢。【参考方案2】:您还可以设置“步长”来构建滑块,如下所示:
- (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];
【讨论】:
【参考方案3】:如果您希望滑块选择一个可变长度的选项数组,这里有一个选项:
-(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];
【讨论】:
【参考方案4】:最简单的答案是使用具有不同“步长”的分段控件。根据用户选择的选项,您的滑块将具有什么步长。我什至建议这可能是一种对用户更友好的方法:)。
随意使用Dapp 来体验您的应用外观,看看分段控件如何适合设计。
但是...您想要一种更精简的方法 ;)。
我开始编写所需的 10 个左右的步骤,但当我意识到您可能已经找到了类似的解决方案时,我停止了。您的字符串数组想法很好,我假设您只需将滑块值转换为整数并从数组中获取相关索引?
有时作为程序员,我们在解决问题的方法上走得太远了。是的,字符串数组不是最“灵活”的解决方案,但它很快!而且,我认为即使是天才的数学解决方案也没有你想象的那么灵活。此外,如果您不打算很快更改值并且不需要不同滑块上的不同滑块范围,那么创建一个静态数组是有意义的。
祝你好运! :)
【讨论】:
嗨,Raal,谢谢。 “有时作为程序员,我们在解决问题的方法上走得太远了”完美地总结了过去几个小时:) 我花了 25 分钟将我在之前的评论中描述的解决方案与数组放在一起。它工作得很好,我只处理 75 个值,所以查找时间/开销几乎不存在。我让我的滑块从 0-74 开始,然后将数字四舍五入/转换为整数,然后查找相应的值。当它从米变为公里时,我选择了索引 21 的值。如果有人可以从中受益,我将在答案中发布代码。【参考方案5】:+ (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 m”或“1.5 km”并使用以米为单位的距离数更新委托,以便在使用谓词对我的结果进行排序时使用。
【讨论】:
【参考方案6】:为了做到这一点,您希望滑块代表指数。因此,例如,如果您想要 10-100000 的比例,您希望滑块的范围为 1(10 = 10^1) 到 5 (100,000 = 10^5)。您可以将滑块上的步进设置为分数,以便为您提供所需的精度。对于我的示例,我将使用最少 20,000 和最多 20,000,000 的输出(因为这是我坐下来解决这个问题时所需要的)。因为我基本上要从 min 到 max 增加 1000 倍,所以我想要 10^3,所以我的滑块将是 0-3(10^0 评估为 1)。我将使用 0.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 以及最大值(指数)。如果你上升十次方,生活会更轻松。
【讨论】:
谢谢,对 jQuery 有用,但对 Cocoa Touch 没用!以上是关于如何使 UISlider 以指数方式输出漂亮的舍入数字?的主要内容,如果未能解决你的问题,请参考以下文章