【iOS】UISlider自定义样式(swift)

写在前面的:

在实际项目开发中,我们经常有定制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 // 设置 主视图背景色
    }

}

(==完==)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容