问题
I'm trying to implement Range Slider and I used custom control called NMRangeSlider.
But when I use it, the slider doesn't appear at all. Could it be also because it's all written in Objective-C?
This is how I've currently implemented it:
var rangeSlider = NMRangeSlider(frame: CGRectMake(16, 6, 275, 34))
rangeSlider.lowerValue = 0.54
rangeSlider.upperValue = 0.94
self.view.addSubview(rangeSlider)
回答1:
UPDATE:
It did not show to me, because it was all white. So the solution, without using any other framework and sticking with this one - you need to set all the views for all the components and then it will display well:

I have tried to import it in Swift as I used it before in Objective-C code, but without any luck. If I set everything properly and add it either in viewDidLoad()
or viewDidAppear()
, nothing gets displayed. One thing is worth mentioning, though - when I enter View Debug Hierarchy, the slider actually is there on the canvas:

But it's simply not rendered with all the colors that I did set before adding in it to the view. 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 for capturing the UIView as 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
}
}
Other solution:
You can also try sgwilly/RangeSlider instead, it's written in Swift and therefore you won't even need a Bridging Header.
回答2:
To create a custom Range Slider I found a good solution here: range finder tutorial iOS 8 but I needed 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 show a 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 the helper function to print slider output below viewDidLayoutSubviews()
func rangeSliderValueChanged() { //rangeSlider: RangeSlider print("Range slider value changed: \(rangeSlider.lowerValue) \(rangeSlider.upperValue) ")//(\(rangeSlider.lowerValue) \(rangeSlider.upperValue)) }
Create the file RangeSlider.swift and add this 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 } }
Next add the thumb layer subclass file RangeSliderThumbLayer.swift and add this 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 the track layer subclass file 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:
回答3:
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)
}
回答4:
I implemented the range slider using : https://github.com/Zengzhihui/RangeSlider
In the GZRangeSlider class, there is a method called : private func setLabelText()
In that method, just 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 lower and upper labels..
This one is working well for me and its in swift.. just try it..
来源:https://stackoverflow.com/questions/31179786/how-to-implement-range-slider-in-swift