diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 3ca077d245..0c2f9a8d52 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -124,8 +124,9 @@ 43E344A61B9E1B3300C85C07 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E344A51B9E1B3300C85C07 /* NSBundle.swift */; }; 43E344A81B9EAA8400C85C07 /* RemoteSettings.plist in Resources */ = {isa = PBXBuildFile; fileRef = 43E344A71B9EAA8300C85C07 /* RemoteSettings.plist */; }; 43EB40861C82646A00472A8C /* StatusChartManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EB40851C82646A00472A8C /* StatusChartManager.swift */; }; - 43EB40C21C83F84900472A8C /* StatusChartHighlightLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EB40C11C83F84900472A8C /* StatusChartHighlightLayer.swift */; }; 43EDDBF31C361C75007D89B5 /* xDripG5.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43EDDBF11C361C75007D89B5 /* xDripG5.framework */; }; + 43F41C331D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F41C321D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift */; }; + 43F41C351D3B623800C11ED6 /* ChartPointsTouchHighlightLayerViewCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F41C341D3B623800C11ED6 /* ChartPointsTouchHighlightLayerViewCache.swift */; }; 43F4EF1D1BA2A57600526CE1 /* DiagnosticLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F4EF1C1BA2A57600526CE1 /* DiagnosticLogger.swift */; }; 43F5C2C91B929C09003EB13D /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F5C2C81B929C09003EB13D /* HealthKit.framework */; }; 43F5C2DB1B92A5E1003EB13D /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F5C2DA1B92A5E1003EB13D /* SettingsTableViewController.swift */; }; @@ -331,12 +332,13 @@ 43E344A51B9E1B3300C85C07 /* NSBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSBundle.swift; sourceTree = ""; }; 43E344A71B9EAA8300C85C07 /* RemoteSettings.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = RemoteSettings.plist; sourceTree = ""; }; 43EB40851C82646A00472A8C /* StatusChartManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusChartManager.swift; sourceTree = ""; }; - 43EB40C11C83F84900472A8C /* StatusChartHighlightLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusChartHighlightLayer.swift; sourceTree = ""; }; 43EDDBF01C361C75007D89B5 /* CommonCrypto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CommonCrypto.framework; path = Carthage/Build/iOS/CommonCrypto.framework; sourceTree = ""; }; 43EDDBF11C361C75007D89B5 /* xDripG5.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = xDripG5.framework; path = Carthage/Build/iOS/xDripG5.framework; sourceTree = ""; }; 43EDDBF51C361D24007D89B5 /* CommonCrypto.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = CommonCrypto.framework.dSYM; path = Carthage/Build/iOS/CommonCrypto.framework.dSYM; sourceTree = ""; }; 43EDDBF61C361D24007D89B5 /* xDripG5.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = xDripG5.framework.dSYM; path = Carthage/Build/iOS/xDripG5.framework.dSYM; sourceTree = ""; }; 43EDEE6B1CF2E12A00393BE3 /* Loop.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Loop.entitlements; sourceTree = ""; }; + 43F41C321D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisValueDoubleUnit.swift; sourceTree = ""; }; + 43F41C341D3B623800C11ED6 /* ChartPointsTouchHighlightLayerViewCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointsTouchHighlightLayerViewCache.swift; sourceTree = ""; }; 43F4EF1C1BA2A57600526CE1 /* DiagnosticLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiagnosticLogger.swift; sourceTree = ""; }; 43F5C2C81B929C09003EB13D /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 43F5C2D41B92A4A6003EB13D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -434,6 +436,7 @@ 43DE92601C555C26001FFDE1 /* AbsorptionTimeType+CarbKit.swift */, 438849EB1D29EC34003B3F23 /* AmplitudeService.swift */, 4331E0791C85650D00FBE832 /* ChartAxisValueDoubleLog.swift */, + 43F41C321D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift */, 434F24CA1CFCB7AB0004498F /* GlucoseRxMessage.swift */, 436FACED1D0BA636004E2427 /* InsulinDataSource.swift */, 436A0DA41D236A2A00104B24 /* LoopError.swift */, @@ -611,13 +614,13 @@ 437CEEC91CD84DB7003C8C80 /* BatteryLevelHUDView.swift */, 437CCADB1D284B830075D2C3 /* ButtonTableViewCell.swift */, 434F54581D28805E002A9274 /* ButtonTableViewCell.xib */, + 43F41C341D3B623800C11ED6 /* ChartPointsTouchHighlightLayerViewCache.swift */, 4346D1E61C77F5FE00ABAFE3 /* ChartTableViewCell.swift */, 437CEEBB1CD6DE6A003C8C80 /* HUDView.swift */, 437CEEBD1CD6E0CB003C8C80 /* LoopCompletionHUDView.swift */, 438DADC71CDE8F8B007697A5 /* LoopStateView.swift */, 437CEEC71CD84CBB003C8C80 /* ReservoirVolumeHUDView.swift */, 436FACEB1D0BA246004E2427 /* SegmentedControlTableViewCell.swift */, - 43EB40C11C83F84900472A8C /* StatusChartHighlightLayer.swift */, 43A5676A1C96155700334FAC /* SwitchTableViewCell.swift */, 434F54621D28DD80002A9274 /* ValidatingIndicatorView.swift */, ); @@ -953,6 +956,7 @@ 43776F901B8022E90074EA36 /* AppDelegate.swift in Sources */, 437CCADA1D284ADF0075D2C3 /* AuthenticationTableViewCell.swift in Sources */, 43CE7CDE1CA8B63E003CC1B0 /* NSDate.swift in Sources */, + 43F41C331D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift in Sources */, 43F5C2DB1B92A5E1003EB13D /* SettingsTableViewController.swift in Sources */, 43B371861CE583890013C5A6 /* BasalStateView.swift in Sources */, 434FF1EA1CF26C29000DB779 /* IdentifiableClass.swift in Sources */, @@ -972,12 +976,12 @@ 436FACEC1D0BA246004E2427 /* SegmentedControlTableViewCell.swift in Sources */, 43DBF0591C93F73800B3C386 /* CarbEntryTableViewController.swift in Sources */, 43DE925C1C547A20001FFDE1 /* WatchContext.swift in Sources */, + 43F41C351D3B623800C11ED6 /* ChartPointsTouchHighlightLayerViewCache.swift in Sources */, 43EB40861C82646A00472A8C /* StatusChartManager.swift in Sources */, 437CEEC81CD84CBB003C8C80 /* ReservoirVolumeHUDView.swift in Sources */, 43C0944A1CACCC73001F6403 /* NotificationManager.swift in Sources */, 434FF1EE1CF27EEF000DB779 /* UITableViewCell.swift in Sources */, 438849EA1D297CB6003B3F23 /* NightscoutService.swift in Sources */, - 43EB40C21C83F84900472A8C /* StatusChartHighlightLayer.swift in Sources */, 437CCADC1D284B830075D2C3 /* ButtonTableViewCell.swift in Sources */, 4315D2871CA5CC3B00589052 /* CarbEntryEditTableViewController.swift in Sources */, 4331E0781C85302200FBE832 /* CGPoint.swift in Sources */, diff --git a/Loop/Managers/StatusChartManager.swift b/Loop/Managers/StatusChartManager.swift index c664c218c2..02535069a0 100644 --- a/Loop/Managers/StatusChartManager.swift +++ b/Loop/Managers/StatusChartManager.swift @@ -73,23 +73,31 @@ class StatusChartsManager { var glucoseValues: [GlucoseValue] = [] { didSet { - glucosePoints = glucoseValues.map({ + // TODO: Use the preferred unit + let unit = HKUnit.milligramsPerDeciliterUnit() + let unitString = unit.glucoseUnitDisplayString + + glucosePoints = glucoseValues.map { return ChartPoint( x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter), - y: ChartAxisValueDouble($0.quantity.doubleValueForUnit(HKUnit.milligramsPerDeciliterUnit())) + y: ChartAxisValueDoubleUnit($0.quantity.doubleValueForUnit(unit), unitString: unitString) ) - }) + } } } var predictedGlucoseValues: [GlucoseValue] = [] { didSet { - predictedGlucosePoints = predictedGlucoseValues.map({ + // TODO: Use the preferred unit + let unit = HKUnit.milligramsPerDeciliterUnit() + let unitString = unit.glucoseUnitDisplayString + + predictedGlucosePoints = predictedGlucoseValues.map { return ChartPoint( x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter), - y: ChartAxisValueDouble($0.quantity.doubleValueForUnit(HKUnit.milligramsPerDeciliterUnit()), formatter: integerFormatter) + y: ChartAxisValueDoubleUnit($0.quantity.doubleValueForUnit(unit), unitString: unitString, formatter: integerFormatter) ) - }) + } } } @@ -98,7 +106,7 @@ class StatusChartsManager { IOBPoints = IOBValues.map { return ChartPoint( x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter), - y: ChartAxisValueDouble($0.value, formatter: decimalFormatter) + y: ChartAxisValueDoubleUnit($0.value, unitString: "U", formatter: decimalFormatter) ) } } @@ -106,10 +114,13 @@ class StatusChartsManager { var COBValues: [CarbValue] = [] { didSet { + let unit = HKUnit.gramUnit() + let unitString = unit.unitString + COBPoints = COBValues.map { ChartPoint( x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter), - y: ChartAxisValueDouble($0.quantity.doubleValueForUnit(HKUnit.gramUnit()), formatter: integerFormatter) + y: ChartAxisValueDoubleUnit($0.quantity.doubleValueForUnit(unit), unitString: unitString, formatter: integerFormatter) ) } } @@ -122,7 +133,7 @@ class StatusChartsManager { let startX = ChartAxisValueDate(date: entry.startDate, formatter: dateFormatter) let endX = ChartAxisValueDate(date: entry.endDate, formatter: dateFormatter) let zero = ChartAxisValueInt(0) - let value = ChartAxisValueDoubleLog(actualDouble: entry.value, formatter: decimalFormatter) + let value = ChartAxisValueDoubleLog(actualDouble: entry.value, unitString: "U/hour", formatter: decimalFormatter) let newPoints = [ ChartPoint(x: startX, y: zero), @@ -214,6 +225,14 @@ class StatusChartsManager { private var doseChart: Chart? + private var glucoseChartCache: ChartPointsTouchHighlightLayerViewCache? + + private var IOBChartCache: ChartPointsTouchHighlightLayerViewCache? + + private var COBChartCache: ChartPointsTouchHighlightLayerViewCache? + + private var doseChartCache: ChartPointsTouchHighlightLayerViewCache? + // MARK: - Generators func glucoseChartWithFrame(frame: CGRect) -> Chart? { @@ -275,7 +294,7 @@ class StatusChartsManager { prediction = ChartPointsScatterCirclesLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, chartPoints: predictedGlucosePoints, displayDelay: 0, itemSize: CGSize(width: 2, height: 2), itemFillColor: UIColor.glucoseTintColor.colorWithAlphaComponent(0.75)) } - let highlightLayer = StatusChartHighlightLayer( + glucoseChartCache = ChartPointsTouchHighlightLayerViewCache( xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, @@ -292,7 +311,7 @@ class StatusChartsManager { targetOverrideDurationLayer, xAxis, yAxis, - highlightLayer, + glucoseChartCache?.highlightLayer, prediction, circles ] @@ -356,7 +375,7 @@ class StatusChartsManager { return v }) - let highlightLayer = StatusChartHighlightLayer( + IOBChartCache = ChartPointsTouchHighlightLayerViewCache( xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, @@ -371,7 +390,7 @@ class StatusChartsManager { xAxis, yAxis, zeroGuidelineLayer, - highlightLayer, + IOBChartCache?.highlightLayer, IOBArea, IOBLine, ] @@ -425,7 +444,7 @@ class StatusChartsManager { let gridLayer = ChartGuideLinesLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, axis: .XAndY, settings: guideLinesLayerSettings, onlyVisibleX: true, onlyVisibleY: false) - let highlightLayer = StatusChartHighlightLayer( + COBChartCache = ChartPointsTouchHighlightLayerViewCache( xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, @@ -439,7 +458,7 @@ class StatusChartsManager { gridLayer, xAxis, yAxis, - highlightLayer, + COBChartCache?.highlightLayer, COBArea, COBLine ] @@ -492,7 +511,7 @@ class StatusChartsManager { return v }) - let highlightLayer = StatusChartHighlightLayer( + doseChartCache = ChartPointsTouchHighlightLayerViewCache( xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, @@ -507,7 +526,7 @@ class StatusChartsManager { xAxis, yAxis, zeroGuidelineLayer, - highlightLayer, + doseChartCache?.highlightLayer, doseArea, doseLine ] diff --git a/Loop/Models/ChartAxisValueDoubleLog.swift b/Loop/Models/ChartAxisValueDoubleLog.swift index a1d28e4794..8e413b926c 100644 --- a/Loop/Models/ChartAxisValueDoubleLog.swift +++ b/Loop/Models/ChartAxisValueDoubleLog.swift @@ -12,7 +12,9 @@ import SwiftCharts class ChartAxisValueDoubleLog: ChartAxisValueDoubleScreenLoc { - init(actualDouble: Double, formatter: NSNumberFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { + let unitString: String? + + init(actualDouble: Double, unitString: String? = nil, formatter: NSNumberFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { let screenLocDouble: Double switch actualDouble { @@ -24,6 +26,8 @@ class ChartAxisValueDoubleLog: ChartAxisValueDoubleScreenLoc { screenLocDouble = 0 } + self.unitString = unitString + super.init(screenLocDouble: screenLocDouble, actualDouble: actualDouble, formatter: formatter, labelSettings: labelSettings) } @@ -39,7 +43,14 @@ class ChartAxisValueDoubleLog: ChartAxisValueDoubleScreenLoc { actualDouble = 0 } + self.unitString = nil + super.init(screenLocDouble: screenLocDouble, actualDouble: actualDouble, formatter: formatter, labelSettings: labelSettings) } + override var description: String { + let suffix = unitString != nil ? " \(unitString!)" : "" + + return super.description + suffix + } } diff --git a/Loop/Models/ChartAxisValueDoubleUnit.swift b/Loop/Models/ChartAxisValueDoubleUnit.swift new file mode 100644 index 0000000000..3f0052797b --- /dev/null +++ b/Loop/Models/ChartAxisValueDoubleUnit.swift @@ -0,0 +1,31 @@ +// +// ChartAxisValueDoubleUnit.swift +// Loop +// +// Created by Nate Racklyeft on 7/16/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import UIKit +import SwiftCharts + + +class ChartAxisValueDoubleUnit: ChartAxisValueDouble { + let unitString: String + + init(_ double: Double, unitString: String, formatter: NSNumberFormatter) { + self.unitString = unitString + + super.init(double, formatter: formatter) + } + + init(_ double: Double, unitString: String) { + self.unitString = unitString + + super.init(double) + } + + override var description: String { + return "\(super.description) \(unitString)" + } +} diff --git a/Loop/Views/ChartPointsTouchHighlightLayerViewCache.swift b/Loop/Views/ChartPointsTouchHighlightLayerViewCache.swift new file mode 100644 index 0000000000..cf188d6403 --- /dev/null +++ b/Loop/Views/ChartPointsTouchHighlightLayerViewCache.swift @@ -0,0 +1,112 @@ +// +// StatusChartHighlightLayer.swift +// Naterade +// +// Created by Nathan Racklyeft on 2/28/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation +import SwiftCharts + + +class ChartPointsTouchHighlightLayerViewCache { + private lazy var containerView = UIView(frame: .zero) + + private lazy var xAxisOverlayView = UIView() + + private lazy var point = ChartPointEllipseView(center: .zero, diameter: 16) + + private lazy var labelY: UILabel = { + let label = UILabel() + label.font = UIFont.monospacedDigitSystemFontOfSize(15, weight: UIFontWeightBold) + label.textAlignment = .Center + label.backgroundColor = UIColor.whiteColor() + + return label + }() + + private lazy var labelX: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1) + label.textColor = UIColor.secondaryLabelColor + + return label + }() + + private(set) var highlightLayer: ChartPointsTouchHighlightLayer! + + init(xAxis: ChartAxisLayer, yAxis: ChartAxisLayer, innerFrame: CGRect, chartPoints: [ChartPoint], tintColor: UIColor, labelCenterY: CGFloat, gestureRecognizer: UIPanGestureRecognizer? = nil) { + + highlightLayer = ChartPointsTouchHighlightLayer( + xAxis: xAxis, + yAxis: yAxis, + innerFrame: innerFrame, + chartPoints: chartPoints, + gestureRecognizer: gestureRecognizer, + modelFilter: { (screenLoc, chartPointModels) -> ChartPointLayerModel? in + if let index = chartPointModels.map({ $0.screenLoc.x }).findClosestElementIndexToValue(screenLoc.x) { + return chartPointModels[index] + } else { + return nil + } + }, + viewGenerator: { [unowned self] (chartPointModel, layer, chart) -> UIView? in + let containerView = self.containerView + containerView.frame = chart.bounds + containerView.alpha = 1 // This is animated to 0 when touch last ended + + let xAxisOverlayView = self.xAxisOverlayView + if xAxisOverlayView.superview == nil { + xAxisOverlayView.frame = xAxis.rect.offsetBy(dx: 0, dy: 1) + xAxisOverlayView.backgroundColor = UIColor.whiteColor() + xAxisOverlayView.opaque = true + containerView.addSubview(xAxisOverlayView) + } + + let point = self.point + point.center = chartPointModel.screenLoc + if point.superview == nil { + point.fillColor = tintColor.colorWithAlphaComponent(0.5) + containerView.addSubview(point) + } + + if let text = chartPointModel.chartPoint.y.labels.first?.text { + let label = self.labelY + + label.text = text + label.sizeToFit() + label.frame.size.height += 4 + label.frame.size.width += label.frame.size.height / 2 + label.center.y = innerFrame.origin.y - 1 + label.center.x = chartPointModel.screenLoc.x + label.frame.origin.x = min(max(label.frame.origin.x, innerFrame.origin.x), innerFrame.maxX - label.frame.size.width) + label.frame.origin.makeIntegralInPlaceWithDisplayScale(chart.view.traitCollection.displayScale) + label.layer.cornerRadius = label.frame.size.height / 2 + label.layer.borderWidth = 1 / max(1, chart.view.traitCollection.displayScale) + + if label.superview == nil { + label.textColor = tintColor + label.layer.borderColor = tintColor.CGColor + + containerView.addSubview(label) + } + } + + if let text = chartPointModel.chartPoint.x.labels.first?.text { + let label = self.labelX + label.text = text + label.sizeToFit() + label.center = CGPoint(x: chartPointModel.screenLoc.x, y: xAxisOverlayView.center.y) + label.frame.origin.makeIntegralInPlaceWithDisplayScale(chart.view.traitCollection.displayScale) + + if label.superview == nil { + containerView.addSubview(label) + } + } + + return containerView + } + ) + } +} diff --git a/Loop/Views/StatusChartHighlightLayer.swift b/Loop/Views/StatusChartHighlightLayer.swift deleted file mode 100644 index 914dc6ad69..0000000000 --- a/Loop/Views/StatusChartHighlightLayer.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// StatusChartHighlightLayer.swift -// Naterade -// -// Created by Nathan Racklyeft on 2/28/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import Foundation -import SwiftCharts - - -class StatusChartHighlightLayer: ChartPointsTouchHighlightLayer { - init( - xAxis: ChartAxisLayer, - yAxis: ChartAxisLayer, - innerFrame: CGRect, - chartPoints: [T], - tintColor: UIColor, - labelCenterY: CGFloat = 0, - gestureRecognizer: UIPanGestureRecognizer? = nil - ) { - super.init(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, chartPoints: chartPoints, gestureRecognizer: gestureRecognizer, - modelFilter: { (screenLoc, chartPointModels) -> ChartPointLayerModel? in - if let index = chartPointModels.map({ $0.screenLoc.x }).findClosestElementIndexToValue(screenLoc.x) { - return chartPointModels[index] - } else { - return nil - } - }, - viewGenerator: { (chartPointModel, layer, chart) -> U? in - let containerView = U(frame: chart.bounds) - - let xAxisOverlayView = UIView(frame: xAxis.rect.offsetBy(dx: 0, dy: 1)) - xAxisOverlayView.backgroundColor = UIColor.whiteColor() - xAxisOverlayView.opaque = true - containerView.addSubview(xAxisOverlayView) - - let point = ChartPointEllipseView(center: chartPointModel.screenLoc, diameter: 16) - point.fillColor = tintColor.colorWithAlphaComponent(0.5) - containerView.addSubview(point) - - if let text = chartPointModel.chartPoint.y.labels.first?.text { - let label = UILabel() - label.font = UIFont.monospacedDigitSystemFontOfSize(15, weight: UIFontWeightBold) - - label.text = text - label.textColor = tintColor - label.textAlignment = .Center - label.sizeToFit() - label.frame.size.height += 4 - label.frame.size.width += label.frame.size.height / 2 - label.center.y = innerFrame.origin.y - 1 - label.center.x = chartPointModel.screenLoc.x - label.frame.origin.x = min(max(label.frame.origin.x, innerFrame.origin.x), innerFrame.maxX - label.frame.size.width) - label.frame.origin.makeIntegralInPlaceWithDisplayScale(chart.view.traitCollection.displayScale) - label.layer.borderColor = tintColor.CGColor - label.layer.borderWidth = 1 / chart.view.traitCollection.displayScale - label.layer.cornerRadius = label.frame.size.height / 2 - label.backgroundColor = UIColor.whiteColor() - - containerView.addSubview(label) - } - - if let text = chartPointModel.chartPoint.x.labels.first?.text { - let label = UILabel() - label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1) - label.text = text - label.textColor = UIColor.secondaryLabelColor - label.sizeToFit() - label.center = CGPoint(x: chartPointModel.screenLoc.x, y: xAxisOverlayView.center.y) - label.frame.origin.makeIntegralInPlaceWithDisplayScale(chart.view.traitCollection.displayScale) - - containerView.addSubview(label) - } - - return containerView - } - ) - - } -}