写在前面的:
在实际项目开发中,我们经常有定制Slider拖动控件样式的需求!
最近,在自己的项目中,正好也需要定制UISlider
这里将我自己的经验写下来,供需要的人参考!
最终效果:
实现思路:
遇到需要定制控件样式的需求,我们往往一开始会想到用UIView来封装自己的控件,因为这样最直接,也最顺手!……但是,如果从效率角度来讲,自己封装控件,也不见得好,甚至在调试和优化,以及代码复用方面,可能还不如在原生控件上面进行修改!
所以,我最终还是选择在UISlider的基础上,重写部分现有方法,来实现想要的效果!
- 重写thumbRect
- 重写trackRect
- 生成thumbImage图片
- 生成trackImage图片
完整代码:
UISlider 定义部分
//
// ColorSlider.swift
// UIKit-basic
//
// Created by Qire_er on 2021/11/27.
//
import UIKit
class ColorSlider: UISlider {
var valueLabel: UILabel! // 定义【值】文本
private var thumbColor: UIColor! // 定义【滑块】颜色
private var trackHeight: CGFloat = 0 // 定义【track】高度
private var colors: [CGColor]! // 定义【颜色】列表
private var locations: [CGFloat]! // 定义【位置】列表
init(frame: CGRect, trackHeight: CGFloat, colors: [CGColor], locations: [CGFloat]) {
super.init(frame: frame)
self.trackHeight = trackHeight
self.colors = colors
self.locations = locations
addValueLabel() // 添加【值】文本
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// 添加【值】文本
private func addValueLabel() {
self.valueLabel = UILabel()
self.valueLabel.textAlignment = .center
self.addSubview(valueLabel)
}
// 重写 draw 方法
override func draw(_ rect: CGRect) {
// 设置ThumbImage
let thumbWidth = rect.height
let thumbImg = makeThumbImage(rect: CGRect(x: 0, y: 0, width: thumbWidth, height: thumbWidth))
self.setThumbImage(thumbImg, for: .normal)
// 设置MinimumTrackImage
let minTrackImg = makeTrackImage(rect: rect) // 定义minimumTrackImage
self.setMinimumTrackImage(minTrackImg, for: .normal) // 设置minimumTrackImage
// 设置MaximumTrackImage(如果不设置,只有一边有渐变底色)
let maxTrackImg = makeTrackImage(rect: rect)
self.setMaximumTrackImage(maxTrackImg, for: .normal)
}
// 生成thumbImage图片 方法
private func makeThumbImage(rect: CGRect) -> UIImage {
let lineWidth: CGFloat = 2
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
let rect = rect.insetBy(dx: lineWidth / 2 + 3, dy: lineWidth / 2 + 3)
self.thumbColor.setFill()
let oval = UIBezierPath(ovalIn: rect)
oval.fill()
UIColor.white.setStroke()
oval.lineWidth = lineWidth
oval.stroke()
let thumbImg = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return thumbImg
}
// 生成trackImage图片 方法
private func makeTrackImage(rect: CGRect) -> UIImage {
let rect = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: self.trackHeight)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
let ctx = UIGraphicsGetCurrentContext()!
// 创建并设置路径
let cornerRadius: CGFloat = rect.height * 0.5
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
// 添加路径到图形上下文
ctx.addPath(path)
ctx.clip()
// 使用rgb颜色空间
let colorSpace = CGColorSpaceCreateDeviceRGB()
// 定义渐变色
let gradient = CGGradient(
colorsSpace: colorSpace,
colors: self.colors as CFArray,
locations: self.locations
)!
// 渐变开始位置
let start = CGPoint(x: self.bounds.minX, y: self.bounds.minY)
// 渐变结束位置
let end = CGPoint(x: self.bounds.maxX, y: self.bounds.minY)
// 绘制渐变
ctx.drawLinearGradient(gradient, start: start, end: end, options: .drawsBeforeStartLocation)
let trackImg = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return trackImg
}
// 重写 beginTracking 方法
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let tracking = super.beginTracking(touch, with: event)
let thumbWidth = frame.height
let thumbImg = makeThumbImage(rect: CGRect(x: 0, y: 0, width: thumbWidth, height: thumbWidth))
self.setThumbImage(thumbImg, for: .normal)
return tracking
}
// 重写 continueTracking 方法
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let tracking = super.continueTracking(touch, with: event)
let thumbWidth = frame.height
let thumbImg = makeThumbImage(rect: CGRect(x: 0, y: 0, width: thumbWidth, height: thumbWidth))
self.setThumbImage(thumbImg, for: .normal)
return tracking
}
// 重写 endTracking 方法
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
super.endTracking(touch, with: event)
let thumbWidth = frame.height
let thumbImg = makeThumbImage(rect: CGRect(x: 0, y: 0, width: thumbWidth, height: thumbWidth))
self.setThumbImage(thumbImg, for: .normal)
}
// 重写【thumb】显示区域 方法
override func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect {
let rect = super.thumbRect(forBounds: bounds, trackRect: rect, value: value)
self.thumbColor = UIColor(hue: CGFloat(self.value), saturation: 1, brightness: 1, alpha: 1)
// 添加【值】文本 label
if let valueLabel = self.valueLabel {
let offsetY: CGFloat = 25
valueLabel.frame = CGRect(x: rect.minX, y: rect.minY - offsetY, width: rect.width, height: rect.height)
valueLabel.text = String(Int(self.value * 100))
}
// 设置thumb投影效果
// (这样设置的投影效果,如果调用setValue(_:animated:)的时候,animated参数为true,会有阴影和thumb不同步的问题,目前还不知到怎么解决)
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0)
self.layer.shadowOpacity = 0.15
self.layer.shadowRadius = 3
self.layer.shadowPath = UIBezierPath(cgPath: CGPath(ellipseIn: rect.insetBy(dx: 3, dy: 3), transform: nil)).cgPath
return rect
}
// 重写【track】显示区域 方法
override func trackRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: 0, y: 0, width: bounds.width, height: self.trackHeight)
}
}
UISlider 调用部分
//
// ColorSliderVC.swift
// UIKit-basic
//
// Created by Qire_er on 2021/11/27.
//
import UIKit
class ColorSliderVC: UIViewController {
let width = UIScreen.main.bounds.width // 屏幕【宽度】
let height = UIScreen.main.bounds.height // 屏幕【高度】
lazy var sliderWidth: CGFloat = width - 60 // slider【宽度】
let slidermHeight: CGFloat = 36 // slider【高度】
let colors = [
UIColor.red.cgColor,
UIColor.yellow.cgColor,
UIColor.green.cgColor,
UIColor.cyan.cgColor,
UIColor.blue.cgColor,
UIColor.magenta.cgColor,
UIColor.red.cgColor
] // 定义【颜色】列表
var locations = [
CGFloat(0)/CGFloat(360),
CGFloat(60)/CGFloat(360),
CGFloat(120)/CGFloat(360),
CGFloat(180)/CGFloat(360),
CGFloat(240)/CGFloat(360),
CGFloat(300)/CGFloat(360),
CGFloat(360)/CGFloat(360)
] // 定义【位置】列表
override func viewDidLoad() {
super.viewDidLoad()
let colorSlider = ColorSlider(
frame: CGRect(x: (width - sliderWidth) * 0.5, y: (height - slidermHeight) * 0.5, width: sliderWidth, height: slidermHeight),
trackHeight: 8,
colors: self.colors,
locations: self.locations
) // 放置在屏幕中心
colorSlider.setValue(0.5, animated: true) // 设置slider【初始值】
view.addSubview(colorSlider) // 添加slider 到 主视图
view.backgroundColor = UIColor.white // 设置 主视图背景色
}
}