iOS开发示例代码

iOS仪表盘 Dashboard

2017-04-05  本文已影响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下载地址

上一篇下一篇

猜你喜欢

热点阅读