You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2140 lines
91 KiB
2140 lines
91 KiB
![]()
2 years ago
|
//
|
||
|
// MatchStickView.swift
|
||
|
// HPlusFit
|
||
|
//
|
||
|
// Created by lemo. on 2019/9/24.
|
||
|
// Copyright © 2019 lemo. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import UIKit
|
||
|
|
||
|
fileprivate struct Metric {
|
||
|
static let topHeight = kScaleHeight(65.0)
|
||
|
static let bottomHeight = kScaleHeight(40)
|
||
|
static let dotRadius = kScaleWidth(4.0)
|
||
|
static let rightMargin = kScaleWidth(40.0)
|
||
|
static let lineHeight = kScaleHeight(1.0)
|
||
|
}
|
||
|
|
||
|
struct Chartpoint {
|
||
|
var x: CGFloat
|
||
|
var y: CGFloat
|
||
|
}
|
||
|
|
||
|
class MatchStickView: UIView {
|
||
|
|
||
|
/// 图表模型配置
|
||
|
var model: ChartViewModel? {
|
||
|
didSet {
|
||
|
setModel()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate var shapeLayers: Array<CAShapeLayer> = []
|
||
|
fileprivate var layers: Array<CALayer> = []
|
||
|
fileprivate var valueLabels: Array<UILabel> = []
|
||
|
fileprivate var views: Array<UIView> = []
|
||
|
fileprivate var legendImageViews: Array<UIImageView> = []
|
||
|
fileprivate var legendLabels: Array<UILabel> = []
|
||
|
fileprivate var noDataView: UIView?
|
||
|
fileprivate var measureLineView: MeasureLineView?
|
||
|
fileprivate var measureResultLabel: UILabel?
|
||
|
/// 虚线
|
||
|
fileprivate var dotteShapLayers: [CAShapeLayer] = []
|
||
|
|
||
|
|
||
|
fileprivate lazy var leftIconImageView: UIImageView = {
|
||
|
let iconImageView = UIImageView.init()
|
||
|
iconImageView.contentMode = .scaleAspectFit
|
||
|
return iconImageView
|
||
|
}()
|
||
|
|
||
|
fileprivate lazy var titleLabel: UILabel = {
|
||
|
let label = UILabel.init()
|
||
|
label.font = SystemRegularFont(15.0)
|
||
|
return label
|
||
|
}()
|
||
|
|
||
|
fileprivate lazy var solidLine: UIView = {
|
||
|
let line = UIView.init()
|
||
|
line.backgroundColor = kHexColor(0xB8C9DC).withAlphaComponent(0.5)
|
||
|
return line
|
||
|
}()
|
||
|
|
||
|
// 中心值 && 中心单位
|
||
|
fileprivate lazy var centerLabel: UILabel = {
|
||
|
let label = UILabel.init()
|
||
|
label.font = SystemMediumFont(32)
|
||
|
return label
|
||
|
}()
|
||
|
fileprivate lazy var centerUnitLabel: UILabel = {
|
||
|
let label = UILabel.init()
|
||
|
label.font = SystemRegularFont(12)
|
||
|
label.textColor = kHexColor(0x888888)
|
||
|
return label
|
||
|
}()
|
||
|
// 图例
|
||
|
fileprivate lazy var legendView: UIStackView = {
|
||
|
let view = UIStackView()
|
||
|
view.axis = .horizontal
|
||
|
view.spacing = 10
|
||
|
return view
|
||
|
}()
|
||
|
|
||
|
fileprivate var chartHeight: CGFloat = kScaleHeight(158.0)
|
||
|
|
||
|
override init(frame: CGRect) {
|
||
|
super.init(frame: frame)
|
||
|
|
||
|
chartHeight = self.frame.size.height - Metric.topHeight - Metric.bottomHeight
|
||
|
|
||
|
self.addSubview(leftIconImageView)
|
||
|
self.addSubview(titleLabel)
|
||
|
self.addSubview(solidLine)
|
||
|
self.addSubview(centerLabel)
|
||
|
self.addSubview(centerUnitLabel)
|
||
|
self.addSubview(legendView)
|
||
|
|
||
|
drawGrid()
|
||
|
}
|
||
|
|
||
|
required init?(coder aDecoder: NSCoder) {
|
||
|
fatalError("init(coder:) has not been implemented")
|
||
|
}
|
||
|
|
||
|
override func layoutSubviews() {
|
||
|
super.layoutSubviews()
|
||
|
leftIconImageView.snp.makeConstraints { (make) in
|
||
|
make.left.equalToSuperview().offset(kScaleWidth(8.0))
|
||
|
make.top.equalToSuperview().offset(kScaleHeight(7.0))
|
||
|
}
|
||
|
|
||
|
titleLabel.snp.makeConstraints { (make) in
|
||
|
make.left.equalTo(leftIconImageView.snp.right)
|
||
|
make.centerY.equalTo(leftIconImageView.snp.centerY)
|
||
|
}
|
||
|
|
||
|
solidLine.snp.makeConstraints { (make) in
|
||
|
make.left.right.equalToSuperview()
|
||
|
make.bottom.equalToSuperview().offset(-Metric.bottomHeight)
|
||
|
make.height.equalTo(Metric.lineHeight)
|
||
|
}
|
||
|
centerLabel.snp.makeConstraints { (make) in
|
||
|
make.centerX.equalToSuperview()
|
||
|
make.top.equalTo(5)
|
||
|
}
|
||
|
centerUnitLabel.snp.makeConstraints { (make) in
|
||
|
make.lastBaseline.equalTo(centerLabel.snp.lastBaseline).offset(-1)
|
||
|
make.left.equalTo(centerLabel.snp.right).offset(kScaleWidth(5))
|
||
|
}
|
||
|
legendView.snp.makeConstraints { (make) in
|
||
|
make.centerX.equalToSuperview()
|
||
|
make.top.equalTo(centerLabel.snp.bottom).offset(10)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate func drawGrid() {
|
||
|
//绘制两条虚线
|
||
|
let lineSpace = chartHeight/2.0
|
||
|
|
||
|
for i in 0 ..< 2 {
|
||
|
let dotteShapLayer = CAShapeLayer()
|
||
|
dotteShapLayer.fillColor = UIColor.clear.cgColor
|
||
|
dotteShapLayer.strokeColor = kHexColor(0x4E576F).withAlphaComponent(0.2).cgColor
|
||
|
dotteShapLayer.lineWidth = Metric.lineHeight
|
||
|
|
||
|
let arr: Array = [6, 5]
|
||
|
dotteShapLayer.lineDashPhase = Metric.lineHeight
|
||
|
dotteShapLayer.lineDashPattern = arr as [NSNumber]
|
||
|
self.layer.addSublayer(dotteShapLayer)
|
||
|
dotteShapLayers.append(dotteShapLayer)
|
||
|
let pointy = Metric.topHeight + lineSpace * CGFloat.init(i)
|
||
|
let linePath = UIBezierPath.init()
|
||
|
linePath.move(to: CGPoint.init(x: kScaleWidth(16.0), y: pointy))
|
||
|
linePath.addLine(to: CGPoint.init(x: kScreenW-Metric.rightMargin, y: pointy))
|
||
|
dotteShapLayer.path = linePath.cgPath
|
||
|
|
||
|
/// 中线标签
|
||
|
let gridLabel = UILabel.init(frame: CGRect.init(x: kScreenW - Metric.rightMargin, y:pointy, width: 0, height: 0))
|
||
|
gridLabel.text = "0"
|
||
|
gridLabel.tag = 4000 + i
|
||
|
gridLabel.font = SystemRegularFont(11.0)
|
||
|
gridLabel.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
gridLabel.sizeToFit()
|
||
|
gridLabel.center = CGPoint.init(x: gridLabel.center.x, y: pointy)
|
||
|
self.addSubview(gridLabel)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension MatchStickView {
|
||
|
/// 设置模型
|
||
|
fileprivate func setModel() {
|
||
|
if let leftIcon = model?.leftIcon {
|
||
|
leftIconImageView.image = UIImage.init(named: leftIcon)
|
||
|
}else {
|
||
|
titleLabel.snp.updateConstraints { (make) in
|
||
|
make.left.equalToSuperview().offset(kScaleWidth(16.0))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
titleLabel.text = model?.title
|
||
|
if let chartLegends = model?.legends {
|
||
|
setLegends(legends: chartLegends)
|
||
|
}else {
|
||
|
for imgView in legendImageViews {
|
||
|
imgView.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
for label in legendLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
}
|
||
|
// 中心值 && 单位
|
||
|
if let center = model?.centerValue {
|
||
|
centerLabel.isHidden = false
|
||
|
centerLabel.text = center
|
||
|
centerLabel.textColor = model?.centerColor ?? .white
|
||
|
}else {
|
||
|
centerLabel.isHidden = true
|
||
|
}
|
||
|
if let centerUnit = model?.centerUnitValue {
|
||
|
centerUnitLabel.isHidden = false
|
||
|
centerUnitLabel.text = centerUnit
|
||
|
}else {
|
||
|
centerUnitLabel.isHidden = true
|
||
|
}
|
||
|
|
||
|
if let axisX = model?.xAxis {
|
||
|
setAxisX(axisX: axisX)
|
||
|
}
|
||
|
|
||
|
setSourceDatas(sourceDatas: model?.source, chartType: (model?.chartType)!)
|
||
|
}
|
||
|
|
||
|
/// 设置图例
|
||
|
fileprivate func setLegends(legends: Array<LegendData>?) {
|
||
|
legendView.arrangedSubviews.forEach({ view in
|
||
|
view.removeFromSuperview()
|
||
|
})
|
||
|
if let legends = legends {
|
||
|
for legend in legends {
|
||
|
let bgView = UIView()
|
||
|
let label = UILabel.init()
|
||
|
label.textColor = kHexColor(0x888888)
|
||
|
label.font = SystemRegularFont(9)
|
||
|
label.text = legend.name
|
||
|
let dotImageView = UIImageView.init()
|
||
|
dotImageView.backgroundColor = legend.docColor
|
||
|
dotImageView.layer.cornerRadius = 5
|
||
|
dotImageView.layer.masksToBounds = true
|
||
|
bgView.addSubview(dotImageView)
|
||
|
bgView.addSubview(label)
|
||
|
legendView.addArrangedSubview(bgView)
|
||
|
dotImageView.snp.makeConstraints { (make) in
|
||
|
make.left.equalToSuperview()
|
||
|
make.centerY.equalToSuperview()
|
||
|
make.height.width.equalTo(10)
|
||
|
}
|
||
|
label.snp.makeConstraints { (make) in
|
||
|
make.left.equalTo(dotImageView.snp.right).offset(5)
|
||
|
make.centerY.equalToSuperview()
|
||
|
}
|
||
|
bgView.snp.makeConstraints { (make) in
|
||
|
make.left.equalTo(dotImageView.snp.left)
|
||
|
make.right.equalTo(label.snp.right)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// 设置X坐标
|
||
|
///
|
||
|
/// - Parameter axisX: 坐标数组
|
||
|
fileprivate func setAxisX(axisX: Array<String>) {
|
||
|
/// 添加x轴坐标
|
||
|
let count = axisX.count
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
let startPoint = CGPoint.init(x: chartLeftMargin, y: self.frame.size.height - Metric.bottomHeight + kScaleHeight(5.0))
|
||
|
|
||
|
/// 移除x坐标
|
||
|
for view in self.subviews {
|
||
|
if 3000 ..< 4000 ~= view.tag {
|
||
|
view.removeFromSuperview()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i in axisX.indices {
|
||
|
let label = UILabel.init(frame: CGRect.init(x: 0.0, y: startPoint.y, width: 0.0, height: 0.0))
|
||
|
label.text = axisX[i]
|
||
|
label.font = SystemRegularFont(10.0)
|
||
|
label.textColor = kHexColor(0x888888)
|
||
|
label.textAlignment = .center
|
||
|
label.sizeToFit()
|
||
|
label.tag = 3000+i
|
||
|
label.center = CGPoint.init(x: startPoint.x+CGFloat.init(i)*space, y: label.center.y)
|
||
|
self.addSubview(label)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate func setSourceDatas(sourceDatas: Array<ChartViewData>?, chartType: ChartViewType) {
|
||
|
// 睡眠隐藏虚线
|
||
|
if chartType == .horizontalSegment || chartType == .segmentBar || chartType == .rectangleSegBar {
|
||
|
if let firstLabel = self.viewWithTag(4000) as? UILabel {
|
||
|
firstLabel.removeFromSuperview()
|
||
|
}
|
||
|
if let middleLabel = self.viewWithTag(4001) as? UILabel {
|
||
|
middleLabel.removeFromSuperview()
|
||
|
}
|
||
|
for layer in self.dotteShapLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
}else {
|
||
|
/// 更新虚线的值
|
||
|
if chartType == .temp, let floatMaxAxis = model?.floatMaxAxis {
|
||
|
let firstLabel = self.viewWithTag(4000) as! UILabel
|
||
|
firstLabel.text = String(format: "%.1lf", floatMaxAxis)
|
||
|
firstLabel.sizeToFit()
|
||
|
let middleLabel = self.viewWithTag(4001) as! UILabel
|
||
|
middleLabel.text = String(format: "%.1lf", floatMaxAxis / 2)
|
||
|
middleLabel.sizeToFit()
|
||
|
}else {
|
||
|
/// 更新虚线的值
|
||
|
if let maxAxis = model?.maxAxis {
|
||
|
let firstLabel = self.viewWithTag(4000) as! UILabel
|
||
|
firstLabel.text = "\(maxAxis)"
|
||
|
firstLabel.sizeToFit()
|
||
|
|
||
|
let middleLabel = self.viewWithTag(4001) as! UILabel
|
||
|
middleLabel.text = "\(maxAxis/2)"
|
||
|
middleLabel.sizeToFit()
|
||
|
|
||
|
// 配速图表网格线值
|
||
|
if chartType == .gradentFill && model?.leftIcon == "my_icon_pace" {
|
||
|
firstLabel.text = "\(maxAxis/60)\'\(maxAxis%60)\""
|
||
|
firstLabel.sizeToFit()
|
||
|
|
||
|
middleLabel.text = "\(maxAxis/2/60)\'\(maxAxis/2%60)"
|
||
|
middleLabel.sizeToFit()
|
||
|
}
|
||
|
// 燃脂图表网格线值
|
||
|
if chartType == .gradentFill && model?.leftIcon == "my_icon_kcal" {
|
||
|
firstLabel.text = String(format: "%.1f", Float(maxAxis)/1000.0)
|
||
|
firstLabel.sizeToFit()
|
||
|
|
||
|
middleLabel.text = String(format: "%.1f", Float(maxAxis/2)/1000.0)
|
||
|
middleLabel.sizeToFit()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if chartType != .horizontalSegment && chartType != .gradentFill {
|
||
|
/// 添加x轴坐标
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space:CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count-1)
|
||
|
}
|
||
|
|
||
|
let startPoint = CGPoint.init(x: chartLeftMargin, y: self.frame.size.height - Metric.bottomHeight + kScaleHeight(5.0))
|
||
|
|
||
|
/// 移除x坐标
|
||
|
for view in self.subviews {
|
||
|
if 2000 ..< 3000 ~= view.tag {
|
||
|
view.removeFromSuperview()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
let label = UILabel.init(frame: CGRect.init(x: 0.0, y: startPoint.y, width: 0.0, height: 0.0))
|
||
|
let dateComps = data.date?.components(separatedBy: "-").filter{ $0.count == 2}
|
||
|
label.text = dateComps?.joined(separator: "/")
|
||
|
if source.count > 10 {
|
||
|
if i % 5 != 0 && i != source.count - 1 {
|
||
|
label.text = ""
|
||
|
}
|
||
|
}
|
||
|
label.font = SystemRegularFont(8.0)
|
||
|
label.textColor = kRGBA(50.0, 50.0, 50.0, 1.0)
|
||
|
label.textAlignment = .center
|
||
|
label.sizeToFit()
|
||
|
label.tag = 2000+i
|
||
|
label.center = CGPoint.init(x: startPoint.x+CGFloat.init(i)*space, y: label.center.y)
|
||
|
self.addSubview(label)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch chartType {
|
||
|
case .stickMatch:
|
||
|
drawStickMatchChart(sourceDatas: sourceDatas)
|
||
|
case .gradentFill:
|
||
|
drawGradentFillChart(sourceDatas: sourceDatas)
|
||
|
case .polyline:
|
||
|
drawPolylineChart(sourceDatas: sourceDatas)
|
||
|
case .vertical:
|
||
|
drawVerticalLineChart(sourceDatas: sourceDatas)
|
||
|
case .segmentBar:
|
||
|
drawSegmentBarChart(sourceDatas: sourceDatas)
|
||
|
case .rectangleSegBar:
|
||
|
drawRectSegBarChart(sourceDatas: sourceDatas)
|
||
|
case .horizontalSegment:
|
||
|
drawHorizontalSegmentChart(sourceDatas: sourceDatas)
|
||
|
// case .progress:
|
||
|
// drawProgressBarChart(sourceDatas: sourceDatas)
|
||
|
case .roundBar:
|
||
|
drawRoundBarChart(sourceDatas: sourceDatas)
|
||
|
case .regularBar:
|
||
|
drawRegularBarChart(sourceDatas: sourceDatas)
|
||
|
case .stepRoundBar:
|
||
|
drawStepRoundBarChart(sourceDatas: sourceDatas)
|
||
|
case .hrvEcg:
|
||
|
drawHrvEcgChartH(sourceDatas: sourceDatas)
|
||
|
case .temp:
|
||
|
drawFloatGradentFillChart(sourceDatas: sourceDatas)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getLeftStartPosition(space: CGFloat, count: Int) -> CGFloat {
|
||
|
var leftStartPointX: CGFloat = self.center.x
|
||
|
if count % 2 == 0 {
|
||
|
leftStartPointX = self.center.x - (CGFloat(count/2) - 0.5) * space
|
||
|
}else {
|
||
|
leftStartPointX = self.center.x - CGFloat(count/2) * space
|
||
|
}
|
||
|
return leftStartPointX
|
||
|
}
|
||
|
|
||
|
func createXAxisLabel(sourceDatas: Array<ChartViewData>?, number: Int) {
|
||
|
/// 添加x轴坐标
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
let space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(number-1)
|
||
|
let leftStartPointX = getLeftStartPosition(space: space, count: count)
|
||
|
let startPoint = CGPoint.init(x: leftStartPointX, y: self.frame.size.height - Metric.bottomHeight + kScaleHeight(5.0))
|
||
|
|
||
|
/// 移除x坐标
|
||
|
for view in self.subviews {
|
||
|
if 5000 ..< 6000 ~= view.tag {
|
||
|
view.removeFromSuperview()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
let label = UILabel.init(frame: CGRect.init(x: 0.0, y: startPoint.y, width: 0.0, height: 0.0))
|
||
|
label.text = data.date
|
||
|
label.font = SystemRegularFont(10.0)
|
||
|
label.textColor = kRGBA(50.0, 50.0, 50.0, 1.0)
|
||
|
label.textAlignment = .center
|
||
|
label.sizeToFit()
|
||
|
label.tag = 5000+i
|
||
|
label.center = CGPoint.init(x: startPoint.x+CGFloat.init(i)*space, y: label.center.y)
|
||
|
self.addSubview(label)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// 睡眠日详情图表
|
||
|
fileprivate func drawHorizontalSegmentChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
|
||
|
if let source = sourceDatas {
|
||
|
let hours = 12
|
||
|
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space:CGFloat = 0.0
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(hours)
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 无数据遮罩
|
||
|
let valueSum = source.map{ $0.sleep?.value ?? 0 }.reduce(0, +)
|
||
|
guard valueSum > 0 else {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for i in source.indices {
|
||
|
let sr = source[i]
|
||
|
var color: UIColor?
|
||
|
switch sr.sleep?.type {
|
||
|
case 2: /// 深睡
|
||
|
color = model?.legends?.last?.docColor
|
||
|
case 1: /// 浅睡
|
||
|
color = (model?.legends?[1].docColor)!
|
||
|
case 0: /// 醒来
|
||
|
color = model?.legends?.first?.docColor
|
||
|
default:
|
||
|
color = .clear
|
||
|
}
|
||
|
|
||
|
let shapeLayer = CAShapeLayer.init()
|
||
|
shapeLayer.fillColor = color?.cgColor
|
||
|
self.layer.addSublayer(shapeLayer)
|
||
|
self.shapeLayers.append(shapeLayer)
|
||
|
|
||
|
let originX = chartLeftMargin + CGFloat.init(timeInterval(start: (model?.xAxis?.first)!, end: (sr.sleep?.begin)!)) * space
|
||
|
let width: CGFloat = CGFloat((sr.sleep?.value)!) * space
|
||
|
let bezierPath = UIBezierPath.init(roundedRect: CGRect.init(x: originX, y: Metric.topHeight + 20, width: width, height: chartHeight - 20), cornerRadius: 0.0)
|
||
|
shapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let hour_f = Float(value/space)
|
||
|
for sr in source {
|
||
|
if let begin = sr.sleep?.begin ,let end = sr.sleep?.end {
|
||
|
let begin_f = self.timeInterval(start: "21:00", end: begin)
|
||
|
let end_f = self.timeInterval(start: "21:00", end: end)
|
||
|
if hour_f >= begin_f && hour_f <= end_f {
|
||
|
self.measureLineView?.measureLineView.isHidden = false
|
||
|
var typeStr: String = ""
|
||
|
if let type = sr.sleep?.type {
|
||
|
typeStr = self.model?.legends?[type].name ?? ""
|
||
|
}
|
||
|
var resultStr: String = ""
|
||
|
if typeStr.count > 0 {
|
||
|
resultStr += typeStr
|
||
|
resultStr += ":"
|
||
|
}
|
||
|
|
||
|
if let value = sr.sleep?.value {
|
||
|
let duration = value * 60
|
||
|
resultStr += String(format: "%.0f", duration)
|
||
|
resultStr += MultiLanguageKey.min.localized
|
||
|
}
|
||
|
|
||
|
resultStr += " \(begin)-\(end)"
|
||
|
self.measureResultLabel?.text = resultStr
|
||
|
break
|
||
|
}else {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
self.measureLineView?.measureLineView.isHidden = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// 睡眠月统计图表
|
||
|
fileprivate func drawRectSegBarChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.sum }.reduce(0, +)
|
||
|
guard valueSum > 0 else {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/// 绘制分段条形图
|
||
|
for i in source.indices {
|
||
|
let sr = source[i]
|
||
|
if sr.sum > 0 {
|
||
|
if let sleeps = source[i].sleeps {
|
||
|
var lastPointy: CGFloat = baseH
|
||
|
lastPointy = baseH - CGFloat.init(sr.sum) * dotPerPixels
|
||
|
for sleep in sleeps.reversed() {
|
||
|
var color: UIColor?
|
||
|
switch sleep.type {
|
||
|
case 2: /// 深睡
|
||
|
color = model?.legends?.last?.docColor
|
||
|
case 1: /// 浅睡
|
||
|
color = (model?.legends?[1].docColor)!
|
||
|
case 0: /// 醒来
|
||
|
color = model?.legends?.first?.docColor
|
||
|
default:
|
||
|
color = .clear
|
||
|
}
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = color?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(2.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: lastPointy))
|
||
|
lastPointy = lastPointy + CGFloat.init(sleep.value!) * dotPerPixels
|
||
|
// 绘制最后一段确保不超过图标底线
|
||
|
if sleep.type == 2 {
|
||
|
lastPointy = baseH
|
||
|
}
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: lastPointy))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
let day = value/space
|
||
|
var idx: Int = 0
|
||
|
if day < 0 {
|
||
|
idx = 0
|
||
|
}else if (day >= CGFloat(source.count)) {
|
||
|
idx = source.count - 1
|
||
|
}else {
|
||
|
idx = lroundf(Float(day))
|
||
|
}
|
||
|
|
||
|
let sr = source[idx]
|
||
|
var duration: Float = 0.0
|
||
|
if let sleeps = sr.sleeps {
|
||
|
for sleep in sleeps {
|
||
|
if sleep.type == 2 || sleep.type == 1 {
|
||
|
duration += (sleep.value ?? 0.0)
|
||
|
}
|
||
|
}
|
||
|
if duration > 0 {
|
||
|
self.measureResultLabel?.text = "睡眠: " + String(format: "%.1f", duration) + "小时"
|
||
|
let topSpace = CGFloat.init(Float(maxAxis!) - sr.sum) * dotPerPixels
|
||
|
self.handleMeasureResult(topSpace)
|
||
|
}else {
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// 睡眠周统计
|
||
|
fileprivate func drawSegmentBarChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = (chartHeight - CGFloat.init(2))/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.sum }.reduce(0, +)
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
/// 绘制分段条形图
|
||
|
for i in source.indices {
|
||
|
let sr = source[i]
|
||
|
|
||
|
if sr.sum > 0 {
|
||
|
if let sleeps = source[i].sleeps {
|
||
|
var lastPointy: CGFloat = baseH
|
||
|
lastPointy = baseH - CGFloat.init(sr.sum) * dotPerPixels
|
||
|
var totalSleep: Float32 = 0.0
|
||
|
for sleep in sleeps.reversed() {
|
||
|
var color: UIColor?
|
||
|
switch sleep.type {
|
||
|
case 2: /// 深睡
|
||
|
color = model?.legends?.last?.docColor
|
||
|
case 1: /// 浅睡
|
||
|
color = (model?.legends?[1].docColor)!
|
||
|
case 0: /// 醒来
|
||
|
color = model?.legends?.first?.docColor
|
||
|
default:
|
||
|
color = .clear
|
||
|
}
|
||
|
totalSleep += sleep.value ?? 0
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = color?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(7.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: lastPointy))
|
||
|
lastPointy = lastPointy + CGFloat.init(sleep.value!) * dotPerPixels
|
||
|
// 绘制最后一段确保不超过图标底线
|
||
|
if sleep.type == 2 {
|
||
|
lastPointy = baseH
|
||
|
}
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: lastPointy))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
|
||
|
// // 数值标注
|
||
|
// let label = UILabel.init(frame: CGRect.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(sr.sum) * dotPerPixels - kScaleHeight(15.0), width: 0, height: 0))
|
||
|
// label.text = totalSleep.float2String()
|
||
|
// label.textAlignment = .center
|
||
|
// label.font = SystemRegularFont(11.0)
|
||
|
// label.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
// label.sizeToFit()
|
||
|
// self.addSubview(label)
|
||
|
// label.center = CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: label.center.y)
|
||
|
// valueLabels.append(label)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate func drawVerticalLineChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.maxValue }.reduce(0, +)
|
||
|
guard valueSum > 0 else {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/// 绘制竖线图
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
if data.maxValue > 0 && data.minValue > 0 {
|
||
|
|
||
|
/// 最大值与最小值相等的情况
|
||
|
if data.maxValue == data.minValue {
|
||
|
let circleShapeLayer = CAShapeLayer()
|
||
|
circleShapeLayer.fillColor = model?.lineColor?.cgColor
|
||
|
circleShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
self.layer.addSublayer(circleShapeLayer)
|
||
|
shapeLayers.append(circleShapeLayer)
|
||
|
|
||
|
let center = CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.minValue)*dotPerPixels)
|
||
|
let circlePath = UIBezierPath(arcCenter: center, radius: 1.5, startAngle: 0, endAngle: CGFloat(Double.pi*2), clockwise: true)
|
||
|
circleShapeLayer.path = circlePath.cgPath
|
||
|
}else {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(2.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.minValue)*dotPerPixels))
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.maxValue)*dotPerPixels))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/// 绘制平均值折线
|
||
|
if let isDrawAvgLine = model?.isDrawAvgLine, isDrawAvgLine {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = UIColor.red.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(1.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
var i = 0
|
||
|
var points = Array<Chartpoint>([])
|
||
|
for data in source {
|
||
|
if data.aveValue > 0 {
|
||
|
points.append(Chartpoint(x: CGFloat(i), y: CGFloat(data.aveValue)))
|
||
|
}
|
||
|
i += 1
|
||
|
}
|
||
|
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
for i in points.indices {
|
||
|
let point = points[i]
|
||
|
if i == 0 {
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+point.x*space, y: baseH - point.y*dotPerPixels))
|
||
|
}else {
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+point.x*space, y: baseH - point.y*dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let index_f = value/space
|
||
|
var idx: Int = 0
|
||
|
if index_f < 0 {
|
||
|
idx = 0
|
||
|
}else if (index_f >= CGFloat(source.count)) {
|
||
|
idx = source.count - 1
|
||
|
}else {
|
||
|
idx = lroundf(Float(index_f))
|
||
|
}
|
||
|
|
||
|
let sr = source[idx]
|
||
|
if sr.maxValue > 0 && sr.minValue > 0 {
|
||
|
// 血压
|
||
|
if self.model?.leftIcon == "my_icon_blood_pressure" {
|
||
|
self.measureResultLabel?.text = "收缩压:"+"\(sr.maxValue)mmhg" + " " + "舒张压:"+"\(sr.minValue)mmhg"
|
||
|
}
|
||
|
// 心率
|
||
|
if self.model?.leftIcon == "my_icon_hrart_rate" {
|
||
|
self.measureResultLabel?.text = "最高:"+"\(sr.maxValue)bpm" + " " + "最低:"+"\(sr.minValue)bpm"
|
||
|
}
|
||
|
let topSpace = CGFloat(maxAxis! - sr.maxValue)*dotPerPixels
|
||
|
self.handleMeasureResult(topSpace)
|
||
|
}else {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// 绘制常规条形图(月血氧)
|
||
|
///
|
||
|
/// - Parameter sourceDatas: 数据源
|
||
|
fileprivate func drawRegularBarChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
let valueSum = source.map{ $0.value }.reduce(0, +)
|
||
|
guard valueSum > 0 else {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/// 绘制竖线图
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
if data.value > 0 {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(2.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH))
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.value)*dotPerPixels))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
let index_f = value/space
|
||
|
var idx: Int = 0
|
||
|
if index_f < 0 {
|
||
|
idx = 0
|
||
|
}else if (index_f >= CGFloat(source.count)) {
|
||
|
idx = source.count - 1
|
||
|
}else {
|
||
|
idx = lroundf(Float(index_f))
|
||
|
}
|
||
|
|
||
|
let sr = source[idx]
|
||
|
if sr.value > 0 {
|
||
|
self.measureResultLabel?.text = "血氧:" + "\(sr.value)%"
|
||
|
let topSpace = CGFloat(maxAxis! - sr.value)*dotPerPixels
|
||
|
self.handleMeasureResult(topSpace)
|
||
|
}else {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// 绘制圆角条形图(血氧)
|
||
|
///
|
||
|
/// - Parameter sourceDatas: 数据源
|
||
|
fileprivate func drawRoundBarChart(sourceDatas: Array<ChartViewData>?){
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let margin = kScaleWidth(2.0)
|
||
|
let dotPerPixels = (chartHeight - CGFloat.init(2)*margin)/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight - margin // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.value }.reduce(0, +)
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
/// 圆角柱状图
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
if data.value > 0 {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(4.0)
|
||
|
lineShapeLayer.lineCap = CAShapeLayerLineCap.round
|
||
|
lineShapeLayer.lineJoin = CAShapeLayerLineJoin.round
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH))
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.value)*dotPerPixels))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
|
||
|
// 数值标注
|
||
|
let label = UILabel.init(frame: CGRect.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.value) * dotPerPixels - kScaleHeight(15.0), width: 0, height: 0))
|
||
|
label.text = "\(data.value)%"
|
||
|
label.textAlignment = .center
|
||
|
label.font = SystemRegularFont(11.0)
|
||
|
label.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
label.sizeToFit()
|
||
|
self.addSubview(label)
|
||
|
label.center = CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: label.center.y)
|
||
|
valueLabels.append(label)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// 绘制圆角条形图(今日步数)
|
||
|
///
|
||
|
/// - Parameter sourceDatas: 数据源
|
||
|
fileprivate func drawStepRoundBarChart(sourceDatas: Array<ChartViewData>?){
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let margin = kScaleWidth(2.0)
|
||
|
let dotPerPixels = (chartHeight - CGFloat.init(2)*margin)/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight - margin // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(count - 1)
|
||
|
}
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.value }.reduce(0, +)
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
/// 圆角柱状图
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
if data.value > 0 {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(4.0)
|
||
|
lineShapeLayer.lineCap = CAShapeLayerLineCap.round
|
||
|
lineShapeLayer.lineJoin = CAShapeLayerLineJoin.round
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH))
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat.init(data.value)*dotPerPixels))
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func handleMeasureResult(_ value: CGFloat) {
|
||
|
self.measureLineView?.drawMeasureLine(topSpace: value)
|
||
|
}
|
||
|
|
||
|
fileprivate func drawGradentFillChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for layer in layers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
let valueArr: [Int] = source.map{ $0.value }
|
||
|
let valueSum = valueArr.reduce(0, +)
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
guard valueSum > 0 else {
|
||
|
return
|
||
|
}
|
||
|
let valueCount = valueArr.filter { $0 > 0 }.count
|
||
|
if valueCount == 1 { // 一个点的情况
|
||
|
let positiveNumbers = valueArr.filter { $0 > 0 }
|
||
|
let circelShapeLayer = CAShapeLayer()
|
||
|
circelShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
circelShapeLayer.fillColor = model?.lineColor?.cgColor
|
||
|
self.layer.addSublayer(circelShapeLayer)
|
||
|
shapeLayers.append(circelShapeLayer)
|
||
|
let circlePath = UIBezierPath()
|
||
|
let point = CGPoint(x: chartLeftMargin, y:baseH - CGFloat.init(positiveNumbers[0]) * dotPerPixels)
|
||
|
circlePath.addArc(withCenter: point, radius: 3.0, startAngle: 0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
|
||
|
circelShapeLayer.path = circlePath.cgPath
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/// 绘图
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(0.2)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
let path = UIBezierPath()
|
||
|
|
||
|
// 能显示最多的点个数
|
||
|
var maxPoints: Int = source.count
|
||
|
if let maxPs = model?.maxpoints, maxPs > 0 {
|
||
|
maxPoints = maxPs
|
||
|
}
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var dotSpace: CGFloat = 0.0
|
||
|
if maxPoints > 1 {
|
||
|
dotSpace = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(maxPoints - 1)
|
||
|
}
|
||
|
|
||
|
// 实际显示的点个数
|
||
|
var pointCount = source.count
|
||
|
if let points = model?.points, points > 0 {
|
||
|
pointCount = points
|
||
|
}
|
||
|
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
path.move(to: CGPoint.init(x: chartLeftMargin, y:baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}else {
|
||
|
path.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
lineShapeLayer.path = path.cgPath
|
||
|
|
||
|
|
||
|
/// 绘制渐变图的轮廓
|
||
|
let contourShapeLayer = CAShapeLayer.init()
|
||
|
contourShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
contourShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
contourShapeLayer.lineWidth = kScaleWidth(1.2)
|
||
|
self.layer.addSublayer(contourShapeLayer)
|
||
|
shapeLayers.append(contourShapeLayer)
|
||
|
|
||
|
//轮廓的路径
|
||
|
let contourPath = UIBezierPath()
|
||
|
var isAddLine: Bool = false
|
||
|
var numberOfPoints = 0
|
||
|
// 折线不连续
|
||
|
if let isContinuous = model?.isContinuous, !isContinuous {
|
||
|
for i in 0 ..< pointCount {
|
||
|
if source[i].value > 0 {
|
||
|
numberOfPoints += 1
|
||
|
if !isAddLine {
|
||
|
contourPath.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
isAddLine = true
|
||
|
continue
|
||
|
}
|
||
|
contourPath.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
}else {
|
||
|
//连续
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
contourPath.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
|
||
|
}else {
|
||
|
contourPath.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// TODO 一个点的处理
|
||
|
if numberOfPoints == 1 {
|
||
|
}
|
||
|
contourShapeLayer.path = contourPath.cgPath
|
||
|
|
||
|
/// 遮罩层形状
|
||
|
let firstPoint: CGPoint = CGPoint.init(x: chartLeftMargin, y: baseH - CGFloat.init(source[0].value) * dotPerPixels)
|
||
|
let lastPoint: CGPoint = CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(pointCount-1), y: baseH - CGFloat.init(source[pointCount-1].value) * dotPerPixels)
|
||
|
|
||
|
let bezier1 = UIBezierPath()
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
bezier1.move(to: CGPoint.init(x: chartLeftMargin, y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}else {
|
||
|
bezier1.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bezier1.addLine(to: CGPoint.init(x: lastPoint.x, y: baseH))
|
||
|
bezier1.addLine(to: CGPoint.init(x: chartLeftMargin, y: baseH))
|
||
|
bezier1.addLine(to: firstPoint)
|
||
|
|
||
|
let shadeLayer = CAShapeLayer.init()
|
||
|
shadeLayer.path = bezier1.cgPath
|
||
|
shadeLayer.fillColor = UIColor.green.cgColor
|
||
|
|
||
|
/// 绘制渐变层
|
||
|
let colorA: UIColor = (model?.lineColor?.withAlphaComponent(0.5))!
|
||
|
let colorB: UIColor = (model?.lineColor?.withAlphaComponent(0.0))!
|
||
|
|
||
|
let gradientColors = [colorA.cgColor,
|
||
|
colorB.cgColor
|
||
|
]
|
||
|
let gradientLayer = CAGradientLayer()
|
||
|
gradientLayer.startPoint = CGPoint.init(x: 0, y: 0)
|
||
|
gradientLayer.endPoint = CGPoint.init(x: 0, y: 1)
|
||
|
gradientLayer.locations = [0.0, 1.0]
|
||
|
gradientLayer.frame = CGRect.init(x: 0, y: kScaleHeight(65.0), width: kScreenW, height: chartHeight)
|
||
|
gradientLayer.colors = gradientColors
|
||
|
|
||
|
let baseLayer = CALayer.init()
|
||
|
baseLayer.addSublayer(gradientLayer)
|
||
|
baseLayer.mask = shadeLayer
|
||
|
self.layer.insertSublayer(baseLayer, at: 0)
|
||
|
layers.append(baseLayer)
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
var idx = 0
|
||
|
let idx_f = value/dotSpace
|
||
|
if idx_f < 0 {
|
||
|
idx = 0
|
||
|
}else if (idx_f > CGFloat(count - 1)) {
|
||
|
idx = count - 1
|
||
|
}else {
|
||
|
idx = Int(idx_f)
|
||
|
}
|
||
|
if source[idx].value > 0 {
|
||
|
self.measureLineView?.measureLineView.isHidden = false
|
||
|
self.measureResultLabel?.text = "\(source[idx].value)"
|
||
|
let topSpace = CGFloat(maxAxis! - source[idx].value) * dotPerPixels
|
||
|
self.handleMeasureResult(topSpace)
|
||
|
}else {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
self.measureLineView?.measureLineView.isHidden = true
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func createMeasureResultLabel() -> UILabel {
|
||
|
let measureResultLabel = UILabel()
|
||
|
measureResultLabel.frame = CGRect(x: 0, y: kScaleHeight(65.0) - kScaleHeight(20.0), width: self.width, height: kScaleHeight(20.0))
|
||
|
measureResultLabel.center = CGPoint(x: self.center.x, y: measureResultLabel.center.y)
|
||
|
measureResultLabel.font = SystemRegularFont(15.0)
|
||
|
measureResultLabel.textAlignment = .center
|
||
|
measureResultLabel.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
return measureResultLabel
|
||
|
}
|
||
|
|
||
|
func drawPolylineChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
for view in views {
|
||
|
view.removeFromSuperview()
|
||
|
}
|
||
|
// 收缩压 && 舒张压标签
|
||
|
let dbpView = UIView(frame: CGRect(x: 25, y: 20 + 6, width: 10, height: 2))
|
||
|
dbpView.backgroundColor = kHexColor(0xCA1E44)
|
||
|
let dbpLabel = UILabel(frame: CGRect(x: 35 + 6, y: 20, width: 28, height: 14))
|
||
|
dbpLabel.text = "收缩压".localized
|
||
|
dbpLabel.textColor = kHexColor(0xCA1E44)
|
||
|
dbpLabel.font = SystemRegularFont(9)
|
||
|
self.addSubview(dbpView)
|
||
|
self.addSubview(dbpLabel)
|
||
|
let sbpView = UIView(frame: CGRect(x: dbpLabel.mj_x + 28 + 18, y: dbpView.mj_y, width: 10, height: 2))
|
||
|
sbpView.backgroundColor = kHexColor(0x76BA3F)
|
||
|
let sbpLabel = UILabel(frame: CGRect(x: sbpView.mj_x + 10 + 6, y: dbpLabel.mj_y, width: 28, height: 14))
|
||
|
sbpLabel.text = "舒张压".localized
|
||
|
sbpLabel.textColor = kHexColor(0x76BA3F)
|
||
|
sbpLabel.font = SystemRegularFont(9)
|
||
|
self.addSubview(sbpView)
|
||
|
self.addSubview(sbpLabel)
|
||
|
views.append(dbpView)
|
||
|
valueLabels.append(dbpLabel)
|
||
|
views.append(sbpView)
|
||
|
valueLabels.append(sbpLabel)
|
||
|
// 绘制数据
|
||
|
if let source = sourceDatas {
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
for layer in layers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
let valueArr: [Int] = source.map{ $0.value }
|
||
|
let valueSum = valueArr.reduce(0, +)
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
guard valueSum > 0 else {
|
||
|
return
|
||
|
}
|
||
|
// 能显示最多的点个数
|
||
|
var maxPoints: Int = source.count
|
||
|
if let maxPs = model?.maxpoints, maxPs > 0 {
|
||
|
maxPoints = maxPs
|
||
|
}
|
||
|
/// 添加X坐标
|
||
|
var dotSpace: CGFloat = 0.0
|
||
|
if maxPoints > 1 {
|
||
|
dotSpace = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(maxPoints - 1)
|
||
|
}
|
||
|
// 实际显示的点个数
|
||
|
var pointCount = source.count
|
||
|
if let points = model?.points, points > 0 {
|
||
|
pointCount = points
|
||
|
}
|
||
|
/// 绘图
|
||
|
var realPointCount = 0 // 实际绘制的点数量
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = kHexColor(0xCA1E44).cgColor
|
||
|
lineShapeLayer.lineWidth = 1
|
||
|
let path = UIBezierPath()
|
||
|
var isMove = false
|
||
|
for i in 0 ..< pointCount {
|
||
|
if source[i].value > 0 {
|
||
|
realPointCount += 1
|
||
|
if !isMove {
|
||
|
path.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
isMove = true
|
||
|
continue
|
||
|
}
|
||
|
path.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
// 第二根线
|
||
|
let lineShapeLayer2 = CAShapeLayer.init()
|
||
|
lineShapeLayer2.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer2.strokeColor = kHexColor(0x76BA3F).cgColor
|
||
|
lineShapeLayer2.lineWidth = 1
|
||
|
let path2 = UIBezierPath()
|
||
|
var isMove2 = false
|
||
|
for i in 0 ..< pointCount {
|
||
|
if source[i].value2 > 0 {
|
||
|
if !isMove2 {
|
||
|
path2.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - CGFloat.init(source[i].value2) * dotPerPixels))
|
||
|
isMove2 = true
|
||
|
continue
|
||
|
}
|
||
|
path2.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value2) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
// 仅有一个点
|
||
|
if realPointCount == 1 {
|
||
|
let radius: CGFloat = 2
|
||
|
path.addArc(withCenter: path.currentPoint, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
|
||
|
lineShapeLayer.fillColor = kHexColor(0xCA1E44).cgColor
|
||
|
path2.addArc(withCenter: path2.currentPoint, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
|
||
|
lineShapeLayer2.fillColor = kHexColor(0x76BA3F).cgColor
|
||
|
}
|
||
|
lineShapeLayer.path = path.cgPath
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
lineShapeLayer2.path = path2.cgPath
|
||
|
self.layer.addSublayer(lineShapeLayer2)
|
||
|
shapeLayers.append(lineShapeLayer2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileprivate func drawStickMatchChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
|
||
|
if let source = sourceDatas {
|
||
|
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
var space: CGFloat = 0.0
|
||
|
if count > 1 {
|
||
|
space = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat(count - 1)
|
||
|
}
|
||
|
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
let valueSum = source.map{ $0.maxValue }.reduce(0, +)
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
for i in source.indices {
|
||
|
let data = source[i]
|
||
|
|
||
|
/// 数据点不为0时绘制火柴
|
||
|
if data.minValue > 0 && data.maxValue > 0 {
|
||
|
|
||
|
// 绘制下圆点
|
||
|
let downDotShapeLayer = CAShapeLayer()
|
||
|
downDotShapeLayer.fillColor = model?.legends?.first?.docColor.cgColor
|
||
|
downDotShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
self.layer.addSublayer(downDotShapeLayer)
|
||
|
shapeLayers.append(downDotShapeLayer)
|
||
|
|
||
|
let downPoint = CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat(data.minValue)*dotPerPixels)
|
||
|
let downDotPath = UIBezierPath(arcCenter: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat(data.minValue)*dotPerPixels), radius: kScaleWidth(3.0), startAngle: 0, endAngle: CGFloat(Double.pi*2), clockwise: true)
|
||
|
|
||
|
downDotShapeLayer.path = downDotPath.cgPath
|
||
|
|
||
|
// 绘制上圆点
|
||
|
let upDotShapeLayer = CAShapeLayer()
|
||
|
upDotShapeLayer.fillColor = model?.legends?.last?.docColor.cgColor
|
||
|
upDotShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
self.layer.addSublayer(upDotShapeLayer)
|
||
|
shapeLayers.append(upDotShapeLayer)
|
||
|
|
||
|
let upPoint = CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat(data.maxValue)*dotPerPixels)
|
||
|
let upDotPath = UIBezierPath(arcCenter: CGPoint.init(x: chartLeftMargin+CGFloat.init(i)*space, y: baseH - CGFloat(data.maxValue)*dotPerPixels), radius: kScaleWidth(3.0), startAngle: 0, endAngle: CGFloat(Double.pi*2), clockwise: true)
|
||
|
|
||
|
upDotShapeLayer.path = upDotPath.cgPath
|
||
|
|
||
|
// 绘制直线
|
||
|
let lineShapeLayer = CAShapeLayer()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = kHexColor(0x4E576F).cgColor
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
let linePath = UIBezierPath()
|
||
|
linePath.move(to: CGPoint.init(x: downPoint.x, y: downPoint.y - kScaleWidth(3.0)))
|
||
|
linePath.addLine(to: CGPoint.init(x: upPoint.x, y: upPoint.y + kScaleWidth(3.0)))
|
||
|
lineShapeLayer.path = linePath.cgPath
|
||
|
//舒张压数据显示标签
|
||
|
let diastolicLabel = UILabel.init(frame: CGRect.init(x: downPoint.x, y: downPoint.y + kScaleHeight(5.0), width: 0, height: 0))
|
||
|
diastolicLabel.text = "\(data.minValue)"
|
||
|
diastolicLabel.textAlignment = .center
|
||
|
diastolicLabel.font = SystemRegularFont(11.0)
|
||
|
diastolicLabel.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
diastolicLabel.sizeToFit()
|
||
|
self.addSubview(diastolicLabel)
|
||
|
diastolicLabel.center = CGPoint.init(x: downPoint.x, y: diastolicLabel.center.y)
|
||
|
valueLabels.append(diastolicLabel)
|
||
|
//收缩压数据显示标签
|
||
|
let systolicLabel = UILabel.init(frame: CGRect.init(x: 0, y: upPoint.y - kScaleHeight(18.0), width: 0, height: 0))
|
||
|
systolicLabel.text = "\(data.maxValue)"
|
||
|
systolicLabel.textAlignment = .center
|
||
|
systolicLabel.font = SystemRegularFont(11.0)
|
||
|
systolicLabel.textColor = kRGBA(50.0, 50.0, 50.0, 0.5)
|
||
|
systolicLabel.sizeToFit()
|
||
|
self.addSubview(systolicLabel)
|
||
|
systolicLabel.center = CGPoint.init(x: upPoint.x, y: systolicLabel.center.y)
|
||
|
valueLabels.append(systolicLabel)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/// 绘制平均值折线
|
||
|
if let isDrawAvgLine = model?.isDrawAvgLine, isDrawAvgLine {
|
||
|
/// 图层
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = UIColor.red.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(1.0)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
var i = 0
|
||
|
var points = Array<Chartpoint>([])
|
||
|
for data in source {
|
||
|
if data.aveValue > 0 {
|
||
|
points.append(Chartpoint(x: CGFloat(i), y: CGFloat(data.aveValue)))
|
||
|
}
|
||
|
i += 1
|
||
|
}
|
||
|
|
||
|
/// 路径
|
||
|
let bezierPath = UIBezierPath.init()
|
||
|
for i in points.indices {
|
||
|
let point = points[i]
|
||
|
if i == 0 {
|
||
|
bezierPath.move(to: CGPoint.init(x: chartLeftMargin+point.x*space, y: baseH - point.y*dotPerPixels))
|
||
|
}else {
|
||
|
bezierPath.addLine(to: CGPoint.init(x: chartLeftMargin+point.x*space, y: baseH - point.y*dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
lineShapeLayer.path = bezierPath.cgPath
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/// 绘制HRV/心电图
|
||
|
///
|
||
|
/// - Parameter sourceDatas: 数据源
|
||
|
fileprivate func drawHrvEcgChartH(sourceDatas: Array<ChartViewData>?){
|
||
|
for label in valueLabels {
|
||
|
label.removeFromSuperview()
|
||
|
}
|
||
|
for view in views {
|
||
|
view.removeFromSuperview()
|
||
|
}
|
||
|
// 绘制数据
|
||
|
if let source = sourceDatas {
|
||
|
let maxAxis = model?.maxAxis
|
||
|
let dotPerPixels = chartHeight/CGFloat.init(maxAxis!)
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
for layer in layers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
let valueArr: [Int] = source.map{ $0.value }
|
||
|
let valueSum = valueArr.reduce(0, +)
|
||
|
|
||
|
// 网格
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
guard valueSum > 0 else {
|
||
|
return
|
||
|
}
|
||
|
// 能显示最多的点个数
|
||
|
var maxPoints: Int = source.count
|
||
|
if let maxPs = model?.maxpoints, maxPs > 0 {
|
||
|
maxPoints = maxPs
|
||
|
}
|
||
|
/// 添加X坐标
|
||
|
var dotSpace: CGFloat = 0.0
|
||
|
if maxPoints > 1 {
|
||
|
dotSpace = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(maxPoints - 1)
|
||
|
}
|
||
|
// 实际显示的点个数
|
||
|
var pointCount = source.count
|
||
|
if let points = model?.points, points > 0 {
|
||
|
pointCount = points
|
||
|
}
|
||
|
/// 绘图
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = kHexColor(0xCA1E44).cgColor
|
||
|
lineShapeLayer.lineWidth = 1
|
||
|
let path = UIBezierPath()
|
||
|
var isMove = false
|
||
|
for i in 0 ..< pointCount {
|
||
|
if source[i].value > 0 {
|
||
|
if !isMove {
|
||
|
path.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
isMove = true
|
||
|
continue
|
||
|
}
|
||
|
path.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - CGFloat.init(source[i].value) * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
lineShapeLayer.path = path.cgPath
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// 体温小数点折线图
|
||
|
fileprivate func drawFloatGradentFillChart(sourceDatas: Array<ChartViewData>?) {
|
||
|
if let source = sourceDatas {
|
||
|
let count = source.count
|
||
|
let maxAxis = model?.floatMaxAxis
|
||
|
let dotPerPixels = chartHeight/maxAxis!
|
||
|
let baseH = self.frame.size.height - Metric.bottomHeight // 图像启始位置(x轴位置)
|
||
|
let chartLeftMargin = kScaleWidth(49.0)
|
||
|
let chartRightMargin = kScaleWidth(72.0)
|
||
|
|
||
|
/// 先移除图层
|
||
|
for layer in shapeLayers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
for layer in layers {
|
||
|
layer.removeFromSuperlayer()
|
||
|
}
|
||
|
|
||
|
let valueArr: [CGFloat] = source.map{ CGFloat($0.floatValue) }
|
||
|
let valueSum = valueArr.reduce(0, +)
|
||
|
|
||
|
//无数据遮罩
|
||
|
if noDataView != nil {
|
||
|
noDataView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量线
|
||
|
if self.measureLineView != nil {
|
||
|
measureLineView?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
// 测量值
|
||
|
if self.measureResultLabel != nil {
|
||
|
measureResultLabel?.removeFromSuperview()
|
||
|
}
|
||
|
|
||
|
if valueSum == 0 {
|
||
|
let noDataView = NoDataChartView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: kScaleWidth(251.0), height: kScaleHeight(160.0)))
|
||
|
self.addSubview(noDataView)
|
||
|
self.noDataView = noDataView
|
||
|
}
|
||
|
|
||
|
guard valueSum > 0 else {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if count == 1 { // 一个点的情况
|
||
|
let circelShapeLayer = CAShapeLayer()
|
||
|
circelShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
circelShapeLayer.fillColor = model?.lineColor?.cgColor
|
||
|
self.layer.addSublayer(circelShapeLayer)
|
||
|
shapeLayers.append(circelShapeLayer)
|
||
|
|
||
|
let circlePath = UIBezierPath()
|
||
|
let point = CGPoint(x: chartLeftMargin, y:baseH - CGFloat.init(source[0].value) * dotPerPixels)
|
||
|
circlePath.addArc(withCenter: point, radius: 3.0, startAngle: 0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
|
||
|
circelShapeLayer.path = circlePath.cgPath
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/// 绘图
|
||
|
let lineShapeLayer = CAShapeLayer.init()
|
||
|
lineShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.strokeColor = UIColor.clear.cgColor
|
||
|
lineShapeLayer.lineWidth = kScaleWidth(0.2)
|
||
|
self.layer.addSublayer(lineShapeLayer)
|
||
|
shapeLayers.append(lineShapeLayer)
|
||
|
|
||
|
let path = UIBezierPath()
|
||
|
|
||
|
// 能显示最多的点个数
|
||
|
var maxPoints: Int = source.count
|
||
|
if let maxPs = model?.maxpoints, maxPs > 0 {
|
||
|
maxPoints = maxPs
|
||
|
}
|
||
|
|
||
|
/// 添加X坐标
|
||
|
var dotSpace: CGFloat = 0.0
|
||
|
if maxPoints > 1 {
|
||
|
dotSpace = (kScreenW - chartLeftMargin - chartRightMargin)/CGFloat.init(maxPoints - 1)
|
||
|
}
|
||
|
|
||
|
// 实际显示的点个数
|
||
|
var pointCount = source.count
|
||
|
if let points = model?.points, points > 0 {
|
||
|
pointCount = points
|
||
|
}
|
||
|
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
path.move(to: CGPoint.init(x: chartLeftMargin, y:baseH - source[i].floatValue * dotPerPixels))
|
||
|
}else {
|
||
|
path.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - source[i].floatValue * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
lineShapeLayer.path = path.cgPath
|
||
|
|
||
|
|
||
|
/// 绘制渐变图的轮廓
|
||
|
let contourShapeLayer = CAShapeLayer.init()
|
||
|
contourShapeLayer.fillColor = UIColor.clear.cgColor
|
||
|
contourShapeLayer.strokeColor = model?.lineColor?.cgColor
|
||
|
contourShapeLayer.lineWidth = kScaleWidth(1.2)
|
||
|
self.layer.addSublayer(contourShapeLayer)
|
||
|
shapeLayers.append(contourShapeLayer)
|
||
|
|
||
|
//轮廓的路径
|
||
|
let contourPath = UIBezierPath()
|
||
|
var isAddLine: Bool = false
|
||
|
|
||
|
// 折线不连续
|
||
|
if let isContinuous = model?.isContinuous, !isContinuous {
|
||
|
for i in 0 ..< pointCount {
|
||
|
// 点为0指向该点,但不再连线
|
||
|
if source[i].floatValue == 0 {
|
||
|
contourPath.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - source[i].floatValue * dotPerPixels))
|
||
|
isAddLine = false
|
||
|
}else {
|
||
|
// 第一个不为0的点
|
||
|
if isAddLine {
|
||
|
contourPath.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - source[i].floatValue * dotPerPixels))
|
||
|
}else {
|
||
|
contourPath.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - source[i].floatValue * dotPerPixels))
|
||
|
isAddLine = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}else {
|
||
|
//连续
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
contourPath.move(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y:baseH - source[i].floatValue * dotPerPixels))
|
||
|
|
||
|
}else {
|
||
|
contourPath.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - source[i].floatValue * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
contourShapeLayer.path = contourPath.cgPath
|
||
|
|
||
|
/// 遮罩层形状
|
||
|
let firstPoint: CGPoint = CGPoint.init(x: chartLeftMargin, y: baseH - source[0].floatValue * dotPerPixels)
|
||
|
let lastPoint: CGPoint = CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(pointCount-1), y: baseH - CGFloat.init(source[pointCount-1].value) * dotPerPixels)
|
||
|
|
||
|
let bezier1 = UIBezierPath()
|
||
|
for i in 0 ..< pointCount {
|
||
|
if i == 0 {
|
||
|
bezier1.move(to: CGPoint.init(x: chartLeftMargin, y: baseH - source[i].floatValue * dotPerPixels))
|
||
|
}else {
|
||
|
bezier1.addLine(to: CGPoint.init(x: chartLeftMargin+dotSpace*CGFloat(i), y: baseH - source[i].floatValue * dotPerPixels))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bezier1.addLine(to: CGPoint.init(x: lastPoint.x, y: baseH))
|
||
|
bezier1.addLine(to: CGPoint.init(x: chartLeftMargin, y: baseH))
|
||
|
bezier1.addLine(to: firstPoint)
|
||
|
|
||
|
let shadeLayer = CAShapeLayer.init()
|
||
|
shadeLayer.path = bezier1.cgPath
|
||
|
shadeLayer.fillColor = UIColor.green.cgColor
|
||
|
|
||
|
/// 绘制渐变层
|
||
|
let colorA: UIColor = (model?.lineColor?.withAlphaComponent(0.5))!
|
||
|
let colorB: UIColor = (model?.lineColor?.withAlphaComponent(0.0))!
|
||
|
|
||
|
let gradientColors = [colorA.cgColor,
|
||
|
colorB.cgColor
|
||
|
]
|
||
|
let gradientLayer = CAGradientLayer()
|
||
|
gradientLayer.startPoint = CGPoint.init(x: 0, y: 0)
|
||
|
gradientLayer.endPoint = CGPoint.init(x: 0, y: 1)
|
||
|
gradientLayer.locations = [0.0, 1.0]
|
||
|
gradientLayer.frame = CGRect.init(x: 0, y: kScaleHeight(65.0), width: kScreenW, height: chartHeight)
|
||
|
gradientLayer.colors = gradientColors
|
||
|
|
||
|
let baseLayer = CALayer.init()
|
||
|
baseLayer.addSublayer(gradientLayer)
|
||
|
baseLayer.mask = shadeLayer
|
||
|
self.layer.insertSublayer(baseLayer, at: 0)
|
||
|
layers.append(baseLayer)
|
||
|
|
||
|
if let isShowMeasureLine = model?.isShowMeasureLine, isShowMeasureLine {
|
||
|
let width = self.width - chartLeftMargin - chartRightMargin
|
||
|
let heght = self.height - kScaleHeight(65.0) - Metric.bottomHeight
|
||
|
//创建显示测量结果标签
|
||
|
let measureResultLabel = createMeasureResultLabel()
|
||
|
self.addSubview(measureResultLabel)
|
||
|
self.measureResultLabel = measureResultLabel
|
||
|
//创建测量线视图
|
||
|
let measureLine = MeasureLineView(frame: CGRect(x: chartLeftMargin, y: kScaleHeight(65.0), width: width, height: heght))
|
||
|
self.addSubview(measureLine)
|
||
|
self.measureLineView = measureLine
|
||
|
self.measureLineView?.measureLineClosure = { [weak self] (value, isHidden) in
|
||
|
guard let `self` = self else { return }
|
||
|
if isHidden {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
return
|
||
|
}
|
||
|
var idx = 0
|
||
|
let idx_f = value/dotSpace
|
||
|
if idx_f < 0 {
|
||
|
idx = 0
|
||
|
}else if (idx_f > CGFloat(count - 1)) {
|
||
|
idx = count - 1
|
||
|
}else {
|
||
|
idx = Int(idx_f)
|
||
|
}
|
||
|
if source[idx].value > 0 {
|
||
|
self.measureLineView?.measureLineView.isHidden = false
|
||
|
self.measureResultLabel?.text = "\(source[idx].floatValue)"
|
||
|
let topSpace = CGFloat(maxAxis! - source[idx].floatValue) * dotPerPixels
|
||
|
self.handleMeasureResult(topSpace)
|
||
|
}else {
|
||
|
self.measureResultLabel?.text = ""
|
||
|
self.measureLineView?.measureLineView.isHidden = true
|
||
|
self.handleMeasureResult(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
extension MatchStickView {
|
||
|
func timeInterval(start: String, end: String) -> Float {
|
||
|
|
||
|
if start.decomposeTime() - end.decomposeTime() > 0 {
|
||
|
return "24:00".decomposeTime() - start.decomposeTime() + end.decomposeTime()
|
||
|
}
|
||
|
|
||
|
return end.decomposeTime() - start.decomposeTime()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// MARK: - 符点型数据转字符串
|
||
|
extension Float {
|
||
|
func float2String() -> String {
|
||
|
let iValue = Int.init(self)
|
||
|
if Float.init(iValue) == self {
|
||
|
return "\(iValue)"
|
||
|
}
|
||
|
// 保留1位小数
|
||
|
return String(format: "%.1f", self)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// MARK: - 将字符串的时间转成带小数点的时间
|
||
|
extension String {
|
||
|
func decomposeTime() -> Float {
|
||
|
guard self.contains(":") else {
|
||
|
return 0.0
|
||
|
}
|
||
|
let comps = self.components(separatedBy: ":")
|
||
|
let result = Int(comps.first ?? "0")! * 60 + Int(comps.last ?? "0")!
|
||
|
return Float(result) / 60.0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension Reactive where Base: MatchStickView {
|
||
|
func chartDatsource() -> Binder<ChartViewModel> {
|
||
|
return Binder(self.base, binding: { (stickView, vm) in
|
||
|
stickView.model = vm
|
||
|
})
|
||
|
}
|
||
|
}
|