UISlider 捕捉到固定数量的步骤(如 iOS 7 设置应用程序中的文本大小)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8219056/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UISlider that snaps to a fixed number of steps (like Text Size in the iOS 7 Settings app)
提问by Matias Vad
I'm trying to create a UISlider
that lets you choose from an array of numbers. Each slider position should be equidistant and the slider should snap to each position, rather than smoothly slide between them. (This is the behavior of the slider in Settings
> General
> Text Size
, which was introduced in iOS 7.)
我正在尝试创建一个UISlider
让您从数字数组中进行选择的对象。每个滑块位置应等距且滑块应卡入每个位置,而不是在它们之间平滑滑动。(这是iOS 7 中引入的Settings
> General
> 中滑块的行为Text Size
。)
The numbers I want to choose from are: -3, 0, 2, 4, 7, 10, and 12.
我要选择的数字是: -3, 0, 2, 4, 7, 10, and 12.
(I'm very new to Objective-C, so a complete code example would be much more helpful than a code snippet. =)
(我对 Objective-C 很陌生,所以完整的代码示例比代码片段更有帮助。=)
回答by matsr
Some of the other answers work, but this will give you the same fixed space between every position in your slider. In this example you treat the slider positions as indexes to an array which contains the actual numeric values you are interested in.
其他一些答案有效,但这将为您提供滑块中每个位置之间的相同固定空间。在此示例中,您将滑块位置视为包含您感兴趣的实际数值的数组的索引。
@interface MyViewController : UIViewController {
UISlider *slider;
NSArray *numbers;
}
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
slider = [[UISlider alloc] initWithFrame:self.view.bounds];
[self.view addSubview:slider];
// These number values represent each slider position
numbers = @[@(-3), @(0), @(2), @(4), @(7), @(10), @(12)];
// slider values go from 0 to the number of values in your numbers array
NSInteger numberOfSteps = ((float)[numbers count] - 1);
slider.maximumValue = numberOfSteps;
slider.minimumValue = 0;
// As the slider moves it will continously call the -valueChanged:
slider.continuous = YES; // NO makes it call only once you let go
[slider addTarget:self
action:@selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
}
- (void)valueChanged:(UISlider *)sender {
// round the slider position to the nearest index of the numbers array
NSUInteger index = (NSUInteger)(slider.value + 0.5);
[slider setValue:index animated:NO];
NSNumber *number = numbers[index]; // <-- This numeric value you want
NSLog(@"sliderIndex: %i", (int)index);
NSLog(@"number: %@", number);
}
I hope that helps, good luck.
希望能帮到你,祝你好运。
Edit: Here's a version in Swift 4 that subclasses UISlider with callbacks.
编辑:这是 Swift 4 中的一个版本,它使用回调子类化 UISlider。
class MySliderStepper: UISlider {
private let values: [Float]
private var lastIndex: Int? = nil
let callback: (Float) -> Void
init(frame: CGRect, values: [Float], callback: @escaping (_ newValue: Float) -> Void) {
self.values = values
self.callback = callback
super.init(frame: frame)
self.addTarget(self, action: #selector(handleValueChange(sender:)), for: .valueChanged)
let steps = values.count - 1
self.minimumValue = 0
self.maximumValue = Float(steps)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func handleValueChange(sender: UISlider) {
let newIndex = Int(sender.value + 0.5) // round up to next index
self.setValue(Float(newIndex), animated: false) // snap to increments
let didChange = lastIndex == nil || newIndex != lastIndex!
if didChange {
lastIndex = newIndex
let actualValue = self.values[newIndex]
self.callback(actualValue)
}
}
}
回答by Bart?omiej Semańczyk
If you have regular spaces between steps, you can use it like this for 15
value:
如果您在步骤之间有规则的空格,您可以像这样使用它来获取15
价值:
@IBAction func timeSliderChanged(sender: UISlider) {
let newValue = Int(sender.value/15) * 15
sender.setValue(Float(newValue), animated: false)
}
回答by PengOne
The answer is essentially the same at this answerto UISlider with increments of 5
To modify it to work for your case, you'll need to create a rounding function that returns only the values you want. For example, you could do something simple (though hacky) like this:
要修改它以适合您的情况,您需要创建一个只返回您想要的值的舍入函数。例如,您可以像这样做一些简单的事情(虽然很笨拙):
-(int)roundSliderValue:(float)x {
if (x < -1.5) {
return -3;
} else if (x < 1.0) {
return 0;
} else if (x < 3.0) {
return 2;
} else if (x < 5.5) {
return 4;
} else if (x < 8.5) {
return 7;
} else if (x < 11.0) {
return 10;
} else {
return 12;
}
}
Now use the answer from the previous post to round the value.
现在使用上一篇文章中的答案来舍入该值。
slider.continuous = YES;
[slider addTarget:self
action:@selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
Finally, implement the changes:
最后,实施更改:
-(void)valueChanged:(id)slider {
[slider setValue:[self roundSliderValue:slider.value] animated:NO];
}
回答by taykay08
This worked for my particular case, using @IBDesignable:
这适用于我的特殊情况,使用@IBDesignable:
Requires even, integer intervals:
需要偶数,整数间隔:
@IBDesignable
class SnappableSlider: UISlider {
@IBInspectable
var interval: Int = 1
override init(frame: CGRect) {
super.init(frame: frame)
setUpSlider()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpSlider()
}
private func setUpSlider() {
addTarget(self, action: #selector(handleValueChange(sender:)), for: .valueChanged)
}
@objc func handleValueChange(sender: UISlider) {
let newValue = (sender.value / Float(interval)).rounded() * Float(interval)
setValue(Float(newValue), animated: false)
}
}
回答by zoha131
If anyone also needs snaping animation then can do the following:
如果有人还需要捕捉动画,那么可以执行以下操作:
You can do the same from swift slider.isContinuous = false
你可以从 swift 做同样的事情 slider.isContinuous = false
- Add the following
@IBAction
in yourViewController
:
- 添加以下
@IBAction
在ViewController
:
@IBAction func sliderMoved(_ slider: UISlider){
let stepCount = 10
let roundedCurrent = (slider.value/Float(stepCount)).rounded()
let newValue = Int(roundedCurrent) * stepCount
slider.setValue(Float(newValue), animated: true)
}
I was inspired by this answer
我受到这个答案的启发
回答by thedonniew
Similar approach as PengOne, but I did manual rounding to make it more clear what was happening.
与 PengOne 类似的方法,但我进行了手动舍入以更清楚发生了什么。
- (IBAction)sliderDidEndEditing:(UISlider *)slider {
// default value slider will start at
float newValue = 0.0;
if (slider.value < -1.5) {
newValue = -3;
} else if (slider.value < 1) {
newValue = 0;
} else if (slider.value < 3) {
newValue = 2;
} else if (slider.value < 5.5) {
newValue = 4;
} else if (slider.value < 8.5) {
newValue = 7;
} else if (slider.value < 11) {
newValue = 10;
} else {
newValue = 12;
}
slider.value = newValue;
}
- (IBAction)sliderValueChanged:(id)sender {
UISlider *slider = sender;
float newValue = 0.0;
if (slider.value < -1.5) {
newValue = -3;
} else if (slider.value < 1) {
newValue = 0;
} else if (slider.value < 3) {
newValue = 2;
} else if (slider.value < 5.5) {
newValue = 4;
} else if (slider.value < 8.5) {
newValue = 7;
} else if (slider.value < 11) {
newValue = 10;
} else {
newValue = 12;
}
// and if you have a label displaying the slider value, set it
[yourLabel].text = [NSString stringWithFormat:@"%d", (int)newValue];
}