How to implement a range slider in Swift

I am trying to implement a Range Slider, and I used a custom NMRangeSlider control.

But when I use it, the slider does not appear at all. Maybe this is also because everything is written in Objective-C?

Here's how I did it now:

var rangeSlider = NMRangeSlider(frame: CGRectMake(16, 6, 275, 34)) rangeSlider.lowerValue = 0.54 rangeSlider.upperValue = 0.94 self.view.addSubview(rangeSlider) 
+7
ios objective-c swift uicontrol
source share
4 answers

UPDATE:

It did not seem to me, because everything was white. Thus, a solution that does not use any other frameworks and adheres to this, you need to install all the views for all components, and then it will display well:

enter image description here


I tried to import it into Swift, as I used it in Objective-C code, but with no luck. If I configure everything correctly and add it to either viewDidLoad() or viewDidAppear() , nothing will be displayed. One thing is worth mentioning though - when I enter the View debug hierarchy , the slider is actually on the canvas:

enter image description here

But it just doesn't display with all the colors that I set before adding a view to it. For the record, this is the code I used:

 override func viewDidAppear(animated: Bool) { var rangeSlider = NMRangeSlider(frame: CGRectMake(50, 50, 275, 34)) rangeSlider.lowerValue = 0.54 rangeSlider.upperValue = 0.94 let range = 10.0 let oneStep = 1.0 / range let minRange: Float = 0.05 rangeSlider.minimumRange = minRange let bgImage = UIView(frame: rangeSlider.frame) bgImage.backgroundColor = .greenColor() rangeSlider.trackImage = bgImage.pb_takeSnapshot() let trackView = UIView(frame: CGRectMake(0, 0, rangeSlider.frame.size.width, 29)) trackView.backgroundColor = .whiteColor() trackView.opaque = false trackView.alpha = 0.3 rangeSlider.trackImage = UIImage(named: "") let lowerThumb = UIView(frame: CGRectMake(0, 0, 8, 29)) lowerThumb.backgroundColor = .whiteColor() let lowerThumbHigh = UIView(frame: CGRectMake(0, 0, 8, 29)) lowerThumbHigh.backgroundColor = UIColor.blueColor() rangeSlider.lowerHandleImageNormal = lowerThumb.pb_takeSnapshot() rangeSlider.lowerHandleImageHighlighted = lowerThumbHigh.pb_takeSnapshot() rangeSlider.upperHandleImageNormal = lowerThumb.pb_takeSnapshot() rangeSlider.upperHandleImageHighlighted = lowerThumbHigh.pb_takeSnapshot() self.view.addSubview(rangeSlider) self.view.backgroundColor = .lightGrayColor() } 

Using the method to capture a UIView as a UIImage mentioned in this question :

 extension UIView { func pb_takeSnapshot() -> UIImage { UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale) drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } } 

Another solution:

You can also try sgwilly / RangeSlider , it is written in Swift and therefore you do not even need the Bridging header.

+2
source share

To create a custom range slider, I found a good solution here: iOS 8 range finder guide , but I need this in swift 3 for my project. I updated this for Swift 3 iOS 10 here:

  • in your main view controller add this to viewDidLayOut to display the range slider.

     override func viewDidLayoutSubviews() { let margin: CGFloat = 20.0 let width = view.bounds.width - 2.0 * margin rangeSlider.frame = CGRect(x: margin, y: margin + topLayoutGuide.length + 170, width: width, height: 31.0) } 
  • create a helper function to output the slider output below viewDidLayoutSubviews ()

     func rangeSliderValueChanged() { //rangeSlider: RangeSlider print("Range slider value changed: \(rangeSlider.lowerValue) \(rangeSlider.upperValue) ")//(\(rangeSlider.lowerValue) \(rangeSlider.upperValue)) } 
  • Create a RangeSlider.swift file and add it to it:

     import UIKit import QuartzCore class RangeSlider: UIControl { var minimumValue = 0.0 var maximumValue = 1.0 var lowerValue = 0.2 var upperValue = 0.8 let trackLayer = RangeSliderTrackLayer()//= CALayer() defined in RangeSliderTrackLayer.swift let lowerThumbLayer = RangeSliderThumbLayer()//CALayer() let upperThumbLayer = RangeSliderThumbLayer()//CALayer() var previousLocation = CGPoint() var trackTintColor = UIColor(white: 0.9, alpha: 1.0) var trackHighlightTintColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0) var thumbTintColor = UIColor.white var curvaceousness : CGFloat = 1.0 var thumbWidth: CGFloat { return CGFloat(bounds.height) } override init(frame: CGRect) { super.init(frame: frame) trackLayer.rangeSlider = self trackLayer.contentsScale = UIScreen.main.scale layer.addSublayer(trackLayer) lowerThumbLayer.rangeSlider = self lowerThumbLayer.contentsScale = UIScreen.main.scale layer.addSublayer(lowerThumbLayer) upperThumbLayer.rangeSlider = self upperThumbLayer.contentsScale = UIScreen.main.scale layer.addSublayer(upperThumbLayer) } required init?(coder: NSCoder) { super.init(coder: coder) } func updateLayerFrames() { trackLayer.frame = bounds.insetBy(dx: 0.0, dy: bounds.height / 3) trackLayer.setNeedsDisplay() let lowerThumbCenter = CGFloat(positionForValue(value: lowerValue)) lowerThumbLayer.frame = CGRect(x: lowerThumbCenter - thumbWidth / 2.0, y: 0.0, width: thumbWidth, height: thumbWidth) lowerThumbLayer.setNeedsDisplay() let upperThumbCenter = CGFloat(positionForValue(value: upperValue)) upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth / 2.0, y: 0.0, width: thumbWidth, height: thumbWidth) upperThumbLayer.setNeedsDisplay() } func positionForValue(value: Double) -> Double { return Double(bounds.width - thumbWidth) * (value - minimumValue) / (maximumValue - minimumValue) + Double(thumbWidth / 2.0) } override var frame: CGRect { didSet { updateLayerFrames() } } override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { previousLocation = touch.location(in: self) // Hit test the thumb layers if lowerThumbLayer.frame.contains(previousLocation) { lowerThumbLayer.highlighted = true } else if upperThumbLayer.frame.contains(previousLocation) { upperThumbLayer.highlighted = true } return lowerThumbLayer.highlighted || upperThumbLayer.highlighted } func boundValue(value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double { return min(max(value, lowerValue), upperValue) } override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { let location = touch.location(in: self) // 1. Determine by how much the user has dragged let deltaLocation = Double(location.x - previousLocation.x) let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - thumbWidth) previousLocation = location // 2. Update the values if lowerThumbLayer.highlighted { lowerValue += deltaValue lowerValue = boundValue(value: lowerValue, toLowerValue: minimumValue, upperValue: upperValue) } else if upperThumbLayer.highlighted { upperValue += deltaValue upperValue = boundValue(value: upperValue, toLowerValue: lowerValue, upperValue: maximumValue) } // 3. Update the UI CATransaction.begin() CATransaction.setDisableActions(true) updateLayerFrames() CATransaction.commit() sendActions(for: .valueChanged) return true } override func endTracking(_ touch: UITouch?, with event: UIEvent?) { lowerThumbLayer.highlighted = false upperThumbLayer.highlighted = false } } 
  • Then add a subclass file of the thumb RangeSliderThumbLayer.swift and add it to it:

      import UIKit class RangeSliderThumbLayer: CALayer { var highlighted = false weak var rangeSlider: RangeSlider? override func draw(in ctx: CGContext) { if let slider = rangeSlider { let thumbFrame = bounds.insetBy(dx: 2.0, dy: 2.0) let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0 let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius) // Fill - with a subtle shadow let shadowColor = UIColor.gray ctx.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 1.0, color: shadowColor.cgColor) ctx.setFillColor(slider.thumbTintColor.cgColor) ctx.addPath(thumbPath.cgPath) ctx.fillPath() // Outline ctx.setStrokeColor(shadowColor.cgColor) ctx.setLineWidth(0.5) ctx.addPath(thumbPath.cgPath) ctx.strokePath() if highlighted { ctx.setFillColor(UIColor(white: 0.0, alpha: 0.1).cgColor) ctx.addPath(thumbPath.cgPath) ctx.fillPath() } } } } 
  • Finally, add a subclass file for the track layer RangeSliderTrackLayer.swift and add the following to it:

      import Foundation import UIKit import QuartzCore class RangeSliderTrackLayer: CALayer { weak var rangeSlider: RangeSlider? override func draw(in ctx: CGContext) { if let slider = rangeSlider { // Clip let cornerRadius = bounds.height * slider.curvaceousness / 2.0 let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius) ctx.addPath(path.cgPath) // Fill the track ctx.setFillColor(slider.trackTintColor.cgColor) ctx.addPath(path.cgPath) ctx.fillPath() // Fill the highlighted range ctx.setFillColor(slider.trackHighlightTintColor.cgColor) let lowerValuePosition = CGFloat(slider.positionForValue(value: slider.lowerValue)) let upperValuePosition = CGFloat(slider.positionForValue(value: slider.upperValue)) let rect = CGRect(x: lowerValuePosition, y: 0.0, width: upperValuePosition - lowerValuePosition, height: bounds.height) ctx.fill(rect) } } } 

Build Run and Get: enter image description here

+2
source share

try this code:

 override func viewDidLayoutSubviews() { let margin: CGFloat = 20.0 let width = view.bounds.width - 2.0 * margin rangeSlider.frame = CGRect(x: margin, y: margin + topLayoutGuide.length, width: width, height: 31.0) } 
0
source share

I implemented a range slider using: https://github.com/Zengzhihui/RangeSlider

There is a method in the GZRangeSlider class called: private func setLabelText ()

In this method, simply put:

leftTextLayer.frame = CGRectMake (leftHandleLayer.frame.minX - 0.5 * (kTextWidth - leftHandleLayer.frame.width), leftHandleLayer.frame.minY - kTextHeight, kTextWidth, kTextHeight)

rightTextLayer.frame = CGRectMake (rightHandleLayer.frame.minX - 0.5 * (kTextWidth - leftHandleLayer.frame.width), leftTextLayer.frame.minY, kTextWidth, kTextHeight)

to animate the bottom and top marks.

This one works well for me and its quick ... just try it.

0
source share

All Articles