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.
 
 
 

2139 lines
91 KiB

//
// 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_fb_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_FB.minFB.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
})
}
}