I need a UISlider with value on the handle [closed

2019-04-17 18:13发布

问题:

I need to make a UISlider in my iOS App with the value of the slider in the handle.

Demo image of what I mean:

Is there a pod that could do it?

回答1:

You dont need neither any third party control nor you will have to create a slider on your own. You can still use the UISlider and customise it as per your need :)

Step 1:

As we can see we need to set the colour for minimum track and as it is not default white isn't it ??? Its somewhat orange, I dont know exact coolor of it hence am setting it to red. You set whatever colour you want :)

But UISlider's setMinimumTrackImage expects a UIImage not colour. So lets create a UIImage from colour. Am creating it as a lazy variable, because I want it to be created only once :)

let minColor : UIImage = {
    let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    context?.setFillColor(UIColor.red.cgColor)
    context?.fill(rect)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}()

Code is pretty simple. I believe no need to explain that :)

Step 2 :

From your image, we can conclude that the thumb image of slider needs to be custom. But we can't create it as static image, because its text should change based on progress isn't it ??

So lets create a method that returns thumb image based on UISlider progress :)

func progressImage(with progress : Float) -> UIImage {
    let layer = CALayer()
    layer.backgroundColor = UIColor.green.cgColor
    layer.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    layer.cornerRadius = 25

    let label = UILabel(frame: layer.frame)
    label.text = "\(progress)"
    layer.addSublayer(label.layer)
    label.textAlignment = .center
    label.tag = 100

    UIGraphicsBeginImageContext(layer.frame.size)
    layer.render(in: UIGraphicsGetCurrentContext()!)

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}

Step 3

Create a IBOutlet of UISlider and set the images you have created to slider :) preferably in ViewDidLoad.

self.myslider.setMinimumTrackImage(minColor, for: UIControlState.normal)
        self.myslider.setMinimumTrackImage(minColor, for: UIControlState.selected)
        self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.normal)
        self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.selected)

Step 4 :

Create a IBAction for your slider, and write

@IBAction func sliderMoved(_ sender: Any) {
    self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.normal)
    self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.selected)
}

Final Output

EDIT

Not really sure how many noticed it, but if you have seen the o/p carefully you can see that on setting minimum track image makes the UISlider sides square!!!!

How to make Slider edges round ??

Its because the UIImage that we created using the code was returning the square image, on the other hand if we had semi sphere (semi circle) image with curve on left side slider would have shown properly :)

As writing path to draw semi circle and converting that to image will take lot of time :P I am creating circular image with corner radius and exploiting withCapInsets to repeat only the part of the image am interested in making the image look like semi circle :D

Before we answer that lets answer another question :P

How to increase the height of slider ???

Create a subclass of your slider, and override

class MySlider: UISlider {
    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        return CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width, height: 10)
    }
}

As you can see am returning the height of UISlider to be 10.

Now lets go back to previous question. How to make UISlider edges curve ??

modify your minColor lazy variable declaration as below :)

let minColor : UIImage = {
    let layer = CALayer()
    layer.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
    layer.cornerRadius = 5
    layer.backgroundColor = UIColor.red.cgColor
    UIGraphicsBeginImageContext(layer.frame.size)
    layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}()

What am doing here ??? Create a layer of width and height 10, set corner radius to 5 which makes it a circle of radius 5 :) Thats all.

Change your code to set slider images in viewDidLoad as below :)

self.myslider.setMinimumTrackImage(minColor.resizableImage(withCapInsets: UIEdgeInsetsMake(3, 5, 5, 4)), for: UIControlState.normal)
        self.myslider.setMinimumTrackImage(minColor.resizableImage(withCapInsets: UIEdgeInsetsMake(3, 5, 5, 4)), for: UIControlState.selected)
        self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.normal)
        self.myslider.setThumbImage(self.progressImage(with: self.myslider.value), for: UIControlState.selected)

What are those UIEdgeInsetsMake(3, 5, 5, 4)??

I am carefully crafting only the part of the image (part which does not have any curve) and asking iOS to repeat it :) Thats all :)

How does final O/P look like then??