iOS仪表盘 Dashboard

作者: 一根聪 | 来源:发表于2017-04-05 11:39 被阅读929次

    运行效果如下:


    //
    //  AKDashboardView.swift
    //  AKSwifty
    //
    //  Created by wangcong on 01/04/2017.
    //  Copyright © 2017 ApterKing. All rights reserved.
    //
    
    import UIKit
    
    // 指针
    class AKNeedleView: UIView {
        
        public var color = UIColor.red {
            didSet {
                self.setNeedsDisplay()
            }
        }
        
        convenience init() {
            self.init(frame: CGRect.zero)
        }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.backgroundColor = UIColor.clear
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
        
        override func draw(_ rect: CGRect) {
            super.draw(rect)
            
            let context = UIGraphicsGetCurrentContext()
            context?.setFillColor(UIColor.clear.cgColor)
            context?.setStrokeColor(color.cgColor)
            
            // 绘制第一条线
            context?.setLineWidth(1)
            context?.move(to: CGPoint(x: 0, y: rect.height / 2.0))
            context?.addLine(to: CGPoint(x: rect.width * 2.0 / 3.0, y: rect.height / 2.0))
            context?.drawPath(using: CGPathDrawingMode.fillStroke)
            
            // 绘制第二条线
            context?.setLineWidth(2)
            context?.setLineCap(CGLineCap.round)
            context?.move(to: CGPoint(x: rect.width * 2.0 / 3.0, y: rect.height / 2.0))
            context?.addLine(to: CGPoint(x: rect.width - 12, y: rect.height / 2.0))
            context?.drawPath(using: CGPathDrawingMode.fillStroke)
            
            // 绘制小圆圈
            context?.setLineWidth(2)
            context?.addArc(center: CGPoint(x: rect.width - 8, y: rect.height / 2.0), radius: 4, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            context?.drawPath(using: CGPathDrawingMode.fillStroke)
            
            // 绘制小圆边缘阴影
            context?.setStrokeColor(color.withAlphaComponent(0.5).cgColor)
            context?.addArc(center: CGPoint(x: rect.width - 8, y: rect.height / 2.0), radius: 6, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            context?.strokePath()    }
        
    }
    
    class AKDashboardView: UIView {
        
        // Edge
        private lazy var outEdgeLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.white.cgColor
            shapeLayer.lineWidth = 2.0
            shapeLayer.lineCap = kCALineCapButt
            return shapeLayer
        }()
        
        private lazy var inEdgeLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.white.cgColor
            shapeLayer.lineWidth = 2.0
            shapeLayer.lineCap = kCALineCapButt
            return shapeLayer
        }()
        
        public var edgeColor = UIColor.white {
            didSet {
                self.outEdgeLayer.strokeColor = edgeColor.cgColor
                self.inEdgeLayer.strokeColor = edgeColor.cgColor
            }
        }
        
        public var edgeWidth: CGFloat = 2.0 {
            didSet {
                self.outEdgeLayer.lineWidth = edgeWidth
                self.resetAll()
            }
        }
        
        public var edgeSpacing: CGFloat = 40 {
            didSet {
                self.maskLayer.lineWidth = edgeSpacing
                self.longScaleLayer.lineWidth = edgeSpacing / 3
                self.shortScaleLayer.lineWidth = edgeSpacing / 4
                self.resetAll()
            }
        }
        
        // 长刻度
        private lazy var longScaleLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.white.cgColor
            shapeLayer.lineWidth = 40 / 6
            shapeLayer.lineCap = kCALineCapButt
            return shapeLayer
        }()
        public var longScaleColor = UIColor.white {
            didSet {
                longScaleLayer.strokeColor = longScaleColor.cgColor
            }
        }
        public var longScales: Int = 5 {
            didSet {
                resetLongScaleLayer()
                resetLongScaleTextLayer()
            }
        }
        
        // 长刻度文字
        private var longScaleTextLayer: CALayer = CALayer()
        public var longScaleTextColor = UIColor.white {
            didSet {
                guard let sublayers = longScaleTextLayer.sublayers else {
                    return
                }
                for sublayer in sublayers {
                    if let textLayer = sublayer as? CATextLayer {
                        textLayer.foregroundColor = longScaleTextColor.cgColor
                    }
                }
            }
        }
        public var longScaleTexts: [String] = [] {
            didSet {
                resetLongScaleTextLayer()
            }
        }
        
        // 短刻度
        private lazy var shortScaleLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.white.cgColor
            shapeLayer.lineWidth = 40 / 8
            shapeLayer.lineCap = kCALineCapButt
            return shapeLayer
        }()
        public var shortScaleColor = UIColor.white {
            didSet {
                shortScaleLayer.strokeColor = shortScaleColor.cgColor
            }
        }
        
        public var shortScales: Int = 1 { // 短的刻度是:在长刻度区间内的分度值
            didSet {
                resetShortScaleLayer()
            }
        }
        
        // 进度条
        private lazy var maskLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.lineWidth = 40
            shapeLayer.strokeStart = 0.0
            shapeLayer.strokeEnd = 0.0
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.white.cgColor
            return shapeLayer
        }()
        
        private lazy var gradientLayer: CAGradientLayer = {
            let gradientLayer = CAGradientLayer()
            gradientLayer.colors = [UIColor.green.cgColor, UIColor.orange.cgColor, UIColor.red.cgColor]
            gradientLayer.startPoint = CGPoint(x: 0, y: 0)
            gradientLayer.endPoint = CGPoint(x: 1, y: 0)
            return gradientLayer
        }()
        
        public var gradientColors: [UIColor] = [UIColor.green, UIColor.orange, UIColor.red] {
            didSet {
                var cgColors = [CGColor]()
                for color in gradientColors {
                    cgColors.append(color.cgColor)
                }
                gradientLayer.colors = cgColors
            }
        }
        
        // 指针
        private var needleView = AKNeedleView()
        public var needleColor: UIColor = UIColor.white {
            didSet {
                needleView.color = needleColor
            }
        }
        
        // 设置刻度
        public var dashScale: CGFloat = 0.0 {
            didSet {
                setDashSacle(dashScale, animated: false)
            }
        }
        
        convenience init() {
            self.init(frame: CGRect.zero)
        }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            
            gradientLayer.mask = maskLayer
            self.layer.addSublayer(gradientLayer)
            
            shortScaleLayer.lineWidth = edgeSpacing / 4
            self.layer.addSublayer(shortScaleLayer)
            
            longScaleLayer.lineWidth = edgeSpacing / 3
            self.layer.addSublayer(longScaleLayer)
            
            self.layer.addSublayer(outEdgeLayer)
            self.layer.addSublayer(inEdgeLayer)
            
            needleView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
            self.addSubview(needleView)
            
            self.layer.addSublayer(longScaleTextLayer)
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
        
        override func layoutSubviews() {
            super.layoutSubviews()
            gradientLayer.frame = self.bounds
            longScaleTextLayer.frame = self.bounds
            resetAll()
        }
        
        // 重置Layer 及View
        fileprivate func resetAll() {
            
            resetLongScaleLayer()
            resetLongScaleTextLayer()
            resetShortScaleLayer()
            
            let radius = frame.size.width / 2 > frame.size.height ?
                frame.size.height - edgeWidth / 2: frame.size.width / 2 - edgeWidth / 2
            
            maskLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2, y: frame.size.height), radius: radius - edgeWidth / 2 - edgeSpacing / 2, startAngle: CGFloat(Float.pi), endAngle: CGFloat(Float.pi * 2), clockwise: true).cgPath
            
            outEdgeLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2, y: frame.size.height), radius: radius, startAngle: CGFloat(Float.pi), endAngle: CGFloat(Float.pi * 2), clockwise: true).cgPath
            
            inEdgeLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2, y: frame.size.height), radius: radius - edgeWidth / 2 - edgeSpacing, startAngle: CGFloat(Float.pi), endAngle: CGFloat(Float.pi * 2), clockwise: true).cgPath
            
            needleView.frame = CGRect(x: frame.size.width / 2 - radius + edgeSpacing - 2 * edgeWidth, y: frame.size.height - 15 / 2, width: radius - edgeSpacing + 2 * edgeWidth, height: 15)
        }
        
        fileprivate func resetLongScaleLayer() {
            let radius = frame.size.width / 2 > frame.size.height ?
                frame.size.height - edgeWidth / 2: frame.size.width / 2 - edgeWidth / 2
            let longScaleRadius = radius - edgeWidth / 2 - edgeSpacing / 6
            let C = CGFloat.pi * longScaleRadius
            longScaleLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2, y: frame.size.height), radius: longScaleRadius, startAngle: CGFloat(Float.pi), endAngle: CGFloat(Float.pi * 2), clockwise: true).cgPath
            longScaleLayer.lineDashPattern = [2, NSNumber(value: Float((C - CGFloat(2 * longScales)) / CGFloat(longScales - 1)))]
        }
        
        fileprivate func resetLongScaleTextLayer() {
            
            if let sublayers = self.longScaleTextLayer.sublayers {
                for sublayer in sublayers {
                    sublayer.removeFromSuperlayer()
                }
            }
            
            let textSize: CGFloat = 12
            let radius = frame.size.width / 2 > frame.size.height ?
                frame.size.height - edgeWidth / 2: frame.size.width / 2 - edgeWidth / 2
            let longScaleTextRadius = radius - edgeWidth / 2 - edgeSpacing / 6 - textSize
            let angleSpacing = CGFloat.pi / CGFloat(longScales - 1)
            
            var angle = CGFloat.pi
            for index in 0 ..< longScales {
                let textLayer = CATextLayer()
                textLayer.foregroundColor = longScaleTextColor.cgColor
                textLayer.fontSize = 10
                textLayer.string = index >= longScaleTexts.count ? "" : longScaleTexts[index % longScaleTexts.count]
                textLayer.allowsEdgeAntialiasing = true
                textLayer.contentsScale = UIScreen.main.scale
                textLayer.alignmentMode = kCAAlignmentCenter
                
                let x = longScaleTextRadius * cos(angle)
                let y = longScaleTextRadius * sin(angle)
                textLayer.frame = CGRect(origin: CGPoint(x: self.frame.size.width / 2.0 + x - textSize / 2.0, y: self.frame.size.height + y - (index == 0 || index == longScales - 1 ? textSize : textSize / 2.0)), size: CGSize(width: 12, height: 12))
                angle += angleSpacing
                
                self.longScaleTextLayer.addSublayer(textLayer)
            }
            
        }
        
        fileprivate func resetShortScaleLayer() {
            let radius = frame.size.width / 2 > frame.size.height ?
                frame.size.height - edgeWidth / 2: frame.size.width / 2 - edgeWidth / 2
            let shortScaleRadius = radius - edgeWidth / 2 - edgeSpacing / 8
            let C = CGFloat.pi * shortScaleRadius
            let realShortScales = (longScales - 1) * (shortScales + 1) + 1
            shortScaleLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2, y: frame.size.height), radius: shortScaleRadius, startAngle: CGFloat(Float.pi), endAngle: CGFloat(Float.pi * 2), clockwise: true).cgPath
            shortScaleLayer.lineDashPattern = [2, NSNumber(value: Float((C - CGFloat(2 * realShortScales)) / CGFloat(realShortScales - 1)))]
        }
        
        // MARK: 设置dashScale
        public func setDashSacle(_ scale: CGFloat, animated: Bool) {
            let realScale = scale > 1.0 ? 1.0 : (scale < 0 ? 0 : scale)
            if animated {
                let strokeAnim = CABasicAnimation(keyPath: "strokeEnd")
                strokeAnim.duration = 0.25
                strokeAnim.toValue = realScale
                strokeAnim.fillMode = kCAFillModeForwards
                strokeAnim.isRemovedOnCompletion = false
                maskLayer.add(strokeAnim, forKey: "strokeEnd")
            } else {
                self.maskLayer.strokeEnd = realScale
            }
            
            let rotatedAnim = CABasicAnimation(keyPath: "transform.rotation")
            rotatedAnim.duration = animated ? 0.25 : 0.01
            rotatedAnim.toValue = realScale * CGFloat.pi
            rotatedAnim.isRemovedOnCompletion = false
            rotatedAnim.fillMode = kCAFillModeForwards
            needleView.layer.add(rotatedAnim, forKey: "transform.rotation")
        }
    }
    

    ** 用法 **

            let dashboardView = AKDashboardView(frame: CGRect(x: (UIScreen.main.bounds.size.width - 300) / 2.0, y: 200, width: 300, height: 150))
            dashboardView.gradientColors = [UIColor.green, UIColor.orange, UIColor.red]
            dashboardView.needleColor = UIColor.white
            dashboardView.shortScaleColor = UIColor.white.withAlphaComponent(0.6)
            dashboardView.shortScales = 5
            dashboardView.longScales = 11
            dashboardView.longScaleColor = UIColor.white
            dashboardView.longScaleTexts = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
            dashboardView.setDashSacle(0.8, animated: true)
    

    Demo下载地址

    相关文章

      网友评论

      本文标题:iOS仪表盘 Dashboard

      本文链接:https://www.haomeiwen.com/subject/oqvtattx.html