Adds History Graph for channels
This commit is contained in:
@@ -66,9 +66,16 @@ class ChannelDetailViewController: UIViewController, UITableViewDelegate, UITabl
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func writeButtonPressed(_ sender: UIButton) {
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "openHistoryGraph" {
|
||||
let destinationVC = segue.destination as! HistoryGraphViewController
|
||||
destinationVC.channelHistory = channelHistory
|
||||
destinationVC.user = user
|
||||
destinationVC.thisChannel = thisChannel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - TABLE METHODS
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
@@ -25,12 +25,23 @@ class DeviceDetailViewController: UITableViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
loadData()
|
||||
self.navigationItem.title = thisDevice?.vanityName
|
||||
tableView.estimatedRowHeight = 100
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
|
||||
SVProgressHUD.show()
|
||||
firstly {
|
||||
loadData()
|
||||
}.done { _ in
|
||||
self.tableView.reloadData()
|
||||
}.catch{ error in
|
||||
print("Error in loadData: \(error)")
|
||||
}
|
||||
|
||||
self.navigationItem.title = thisDevice?.vanityName
|
||||
|
||||
self.refreshControl = UIRefreshControl()
|
||||
refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
|
||||
refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
|
||||
// tableView.addSubview(refreshControl!)
|
||||
|
||||
firstly {
|
||||
getChannels(deviceTypeId: thisDevice!.deviceTypeId)
|
||||
}.then { _ in
|
||||
@@ -38,11 +49,27 @@ class DeviceDetailViewController: UITableViewController {
|
||||
}.done { _ in
|
||||
self.tableView.reloadData()
|
||||
SVProgressHUD.dismiss()
|
||||
self.refreshControl?.endRefreshing()
|
||||
}.catch { error in
|
||||
print("Error fetching data in DeviceDetailViewController: \(error)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
SVProgressHUD.show()
|
||||
firstly {
|
||||
getChannels(deviceTypeId: thisDevice!.deviceTypeId)
|
||||
}.then { _ in
|
||||
self.getChannelValues(deviceId: self.thisDevice!.id)
|
||||
}.done { _ in
|
||||
self.tableView.reloadData()
|
||||
SVProgressHUD.dismiss()
|
||||
self.refreshControl?.endRefreshing()
|
||||
}.catch { error in
|
||||
print("Error fetching data in DeviceDetailViewController: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -172,10 +199,12 @@ class DeviceDetailViewController: UITableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
deviceTypes = realm.objects(DeviceType.self)
|
||||
thisDevice = realm.objects(Device.self).filter("id == %d", thisDevice!.id).first!
|
||||
tableView.reloadData()
|
||||
func loadData() -> Promise<Void> {
|
||||
return Promise { promise in
|
||||
deviceTypes = realm.objects(DeviceType.self)
|
||||
thisDevice = realm.objects(Device.self).filter("id == %d", thisDevice!.id).first!
|
||||
promise.fulfill(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,31 +28,60 @@ class DeviceListViewController: UITableViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// self.refreshControl = UIRefreshControl()
|
||||
// refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
|
||||
// refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
|
||||
|
||||
SVProgressHUD.show()
|
||||
loadData()
|
||||
firstly {
|
||||
getDeviceTypes()
|
||||
}
|
||||
.then { _ in
|
||||
self.getAddresses()
|
||||
}.then { _ in
|
||||
self.getGateways()
|
||||
}.then { _ in
|
||||
self.getDevices()
|
||||
self.loadRealmData()
|
||||
}.then{ _ in
|
||||
self.loadJSONData()
|
||||
}.done { _ in
|
||||
SVProgressHUD.dismiss()
|
||||
self.tableView.reloadData()
|
||||
SVProgressHUD.dismiss()
|
||||
}.catch { error in
|
||||
print("Error in getting data in DeviceListViewController: \(error)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
|
||||
func loadJSONData() -> Promise<Void>{
|
||||
return Promise { promise in
|
||||
firstly {
|
||||
self.getDeviceTypes()
|
||||
}.then { _ in
|
||||
self.getAddresses()
|
||||
}.then { _ in
|
||||
self.getGateways()
|
||||
}.then { _ in
|
||||
self.getDevices()
|
||||
}.done{ _ in
|
||||
promise.fulfill(())
|
||||
}.catch { error in
|
||||
promise.reject(error)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// @objc func refresh() {
|
||||
//
|
||||
// SVProgressHUD.show()
|
||||
// self.realm.refresh()
|
||||
// firstly {
|
||||
// self.loadJSONData()
|
||||
// }.done { _ in
|
||||
// self.tableView.reloadData()
|
||||
// self.refreshControl?.endRefreshing()
|
||||
// SVProgressHUD.dismiss()
|
||||
// }.catch { error in
|
||||
// print("Error in getting data in DeviceListViewController: \(error)")
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
@@ -97,6 +126,7 @@ class DeviceListViewController: UITableViewController {
|
||||
performSegue(withIdentifier: "openDeviceDetailView", sender: self)
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Data Methods
|
||||
|
||||
func getAddresses() ->Promise<Any> {
|
||||
@@ -284,12 +314,14 @@ class DeviceListViewController: UITableViewController {
|
||||
|
||||
//MARK: - Realm Functions
|
||||
|
||||
func loadData() {
|
||||
devices = realm.objects(Device.self)
|
||||
deviceTypes = realm.objects(DeviceType.self)
|
||||
gateways = realm.objects(Gateway.self)
|
||||
addresses = realm.objects(Address.self)
|
||||
tableView.reloadData()
|
||||
func loadRealmData() -> Promise<Void> {
|
||||
return Promise { promise in
|
||||
devices = realm.objects(Device.self)
|
||||
deviceTypes = realm.objects(DeviceType.self)
|
||||
gateways = realm.objects(Gateway.self)
|
||||
addresses = realm.objects(Address.self)
|
||||
promise.fulfill(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
115
pocloud/Controller/HistoryGraphViewController.swift
Normal file
115
pocloud/Controller/HistoryGraphViewController.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// HistoryGraphViewController.swift
|
||||
// pocloud
|
||||
//
|
||||
// Created by Patrick McDonagh on 5/29/18.
|
||||
// Copyright © 2018 patrickjmcd. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftChart
|
||||
|
||||
class HistoryGraphViewController: UIViewController, ChartDelegate{
|
||||
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var lineChart: Chart!
|
||||
@IBOutlet weak var label: UILabel!
|
||||
var thisChannel : Channel?
|
||||
var channelHistory : [ChannelHistoryValue] = [ChannelHistoryValue]()
|
||||
var user : User?
|
||||
var dateFormatter = DateFormatter()
|
||||
|
||||
let channelDataTypes: [Int : String] = [
|
||||
0: "Unknown",
|
||||
1: "Float",
|
||||
2: "String",
|
||||
3: "Integer",
|
||||
4: "Boolean",
|
||||
5: "DateTime",
|
||||
6: "Timespan",
|
||||
7 :"File",
|
||||
8: "LatLng"
|
||||
]
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
lineChart.delegate = self
|
||||
loadChartData()
|
||||
}
|
||||
|
||||
func loadChartData() {
|
||||
switch channelDataTypes[(thisChannel?.dataType)!] {
|
||||
case "Float", "Integer":
|
||||
print("Float or Integer")
|
||||
let seriesData : [(x: Int, y: Double)] = channelHistory.map { (chHist) -> (x: Int, y: Double) in
|
||||
(x: Int(chHist.timestamp!.timeIntervalSince1970), y:Double(chHist.value)!)
|
||||
}
|
||||
lineChart.add(ChartSeries(data: seriesData))
|
||||
setupLineChart()
|
||||
|
||||
|
||||
default:
|
||||
let alert = UIAlertController(title: "Invalid Type", message: "This channel is of type \(channelDataTypes[(thisChannel?.channelType)!] ?? "ERROR") and cannot be graphed yet.", preferredStyle: .alert)
|
||||
let ok = UIAlertAction(title: "OK", style: .cancel, handler: { _ in
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
})
|
||||
alert.addAction(ok)
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
func setupLineChart() {
|
||||
let xLabelMax = lineChart.series.first?.data.first?.x
|
||||
let xLabelMin = lineChart.series.first?.data.last?.x
|
||||
|
||||
let xLabelDoubles = [0.0, 0.25, 0.5, 0.75, 1.0].map { (multiplier) -> Double in
|
||||
xLabelMin! + (xLabelMax! - xLabelMin!) * multiplier
|
||||
}
|
||||
|
||||
lineChart.xLabels = xLabelDoubles
|
||||
self.dateFormatter.dateFormat = "HH:mm:ss"
|
||||
lineChart.xLabelsFormatter = { (i, d) -> String in
|
||||
self.dateFormatter.string(from: Date(timeIntervalSince1970: d))
|
||||
}
|
||||
lineChart.bottomInset = 40
|
||||
lineChart.series.first?.area = true
|
||||
}
|
||||
|
||||
func didTouchChart(_ chart: Chart, indexes: Array<Int?>, x: Double, left: CGFloat) {
|
||||
for (seriesIndex, dataIndex) in indexes.enumerated() {
|
||||
if dataIndex != nil {
|
||||
let value = chart.valueForSeries(seriesIndex, atIndex: dataIndex)
|
||||
let valueFormat = self.channelDataTypes[(self.thisChannel?.dataType)!]! == "Integer" ? "%d" : "%.3f"
|
||||
let valueString = String(format: valueFormat, value!)
|
||||
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
let timestamp = self.dateFormatter.string(from: channelHistory[dataIndex!].timestamp!)
|
||||
self.label.text = "\(valueString) @ \(timestamp)"
|
||||
let intendedLeftPosition = left - (0.5 * self.label.frame.width)
|
||||
if intendedLeftPosition < 8 {
|
||||
self.leftConstraint.constant = 8
|
||||
} else if (intendedLeftPosition + self.label.frame.width) > (self.view.frame.width - 8) {
|
||||
self.leftConstraint.constant = self.view.frame.width - (8 + self.label.frame.width)
|
||||
} else {
|
||||
self.leftConstraint.constant = intendedLeftPosition
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func didFinishTouchingChart(_ chart: Chart) {
|
||||
label.text = ""
|
||||
}
|
||||
|
||||
func didEndTouchingChart(_ chart: Chart) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -262,16 +262,19 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Um-Bu-9sz">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bkh-Zy-Gmc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="171.5" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="172" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="172" id="RRS-6S-a7K"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EXp-nl-Xlu">
|
||||
<rect key="frame" x="171.5" y="0.0" width="171.5" height="43.5"/>
|
||||
<rect key="frame" x="172" y="0.0" width="171" height="44"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -287,10 +290,9 @@
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="5QW-f5-FX9"/>
|
||||
<constraint firstItem="8Um-Bu-9sz" firstAttribute="leading" secondItem="u2w-Yy-Exm" secondAttribute="leading" constant="16" id="DTN-Mt-MOn"/>
|
||||
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="NKZ-KY-yvA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="8Um-Bu-9sz" secondAttribute="trailing" constant="16" id="Xho-aE-Uhw"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8Um-Bu-9sz" secondAttribute="bottom" id="fpI-Sp-LwU"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
@@ -307,6 +309,10 @@
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" title="Title" id="sUT-g4-ZVO"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="4fv-Lb-C8k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</refreshControl>
|
||||
<connections>
|
||||
<segue destination="peV-M2-goO" kind="show" identifier="goToChannelView" id="rTg-Ze-P5c"/>
|
||||
</connections>
|
||||
@@ -487,6 +493,13 @@
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mlm-Lp-0uP">
|
||||
<rect key="frame" x="287" y="300" width="80" height="30"/>
|
||||
<state key="normal" title="View Graph"/>
|
||||
<connections>
|
||||
<segue destination="sJg-Af-FtE" kind="show" identifier="openHistoryGraph" id="WaU-6H-sQq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
@@ -513,7 +526,9 @@
|
||||
<constraint firstItem="7j2-Cq-PoN" firstAttribute="top" secondItem="qKa-BE-rae" secondAttribute="bottom" constant="4" id="nXo-mX-etp"/>
|
||||
<constraint firstItem="Q4o-w1-JdP" firstAttribute="leading" secondItem="Eh7-ay-CB3" secondAttribute="leading" constant="16" id="pB7-gv-T5z"/>
|
||||
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Q4o-w1-JdP" secondAttribute="trailing" constant="16" id="piC-TD-4GF"/>
|
||||
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Mlm-Lp-0uP" secondAttribute="trailing" constant="8" id="q1R-H2-CcR"/>
|
||||
<constraint firstItem="Q4o-w1-JdP" firstAttribute="top" secondItem="Eh7-ay-CB3" secondAttribute="top" id="rDC-Kx-1JX"/>
|
||||
<constraint firstItem="Mlm-Lp-0uP" firstAttribute="top" secondItem="qKa-BE-rae" secondAttribute="bottom" constant="4" id="us2-ib-CDk"/>
|
||||
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Du3-iX-bbt" secondAttribute="trailing" constant="16" id="w0L-Tf-Bfd"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Eh7-ay-CB3"/>
|
||||
@@ -541,6 +556,64 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3600.8000000000002" y="456.52173913043481"/>
|
||||
</scene>
|
||||
<!--History Graph View Controller-->
|
||||
<scene sceneID="WkV-VI-zTb">
|
||||
<objects>
|
||||
<viewController id="sJg-Af-FtE" customClass="HistoryGraphViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="vle-Io-eDj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t93-GQ-APW" customClass="Chart" customModule="SwiftChart">
|
||||
<rect key="frame" x="0.0" y="98" width="375" height="569"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="identifier" value="lineChart"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="lineWidth">
|
||||
<real key="value" value="2"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="labelColor">
|
||||
<color key="value" red="0.0" green="0.32852089410000002" blue="0.57488495110000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="axesColor">
|
||||
<color key="value" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="gridColor">
|
||||
<color key="value" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tap for Value" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GVB-oH-Gbh">
|
||||
<rect key="frame" x="8" y="64" width="100" height="34"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="34" id="mH7-T4-RSj"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="t93-GQ-APW" firstAttribute="bottom" secondItem="HhQ-pe-EoY" secondAttribute="bottom" id="4hl-L7-cV4"/>
|
||||
<constraint firstItem="GVB-oH-Gbh" firstAttribute="top" secondItem="HhQ-pe-EoY" secondAttribute="top" id="JVy-0b-HuT"/>
|
||||
<constraint firstItem="GVB-oH-Gbh" firstAttribute="leading" secondItem="HhQ-pe-EoY" secondAttribute="leading" constant="8" id="fgy-DC-coW"/>
|
||||
<constraint firstItem="t93-GQ-APW" firstAttribute="leading" secondItem="HhQ-pe-EoY" secondAttribute="leading" id="gSt-Qz-p3K"/>
|
||||
<constraint firstItem="t93-GQ-APW" firstAttribute="top" secondItem="GVB-oH-Gbh" secondAttribute="bottom" id="oYi-Rg-r61"/>
|
||||
<constraint firstItem="t93-GQ-APW" firstAttribute="trailing" secondItem="HhQ-pe-EoY" secondAttribute="trailing" id="z6s-FI-MsI"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="HhQ-pe-EoY"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="label" destination="GVB-oH-Gbh" id="JZo-Wq-9Od"/>
|
||||
<outlet property="leftConstraint" destination="fgy-DC-coW" id="Icg-I6-cP4"/>
|
||||
<outlet property="lineChart" destination="t93-GQ-APW" id="HtP-Wq-xnC"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Add-tM-hJL" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4352.8000000000002" y="456.52173913043481"/>
|
||||
</scene>
|
||||
<!--POCloud Data-->
|
||||
<scene sceneID="CNC-dV-jZs">
|
||||
<objects>
|
||||
|
||||
Reference in New Issue
Block a user