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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:30:12  来源:igfitidea点击:

UISlider that snaps to a fixed number of steps (like Text Size in the iOS 7 Settings app)

iosobjective-ciphonexcodeuislider

提问by Matias Vad

I'm trying to create a UISliderthat 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。)

iOS 7 Text Size Slider

iOS 7 文字大小滑块

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 15value:

如果您在步骤之间有规则的空格,您可以像这样使用它来获取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

答案UISlider 的这个答案基本相同,增量为 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:

如果有人还需要捕捉动画,那么可以执行以下操作:

  1. Uncheck the continuous update of the slider from the storyboard. Storyboard
  1. 从故事板中取消选中滑块的持续更新。 故事板

You can do the same from swift slider.isContinuous = false

你可以从 swift 做同样的事情 slider.isContinuous = false

  1. Add the following @IBActionin your ViewController:
  1. 添加以下@IBActionViewController
   @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];
}