// // SleepViewController.swift // Twear // // Created by yangbin on 2021/11/25. // import UIKit import Charts import SwiftDate class SleepViewController: UIViewController, DateSegmentViewDelegate, RangeSliderDelegate, ChartViewDelegate { @IBOutlet weak var dateSegmentView: DateSegmentView! @IBOutlet weak var barChartView: BarChartView! @IBOutlet weak var sliderView: RangeSliderView! @IBOutlet weak var dateLabel: UILabel! @IBOutlet weak var lengthLabel: UILabel! @IBOutlet weak var deepLabel: UILabel! @IBOutlet weak var awakeLabel: UILabel! @IBOutlet weak var lightLabel: UILabel! @IBOutlet weak var sleepLabel: UILabel! @IBOutlet weak var analysisLabel: UILabel! private var dateType: DateType = .day private var selectedDate = Date() private lazy var monthDays = DateInRegion().monthDays private var barWidth: Double = 1.0/60.0 private struct SleepPoint { var point: Double = 0 var type: SleepType = .awake var length: Int = 0 } private var points: [SleepPoint] = [] private var sleepArray: [SleepModel] = [] { didSet { points = [] if sleepArray.count == 0 { resetLabel() } } } private var sleepSummaryArray: [SleepSummary] = [] { didSet { points = [] if sleepSummaryArray.count == 0 { resetLabel() } } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(true, animated: true) } override func viewDidLoad() { super.viewDidLoad() dateSegmentView.selectedColor = UIColor.rgbColorFromHex(0x9B0AFB) dateSegmentView.delegate = self sliderView.delegate = self // print(RealmTools.queryObjects(SleepModel.self)) view.layoutIfNeeded() setupChartView() didSelectedDate(date: DateInRegion(), dateType: .day) // Do any additional setup after loading the view. } @IBAction func share(_ sender: Any) { let shareView = ShareView(view.captureImage) shareView.show() } //MARK: DateSegmentViewDelegate func didSelectedDate(date: DateInRegion, dateType: DateType) { self.dateType = dateType monthDays = date.monthDays selectedDate = date.date switch dateType { case .day: sleepArray = SleepModel.getSleepByDay(selectedDate) dateLabel.text = "--" case .week: sleepSummaryArray = SleepModel.getSleepByWeek(selectedDate) dateLabel.text = (date.dateAt(.startOfWeek)+1.days).toString(.custom("yyyy.MM.dd")) + " " + LocString("周一") case .month: sleepSummaryArray = SleepModel.getSleepByMonth(selectedDate) dateLabel.text = date.dateAt(.startOfMonth).toString(.custom("yyyy.MM.dd")) case .year: sleepSummaryArray = SleepModel.getSleepByYear(selectedDate) dateLabel.text = date.dateAtStartOf(.year).toString(.custom("yyyy.MM")) + " " + LocString("日均") } updateChartView() } //MARK: RangeSliderDelegate func sliderDidChanged(index: Int) { barChartView.highlightValues([Highlight(x: points[index].point, y: 0, dataSetIndex: 0)]) updateLable(index) } //MARK: ChartViewDelegate func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { // if sleepArray.count == 1 { // barChartView.highlightValues([Highlight(x: points[0].point, y: 0, dataSetIndex: 0)]) // } else { sliderView.didSelectedValue(entry.x) // } } private func resetLabel() { lengthLabel.toTimeType1(length: -1) deepLabel.text = "--" awakeLabel.text = "--" deepLabel.text = "--" lightLabel.text = "--" sleepLabel.toTimeType2(length: -1) analysisLabel.text = LocString("亲,您的睡眠健康指数已击败全国") + "0%" + LocString("的同龄人") } private func updateLable(_ index: Int) { var tLength: Int = 0 if dateType == .day { if sleepArray.count == 0 { resetLabel() return } switch points[index].type { case .awake: dateLabel.text = LocString("清醒") case .deep: dateLabel.text = LocString("深睡") case .light: dateLabel.text = LocString("浅睡") } lengthLabel.toTimeType1(length: points[index].length) let sleep = SleepModel.querySleepPercentage(sleepArray) awakeLabel.text = "\(sleep.awake_pct)" deepLabel.text = "\(sleep.deep_pct)" lightLabel.text = "\(sleep.light_pct)" sleepLabel.toTimeType2(length: sleep.length) tLength = sleep.length } else { if sleepSummaryArray.count == 0 { resetLabel() return } let sleep = sleepSummaryArray[index] let sDate = sleep.endDate!.date switch dateType { case .day: break case .week: dateLabel.text = sDate.toString(.custom("yyyy.MM.dd")) + " " + sDate.weekText case .month: dateLabel.text = sDate.toString(.custom("yyyy.MM.dd")) case .year: dateLabel.text = sDate.toString(.custom("yyyy.MM")) + " " + LocString("日均") } lengthLabel.toTimeType1(length: sleep.sleepLength) deepLabel.text = "\(sleep.deep_pct)" lightLabel.text = "\(sleep.light_pct)" awakeLabel.text = "\(sleep.awake_pct)" sleepLabel.toTimeType2(length: sleep.sleepLength) tLength = sleep.sleepLength } var per = "" switch tLength { case 0..<60: per = String(format:"%.2f",Float(tLength)/10) case 60..<120: per = String(format:"%.2f",10+Float(tLength-60)/15*2.0) case 120..<240: per = String(format:"%.2f",18+Float(tLength-120)/10) case 240..<300: per = String(format:"%.2f",30+Float(tLength-240)/30*7.0) case 300..<420: per = String(format:"%.2f",54+Float(tLength-300)/5) case 420..<540: per = String(format:"%.2f",78+Float(tLength-420)/10) case 540..<960: per = String(format:"%.2f",90+Float(tLength-540)/20) case 960..<2000: per = "99" default: break } analysisLabel.text = LocString("亲,您的睡眠健康指数已击败全国") + "\(per)%" + LocString("的同龄人") } private func updateChartView() { let xAxis = barChartView.xAxis switch dateType { case .day: xAxis.valueFormatter = IndexAxisValueFormatter(values: DayXValues) xAxis.labelCount = 2 if sleepArray.count > 0 { xAxis.axisMinimum = -0.8 xAxis.axisMaximum = 11.5 } else { xAxis.axisMinimum = -2.9 xAxis.axisMaximum = 24 + 2 } case .week: xAxis.valueFormatter = IndexAxisValueFormatter(values: WeekXValues) xAxis.labelCount = 7 xAxis.axisMinimum = -0.8 xAxis.axisMaximum = 6 + 0.8 case .month: xAxis.valueFormatter = IndexAxisValueFormatter(values: MonthXValues(monthDays)) xAxis.labelCount = 8 xAxis.axisMinimum = -3.5 xAxis.axisMinLabels = 8 xAxis.axisMaximum = Double(monthDays-1) + 2.1 case .year: xAxis.valueFormatter = IndexAxisValueFormatter(values: MonthValues) xAxis.labelCount = 12 xAxis.axisMinimum = -1.3 xAxis.axisMaximum = 11 + 0.8 } if dateType == .day { barChartView.leftAxis.valueFormatter = IndexAxisValueFormatter(values: [LocString("清醒"), LocString("浅睡"), LocString("深睡")]) barChartView.leftAxis.yOffset = -30 barChartView.leftAxis.axisMaximum = 3 var awakeEntries = [BarChartDataEntry]() var deepEntries = [BarChartDataEntry]() var lightEntries = [BarChartDataEntry]() var emptyEntries = [BarChartDataEntry]() var startPoint: Double = 0 var sleepXValues: [String] = [] if let startDate = sleepArray.first?.startDate, let endDate = sleepArray.last?.endDate { startPoint = Double(startDate.hour) + Double(startDate.minute)/60.0 if startPoint > 20 { startPoint = startPoint - 24 } var endPoint = Double(endDate.hour) + Double(endDate.minute)/60.0 if endPoint > 20 { endPoint = endPoint - 24 } xAxis.labelCount = 2 xAxis.axisMinimum = -0.8012345 xAxis.axisMaximum = (endPoint - startPoint) + 0.5 xAxis.valueFormatter = CDTimeAxisValueFormatter(min: -0.8012345, max: (endPoint - startPoint) + 0.5, labelCount: 2, values: [startDate.toString(.custom("HH:mm")), endDate.toString(.custom("HH:mm"))]) } else { xAxis.labelCount = 2 xAxis.axisMinimum = -0.8012345 xAxis.axisMaximum = 11.5 xAxis.valueFormatter = CDTimeAxisValueFormatter(min: -0.8012345, max: 11.5, labelCount: 2, values: ["22:00", "09:00"]) } for sleep in sleepArray { var startX = Double(sleep.startDate!.hour) + Double(sleep.startDate!.minute)/60.0 if startX > 20 { startX = startX - 24 } startX = startX - startPoint for n in 0.. 0 { var pointX: Double = 0 switch dateType { case .day: break case .week: barWidth = 0.25 pointX = Double(sleep.endDate!.weekIndex) case .month: pointX = Double(sleep.endDate!.day-1) case .year: barWidth = 0.4 pointX = Double(sleep.endDate!.month-1) } points.append(SleepPoint(point: pointX, type: .awake, length: 0)) dataEntries.append(BarChartDataEntry(x: pointX, y: Double(sleep.light+sleep.deep)/60)) } } let dataSet = BarChartDataSet(entries: dataEntries) dataSet.colors = [UIColor.rgbColorFromHex(0x9733ED)] dataSet.highlightEnabled = true dataSet.highlightColor = UIColor.rgbColorFromHex(0x9B0AFB) dataSet.drawValuesEnabled = false let chartData = BarChartData(dataSets: [dataSet]) chartData.barWidth = barWidth barChartView.data = chartData } barChartView.highlightValues([Highlight(x: 0, y: 0, dataSetIndex: 0)]) setupSliderViewScale() } private func setupSliderViewScale() { var endPointX: Int = 0 switch dateType { case .day: endPointX = 9 case .week: endPointX = 6 case .month: endPointX = monthDays-1 case .year: endPointX = 11 } let pointArray = (0.. Double in return points[i].point } sliderView.setDrawSpace(startPointX: getChartViewX(0), endPointX: getChartViewX(endPointX), range: 0...endPointX, points: pointArray, imageName: "slider_sleep") } private func getChartViewX(_ x: Int) -> CGFloat { return barChartView.pixelForValues(x: Double(x), y: 0, axis: .left).x } private func setupChartView() { barChartView.delegate = self barChartView.chartDescription?.enabled = false //图描述 barChartView.legend.enabled = false //左下角图例 barChartView.setScaleEnabled(false) //可滑动 barChartView.rightAxis.enabled = false //不绘制右边轴的信息 let leftAxis = barChartView.leftAxis leftAxis.labelTextColor = ChartsTextColor leftAxis.labelFont = ChartsTextFont(11) leftAxis.xOffset = -3 leftAxis.gridLineDashLengths = [2.0, 2.0] //设置虚线样式的网格线 leftAxis.gridColor = LineColor leftAxis.gridLineWidth = 1 leftAxis.axisLineWidth = 0 leftAxis.drawGridLinesBehindDataEnabled = false leftAxis.labelPosition = .insideChart leftAxis.axisMinimum = 0 //设置左侧Y轴最小值 leftAxis.granularity = 1 let litmitLine = ChartLimitLine(limit: 0, label: "") litmitLine.lineWidth = 1 litmitLine.lineColor = LineColor leftAxis.addLimitLine(litmitLine) leftAxis.drawLimitLinesBehindDataEnabled = false //设置限制线绘制在折线图的后面 let xAxis = barChartView.xAxis xAxis.granularity = 1 //间隔 xAxis.labelPosition = .bottom xAxis.labelFont = ChartsTextFont(11) xAxis.labelTextColor = ChartsTextColor xAxis.drawGridLinesBehindDataEnabled = false xAxis.axisLineColor = LineColor xAxis.axisLineWidth = 1 xAxis.gridLineDashLengths = [6, 666] xAxis.gridColor = LineColor xAxis.drawAxisLineEnabled = false } @IBAction func back(_ sender: Any) { navigationController?.popViewController(animated: true) } @IBAction func gotoReferenceVC(_ sender: Any) { let vc = UIStoryboard.loadViewControllerIdentifier(storyboardName: "Home", identifier: "SleepReferenceVC") navigationController?.pushViewController(vc, animated: true) } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. } */ } class CDTimeAxisValueFormatter: NSObject, IAxisValueFormatter { private var min: Double = 0 private var max: Double = 0 private var values = [String]() private var labelCount: Int = 0 convenience init(min: Double, max: Double, labelCount: Int, values: [String]){ self.init() self.min = min self.max = max self.values = values self.labelCount = labelCount } func stringForValue(_ value: Double, axis: AxisBase?) -> String { let granularity = (max-min)/Double(labelCount-1) let index = Int(((value-min)/granularity).rounded()) guard index < values.count else { return "" } return values[index] } }