├── MMPGraphView
├── MMPGraph
│ ├── Resources
│ │ └── MMPMedia.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── arrows.imageset
│ │ │ ├── arrows.png
│ │ │ └── Contents.json
│ │ │ └── MMPBackIcon.imageset
│ │ │ ├── backIcon.png
│ │ │ ├── backIcon@2x.png
│ │ │ ├── backIcon@3x.png
│ │ │ └── Contents.json
│ ├── Data
│ │ ├── MMPGraphDelegate.swift
│ │ ├── MMPGraphDataPoint.swift
│ │ └── MMPGraphDataSet.swift
│ ├── Extensions
│ │ └── MMPGraphView+Visuals.swift
│ ├── MMPGraphTableViewCell.swift
│ ├── MMPGraphViewController.swift
│ ├── MMPGraphTableViewCell.xib
│ └── MMPGraphView.swift
├── MMPTestData.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── AppDelegate.swift
└── ViewController.swift
├── .gitignore
├── MMPGraphView.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── JLoewy.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcuserdata
│ └── jloewy.xcuserdatad
│ │ ├── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── MMPGraphView.xcscheme
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
└── project.pbxproj
└── README.md
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.xcuserstate
2 |
3 | *.xcuserstate
4 |
5 | *.xcbkptlist
6 |
7 | *.xcbkptlist
8 |
9 | *.xcuserstate
10 |
11 | *.xcuserstate
12 |
13 | *.xcbkptlist
14 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/arrows.imageset/arrows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JLoewy/MMPGraphView/HEAD/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/arrows.imageset/arrows.png
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JLoewy/MMPGraphView/HEAD/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon.png
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JLoewy/MMPGraphView/HEAD/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon@2x.png
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JLoewy/MMPGraphView/HEAD/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/backIcon@3x.png
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/project.xcworkspace/xcuserdata/JLoewy.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JLoewy/MMPGraphView/HEAD/MMPGraphView.xcodeproj/project.xcworkspace/xcuserdata/JLoewy.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MMPGraphView
2 | A versatile core graphics graph for iOS in swift
3 |
4 | MMPGraphView allows you to plot one or two sets of data on a single graph drawn with lightweight core graphics. It can hold multiple plots on the same graph and can be initlialized programmatically or through storyboard.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/arrows.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrows.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Resources/MMPMedia.xcassets/MMPBackIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "backIcon.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "backIcon@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "backIcon@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/xcuserdata/jloewy.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MMPGraphView.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 037D04A91CBB694700AE0C83
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPTestData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPTestData.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/10/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class MMPTestData: NSObject, MMPGraphDataPoint {
13 |
14 | var title:String
15 | var value:CGFloat
16 |
17 | init(title:String, value:CGFloat)
18 | {
19 | self.title = title
20 | self.value = value
21 | }
22 |
23 | // MARK: - MMPGraphDataPoint Methods
24 |
25 | func mmpGraphTitle() -> String {
26 | return self.title
27 | }
28 |
29 | func mmpGraphValue() -> CGFloat {
30 | return self.value
31 | }
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/MMPGraphView/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Data/MMPGraphDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphDelegate.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/12/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | @objc protocol MMPGraphDelegate
13 | {
14 | /**
15 | Resposnible for letting the delegate know that there is a new average value calculated
16 |
17 | - parameter graphView: the currently active graph view
18 | - parameter graphDataAverage: the cgfloat value of the average
19 | - parameter dataSet: the MMPGraphDataSet whos average was just calculated
20 | */
21 | optional func averageCalculated(graphView:MMPGraphView, graphDataAverage:CGFloat, dataSet:MMPGraphDataSet)
22 |
23 | /**
24 | Responsible for letting the delegate know that the user wants this graph to be presented in full screen mode
25 |
26 | - parameter graphView: The currently active MMPGraphView
27 | */
28 | optional func showFullScreenGraph(graphView:MMPGraphView)
29 | }
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Extensions/MMPGraphView+Visuals.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphView+Visuals.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/18/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension MMPGraphView {
13 |
14 | static func boldFont()->UIFont {
15 | return MMPGraphView.boldFont(14.0)
16 | }
17 |
18 | static func boldFont(pointSize:CGFloat)->UIFont {
19 | return UIFont.boldSystemFontOfSize(pointSize)
20 | }
21 |
22 | static func regularFont()->UIFont {
23 | return MMPGraphView.regularFont(14.0)
24 | }
25 |
26 | static func regularFont(pointSize:CGFloat)->UIFont {
27 | return UIFont.systemFontOfSize(pointSize)
28 | }
29 |
30 | static func lightFont()->UIFont {
31 | return UIFont.systemFontOfSize(14.0)
32 | }
33 |
34 | /**
35 | arrows example image is made by Vaadin http://www.flaticon.com/authors/vaadin
36 | distributed on http://www.flaticon.com
37 |
38 | - returns: The UIImage to be used in the full screen button
39 | */
40 | static func fullScreenImage()->UIImage {
41 | return UIImage(named: "arrows")!
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Data/MMPGraphDataPoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphDataPoint.swift
3 | // My Workout
4 | //
5 | // Created by Jason Loewy on 4/8/16.
6 | // Copyright © 2016 My Macros LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | @objc protocol MMPGraphDataPoint {
13 |
14 | /**
15 | Responsible for getting the value associated with this data point
16 |
17 | - returns: The CGFloat representation of the data point
18 | */
19 | func mmpGraphValue()->CGFloat
20 |
21 | /**
22 | Responsible for getting the title associated with this data point
23 |
24 | - returns: The string representation of the data point
25 | */
26 | func mmpGraphTitle()->String
27 |
28 | }
29 |
30 | /// Generic data object that is designed to feed the GraphDataPoint data source
31 | class MMPGraphData {
32 |
33 | private let title:String
34 | private let value:CGFloat
35 |
36 | init(title:String, value:CGFloat)
37 | {
38 | self.title = title;
39 | self.value = value;
40 | }
41 |
42 | func mmpGraphTitle() -> String {
43 | return self.title
44 | }
45 |
46 | func mmpGraphValue() -> CGFloat {
47 | return self.value
48 | }
49 | }
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/xcuserdata/jloewy.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
18 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/MMPGraphView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeRight
37 | UIInterfaceOrientationLandscapeLeft
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/MMPGraphTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphTableViewCell.swift
3 | // My Track
4 | //
5 | // Created by Jason Loewy on 4/19/16.
6 | // Copyright © 2016 Jason Loewy. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MMPGraphTableViewCell: UITableViewCell {
12 |
13 | static let reuseIdentifier = "MMP-Graph-Cell"
14 | static var token: dispatch_once_t = 0
15 |
16 | @IBOutlet var graphView: MMPGraphView!
17 |
18 | override func awakeFromNib() {
19 | super.awakeFromNib()
20 |
21 | print("AWAKE FROM NIB")
22 | graphView.titleAlignment = .Left
23 | graphView.currentlyLoading = true
24 | graphView.setNeedsDisplay()
25 | // Initialization code
26 | }
27 |
28 | /**
29 | Responsible for configuring the graph table cell for a current array of dataplots
30 |
31 | - parameter dataPlots: The MMPGraphDataPlot array that is going to fuel this MMPGraphTableView
32 | */
33 | func configureWithData(dataPlots:[MMPGraphDataPlot], delegate:MMPGraphDelegate?)
34 | {
35 | graphView.delegate = delegate
36 |
37 | dispatch_once(&MMPGraphTableViewCell.token) { () -> Void in
38 | if 2 <= dataPlots.count {
39 | self.graphView.currentPlotIdx = 2
40 | }
41 | }
42 | graphView.finishLoading(dataPlots)
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/MMPGraphView/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/MMPGraphView/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/10/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/MMPGraphViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphViewController.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/18/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MMPGraphViewController: UIViewController {
12 |
13 | private var graphView = MMPGraphView.newLoadingGraphView(CGRectZero, delegate: nil)
14 | let dataPlots:[MMPGraphDataPlot]
15 | let closeButton = UIButton(type: .Custom)
16 |
17 | init(graphView:MMPGraphView)
18 | {
19 | self.graphView.copyGraphAttributes(graphView)
20 | self.graphView.isFullScreen = true
21 | self.dataPlots = graphView.dataPlots
22 | super.init(nibName: nil, bundle: nil)
23 | }
24 |
25 | required init?(coder aDecoder: NSCoder) {
26 | fatalError("init(coder:) has not been implemented")
27 | }
28 |
29 | override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
30 | return .Landscape
31 | }
32 |
33 | override func prefersStatusBarHidden() -> Bool {
34 | return true
35 | }
36 |
37 | override func viewWillAppear(animated: Bool) {
38 | super.viewWillAppear(animated)
39 | UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: .Fade)
40 | }
41 |
42 | override func viewWillDisappear(animated: Bool) {
43 | super.viewWillAppear(animated)
44 | UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: .Fade)
45 | }
46 |
47 | override func viewDidLayoutSubviews() {
48 | graphView.setNeedsDisplay()
49 | graphView.layoutIfNeeded()
50 | }
51 |
52 | override func viewDidLoad() {
53 | super.viewDidLoad()
54 | graphView.finishLoading(dataPlots)
55 | graphView.allowFullScreen = false
56 | graphView.fullScreenButton?.removeFromSuperview()
57 | view.addSubview(graphView)
58 |
59 | // Add the close button
60 | closeButton.frame = CGRect(x: 20.0, y: 20.0, width: 15.0, height: 15.0)
61 | closeButton.autoresizingMask = [.FlexibleRightMargin, .FlexibleBottomMargin]
62 | closeButton.setBackgroundImage(UIImage(named: "MMPBackIcon")!, forState: .Normal)
63 | closeButton.addTarget(self, action: #selector(MMPGraphViewController.closeButtonTapped(_:)), forControlEvents: .TouchUpInside)
64 | view.addSubview(closeButton)
65 | }
66 |
67 | override func viewWillLayoutSubviews() {
68 | super.viewWillLayoutSubviews()
69 | graphView.frame = view.bounds
70 | }
71 |
72 | func closeButtonTapped(sender:UIButton) {
73 | dismissViewControllerAnimated(true, completion: nil)
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/Data/MMPGraphDataSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphDataSet.swift
3 | // My Workout
4 | //
5 | // Created by Jason Loewy on 4/9/16.
6 | // Copyright © 2016 My Macros LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | The MMPGraphdataPlot is one full sheet of a graph.
13 | Contains all of the data sets that will be responsible for drawing the graph
14 |
15 | If there are more than one data set associated with this plot that means that there will be a segmented controller showing
16 | that allows for cycling between them
17 | */
18 | class MMPGraphDataPlot: NSObject {
19 |
20 | var plotTitle = ""
21 | var primaryDataSet:MMPGraphDataSet
22 | var secondaryDataSet:MMPGraphDataSet?
23 |
24 | init(title:String, primaryDataSet:MMPGraphDataSet, secondaryDataSet:MMPGraphDataSet?)
25 | {
26 | self.plotTitle = title
27 | self.primaryDataSet = primaryDataSet
28 | self.secondaryDataSet = secondaryDataSet
29 | }
30 | }
31 |
32 | /**
33 | The MMPGraphDataSet is what drives a single line on the MMPGraphView
34 | It contains one set of datapoints and the title/color attributes associated with it
35 | */
36 | class MMPGraphDataSet: NSObject {
37 |
38 | var dataTitle:String
39 | var dataPoints:[MMPGraphDataPoint]
40 |
41 | var color:UIColor
42 |
43 | init(title:String, dataPoints:[MMPGraphDataPoint], color:UIColor)
44 | {
45 | dataTitle = title
46 | self.dataPoints = dataPoints
47 | self.color = color
48 | }
49 |
50 | /**
51 | Responsible for getting the min and max values for a datasets
52 |
53 | - returns: A tuple of min max values
54 | */
55 | func maxMinElement()->(minValue:MMPGraphDataPoint, maxValue:MMPGraphDataPoint) {
56 |
57 | var maxValue = dataPoints.first!
58 | var minValue = dataPoints.last!
59 |
60 | if maxValue.mmpGraphValue() < minValue.mmpGraphValue() {
61 | maxValue = minValue
62 | minValue = dataPoints.first!
63 | }
64 |
65 | for i in 0..<(dataPoints.count/2)
66 | {
67 | let valueA = dataPoints[i*2]
68 | let valueB = dataPoints[i*2+1]
69 |
70 | if valueA.mmpGraphValue() <= valueB.mmpGraphValue() {
71 |
72 | if (valueA.mmpGraphValue() < minValue.mmpGraphValue()) {
73 | minValue = valueA
74 | }
75 |
76 | if (valueB.mmpGraphValue() > maxValue.mmpGraphValue()) {
77 | maxValue = valueB
78 | }
79 | }
80 | else {
81 |
82 | if valueA.mmpGraphValue() > maxValue.mmpGraphValue() {
83 | maxValue = valueA
84 | }
85 |
86 | if valueB.mmpGraphValue() < minValue.mmpGraphValue() {
87 | minValue = valueB
88 | }
89 | }
90 | }
91 | return (minValue, maxValue)
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/xcuserdata/jloewy.xcuserdatad/xcschemes/MMPGraphView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/MMPGraphTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/MMPGraphView/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/MMPGraphView/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MMPGraphView
4 | //
5 | // Created by Jason Loewy on 4/10/16.
6 | // Copyright © 2016 My Macros+, LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController, MMPGraphDelegate {
12 |
13 | @IBOutlet var storyboardGraphView: MMPGraphView!
14 | var programmaticGraphView:MMPGraphView!
15 |
16 | required init?(coder aDecoder: NSCoder) {
17 | super.init(coder: aDecoder)
18 | }
19 |
20 | override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
21 | return .Portrait
22 | }
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 |
27 | // Create the first data set and associate it with the graph in the storyboard
28 | let primaryDataSet = MMPGraphDataSet(title: "Weight",
29 | dataPoints: [MMPTestData(title: "5", value: 365),
30 | MMPTestData(title: "8", value: 365),
31 | MMPTestData(title: "10", value: 385),
32 | MMPTestData(title: "7", value: 385),
33 | MMPTestData(title: "8", value: 405)],
34 | color: UIColor.whiteColor())
35 |
36 | let secondaryDataSet = MMPGraphDataSet(title: "Reps",
37 | dataPoints: [MMPTestData(title: "5", value: 6),
38 | MMPTestData(title: "8", value: 4),
39 | MMPTestData(title: "10", value: 6),
40 | MMPTestData(title: "7", value: 4),
41 | MMPTestData(title: "8", value: 2)],
42 | color: UIColor.blueColor())
43 |
44 | let dataPlot = MMPGraphDataPlot(title: "Weight vs Reps", primaryDataSet: primaryDataSet, secondaryDataSet:secondaryDataSet)
45 | storyboardGraphView.dataPlots = [dataPlot]
46 |
47 |
48 | // Create the second, manual graph view example
49 | let TWOprimaryDataSet = MMPGraphDataSet(title: "One",
50 | dataPoints: [MMPTestData(title: "5", value: 365),
51 | MMPTestData(title: "8", value: 365),
52 | MMPTestData(title: "10", value: 385),
53 | MMPTestData(title: "7", value: 385),
54 | MMPTestData(title: "8", value: 405)],
55 | color: UIColor.whiteColor())
56 |
57 | let TWOsecondaryDataSet = MMPGraphDataSet(title: "Two",
58 | dataPoints: [MMPTestData(title: "5", value: 6),
59 | MMPTestData(title: "8", value: 4),
60 | MMPTestData(title: "10", value: 6),
61 | MMPTestData(title: "7", value: 4),
62 | MMPTestData(title: "8", value: 2)],
63 | color: UIColor.blueColor())
64 | let TWOdataPlot = MMPGraphDataPlot(title: "Plot 1", primaryDataSet: TWOprimaryDataSet, secondaryDataSet:TWOsecondaryDataSet)
65 |
66 | let THRprimaryDataSet = MMPGraphDataSet(title: "Three",
67 | dataPoints: [MMPTestData(title: "1/10", value: 1029),
68 | MMPTestData(title: "1/11", value: 987),
69 | MMPTestData(title: "1/14", value: 350),
70 | MMPTestData(title: "1/18", value: 444),
71 | MMPTestData(title: "1/22", value: 605)],
72 | color: UIColor.whiteColor())
73 |
74 | let THRsecondaryDataSet = MMPGraphDataSet(title: "Four",
75 | dataPoints: [MMPTestData(title: "5", value: 6),
76 | MMPTestData(title: "8", value: 4),
77 | MMPTestData(title: "10", value: 6),
78 | MMPTestData(title: "7", value: 4),
79 | MMPTestData(title: "8", value: 2)],
80 | color: UIColor.blueColor())
81 | let THRdataPlot = MMPGraphDataPlot(title: "Plot 2", primaryDataSet: THRsecondaryDataSet, secondaryDataSet:THRprimaryDataSet)
82 |
83 | let FVprimaryDataSet = MMPGraphDataSet(title: "Five",
84 | dataPoints: [MMPTestData(title: "5", value: 6),
85 | MMPTestData(title: "8", value: 4),
86 | MMPTestData(title: "10", value: 6),
87 | MMPTestData(title: "7", value: 4),
88 | MMPTestData(title: "8", value: 2)],
89 | color: UIColor.whiteColor())
90 | let FVdataPlot = MMPGraphDataPlot(title: "Single Plot", primaryDataSet: FVprimaryDataSet, secondaryDataSet:nil)
91 |
92 |
93 |
94 | let graphFrame = CGRect(x: 20.0, y: CGRectGetMaxY(storyboardGraphView.frame) + 50.0, width: CGRectGetWidth(self.view.bounds)-40.0, height: 300)
95 | // programmaticGraphView = MMPGraphView.newGraphView(graphFrame, dataPlots: [TWOdataPlot, THRdataPlot], delegate: nil)
96 |
97 | programmaticGraphView = MMPGraphView.newLoadingGraphView(graphFrame, delegate: self)
98 | programmaticGraphView.titleAlignment = .Left
99 | view.addSubview(programmaticGraphView)
100 |
101 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
102 | dispatch_after(delayTime, dispatch_get_main_queue()) {
103 | self.programmaticGraphView.finishLoading([TWOdataPlot, THRdataPlot, FVdataPlot])
104 | }
105 |
106 | storyboardGraphView.layer.cornerRadius = 6.0
107 | storyboardGraphView.clipsToBounds = true
108 | programmaticGraphView.layer.cornerRadius = 6.0
109 | programmaticGraphView.clipsToBounds = true
110 | }
111 |
112 | // MARK: - MMPGraphDelegate Methods
113 |
114 | func showFullScreenGraph(graphView: MMPGraphView) {
115 |
116 | let graphViewController = MMPGraphViewController(graphView: graphView) // MMPGraphViewController(dataPlots: graphView.dataPlots)
117 | showViewController(graphViewController, sender: self)
118 |
119 | }
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/MMPGraphView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 030A3E401CC9CA77004DE83A /* MMPGraphTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030A3E3E1CC9CA77004DE83A /* MMPGraphTableViewCell.swift */; };
11 | 030A3E411CC9CA77004DE83A /* MMPGraphTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 030A3E3F1CC9CA77004DE83A /* MMPGraphTableViewCell.xib */; };
12 | 0319604E1CC5F29200A56782 /* MMPGraphViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0319604D1CC5F29200A56782 /* MMPGraphViewController.swift */; };
13 | 031960521CC5FC2D00A56782 /* MMPMedia.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 031960511CC5FC2D00A56782 /* MMPMedia.xcassets */; };
14 | 037ABD671CC5BB7A00BE05CC /* MMPGraphView+Visuals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037ABD661CC5BB7A00BE05CC /* MMPGraphView+Visuals.swift */; };
15 | 037ABD6D1CC5C0CF00BE05CC /* MMPGraphDataPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037ABD6A1CC5C0CF00BE05CC /* MMPGraphDataPoint.swift */; };
16 | 037ABD6E1CC5C0CF00BE05CC /* MMPGraphDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037ABD6B1CC5C0CF00BE05CC /* MMPGraphDataSet.swift */; };
17 | 037ABD6F1CC5C0CF00BE05CC /* MMPGraphDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037ABD6C1CC5C0CF00BE05CC /* MMPGraphDelegate.swift */; };
18 | 037D04AE1CBB694700AE0C83 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037D04AD1CBB694700AE0C83 /* AppDelegate.swift */; };
19 | 037D04B01CBB694700AE0C83 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037D04AF1CBB694700AE0C83 /* ViewController.swift */; };
20 | 037D04B31CBB694700AE0C83 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 037D04B11CBB694700AE0C83 /* Main.storyboard */; };
21 | 037D04B51CBB694700AE0C83 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 037D04B41CBB694700AE0C83 /* Assets.xcassets */; };
22 | 037D04B81CBB694700AE0C83 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 037D04B61CBB694700AE0C83 /* LaunchScreen.storyboard */; };
23 | 037D04CD1CBB697800AE0C83 /* MMPGraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037D04CA1CBB697800AE0C83 /* MMPGraphView.swift */; };
24 | 037D04D11CBB6A9A00AE0C83 /* MMPTestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037D04D01CBB6A9A00AE0C83 /* MMPTestData.swift */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXFileReference section */
28 | 030A3E3E1CC9CA77004DE83A /* MMPGraphTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MMPGraphTableViewCell.swift; sourceTree = ""; };
29 | 030A3E3F1CC9CA77004DE83A /* MMPGraphTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MMPGraphTableViewCell.xib; sourceTree = ""; };
30 | 0319604D1CC5F29200A56782 /* MMPGraphViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MMPGraphViewController.swift; sourceTree = ""; };
31 | 031960511CC5FC2D00A56782 /* MMPMedia.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = MMPMedia.xcassets; path = Resources/MMPMedia.xcassets; sourceTree = ""; };
32 | 037ABD661CC5BB7A00BE05CC /* MMPGraphView+Visuals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "MMPGraphView+Visuals.swift"; path = "Extensions/MMPGraphView+Visuals.swift"; sourceTree = ""; };
33 | 037ABD6A1CC5C0CF00BE05CC /* MMPGraphDataPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMPGraphDataPoint.swift; path = Data/MMPGraphDataPoint.swift; sourceTree = ""; };
34 | 037ABD6B1CC5C0CF00BE05CC /* MMPGraphDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMPGraphDataSet.swift; path = Data/MMPGraphDataSet.swift; sourceTree = ""; };
35 | 037ABD6C1CC5C0CF00BE05CC /* MMPGraphDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMPGraphDelegate.swift; path = Data/MMPGraphDelegate.swift; sourceTree = ""; };
36 | 037D04AA1CBB694700AE0C83 /* MMPGraphView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MMPGraphView.app; sourceTree = BUILT_PRODUCTS_DIR; };
37 | 037D04AD1CBB694700AE0C83 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
38 | 037D04AF1CBB694700AE0C83 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
39 | 037D04B21CBB694700AE0C83 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
40 | 037D04B41CBB694700AE0C83 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
41 | 037D04B71CBB694700AE0C83 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
42 | 037D04B91CBB694700AE0C83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
43 | 037D04CA1CBB697800AE0C83 /* MMPGraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MMPGraphView.swift; sourceTree = ""; };
44 | 037D04D01CBB6A9A00AE0C83 /* MMPTestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MMPTestData.swift; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 037D04A71CBB694700AE0C83 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 031960501CC5FBF600A56782 /* Resources */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 031960511CC5FC2D00A56782 /* MMPMedia.xcassets */,
62 | );
63 | name = Resources;
64 | sourceTree = "";
65 | };
66 | 037ABD681CC5BB9B00BE05CC /* Extensions */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 037ABD661CC5BB7A00BE05CC /* MMPGraphView+Visuals.swift */,
70 | );
71 | name = Extensions;
72 | sourceTree = "";
73 | };
74 | 037ABD691CC5C0C300BE05CC /* Data */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 037ABD6A1CC5C0CF00BE05CC /* MMPGraphDataPoint.swift */,
78 | 037ABD6B1CC5C0CF00BE05CC /* MMPGraphDataSet.swift */,
79 | 037ABD6C1CC5C0CF00BE05CC /* MMPGraphDelegate.swift */,
80 | );
81 | name = Data;
82 | sourceTree = "";
83 | };
84 | 037D04A11CBB694700AE0C83 = {
85 | isa = PBXGroup;
86 | children = (
87 | 037D04AC1CBB694700AE0C83 /* MMPGraphView */,
88 | 037D04C71CBB697800AE0C83 /* MMPGraph */,
89 | 037D04AB1CBB694700AE0C83 /* Products */,
90 | );
91 | sourceTree = "";
92 | };
93 | 037D04AB1CBB694700AE0C83 /* Products */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 037D04AA1CBB694700AE0C83 /* MMPGraphView.app */,
97 | );
98 | name = Products;
99 | sourceTree = "";
100 | };
101 | 037D04AC1CBB694700AE0C83 /* MMPGraphView */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 037D04AD1CBB694700AE0C83 /* AppDelegate.swift */,
105 | 037D04AF1CBB694700AE0C83 /* ViewController.swift */,
106 | 037D04B11CBB694700AE0C83 /* Main.storyboard */,
107 | 037D04B41CBB694700AE0C83 /* Assets.xcassets */,
108 | 037D04B61CBB694700AE0C83 /* LaunchScreen.storyboard */,
109 | 037D04B91CBB694700AE0C83 /* Info.plist */,
110 | 037D04D01CBB6A9A00AE0C83 /* MMPTestData.swift */,
111 | );
112 | path = MMPGraphView;
113 | sourceTree = "";
114 | };
115 | 037D04C71CBB697800AE0C83 /* MMPGraph */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 037D04CA1CBB697800AE0C83 /* MMPGraphView.swift */,
119 | 0319604D1CC5F29200A56782 /* MMPGraphViewController.swift */,
120 | 030A3E3E1CC9CA77004DE83A /* MMPGraphTableViewCell.swift */,
121 | 030A3E3F1CC9CA77004DE83A /* MMPGraphTableViewCell.xib */,
122 | 037ABD691CC5C0C300BE05CC /* Data */,
123 | 037ABD681CC5BB9B00BE05CC /* Extensions */,
124 | 031960501CC5FBF600A56782 /* Resources */,
125 | );
126 | name = MMPGraph;
127 | path = MMPGraphView/MMPGraph;
128 | sourceTree = "";
129 | };
130 | /* End PBXGroup section */
131 |
132 | /* Begin PBXNativeTarget section */
133 | 037D04A91CBB694700AE0C83 /* MMPGraphView */ = {
134 | isa = PBXNativeTarget;
135 | buildConfigurationList = 037D04BC1CBB694700AE0C83 /* Build configuration list for PBXNativeTarget "MMPGraphView" */;
136 | buildPhases = (
137 | 037D04A61CBB694700AE0C83 /* Sources */,
138 | 037D04A71CBB694700AE0C83 /* Frameworks */,
139 | 037D04A81CBB694700AE0C83 /* Resources */,
140 | );
141 | buildRules = (
142 | );
143 | dependencies = (
144 | );
145 | name = MMPGraphView;
146 | productName = MMPGraphView;
147 | productReference = 037D04AA1CBB694700AE0C83 /* MMPGraphView.app */;
148 | productType = "com.apple.product-type.application";
149 | };
150 | /* End PBXNativeTarget section */
151 |
152 | /* Begin PBXProject section */
153 | 037D04A21CBB694700AE0C83 /* Project object */ = {
154 | isa = PBXProject;
155 | attributes = {
156 | LastSwiftUpdateCheck = 0730;
157 | LastUpgradeCheck = 0730;
158 | ORGANIZATIONNAME = "My Macros+, LLC";
159 | TargetAttributes = {
160 | 037D04A91CBB694700AE0C83 = {
161 | CreatedOnToolsVersion = 7.3;
162 | };
163 | };
164 | };
165 | buildConfigurationList = 037D04A51CBB694700AE0C83 /* Build configuration list for PBXProject "MMPGraphView" */;
166 | compatibilityVersion = "Xcode 3.2";
167 | developmentRegion = English;
168 | hasScannedForEncodings = 0;
169 | knownRegions = (
170 | en,
171 | Base,
172 | );
173 | mainGroup = 037D04A11CBB694700AE0C83;
174 | productRefGroup = 037D04AB1CBB694700AE0C83 /* Products */;
175 | projectDirPath = "";
176 | projectRoot = "";
177 | targets = (
178 | 037D04A91CBB694700AE0C83 /* MMPGraphView */,
179 | );
180 | };
181 | /* End PBXProject section */
182 |
183 | /* Begin PBXResourcesBuildPhase section */
184 | 037D04A81CBB694700AE0C83 /* Resources */ = {
185 | isa = PBXResourcesBuildPhase;
186 | buildActionMask = 2147483647;
187 | files = (
188 | 037D04B81CBB694700AE0C83 /* LaunchScreen.storyboard in Resources */,
189 | 037D04B51CBB694700AE0C83 /* Assets.xcassets in Resources */,
190 | 037D04B31CBB694700AE0C83 /* Main.storyboard in Resources */,
191 | 030A3E411CC9CA77004DE83A /* MMPGraphTableViewCell.xib in Resources */,
192 | 031960521CC5FC2D00A56782 /* MMPMedia.xcassets in Resources */,
193 | );
194 | runOnlyForDeploymentPostprocessing = 0;
195 | };
196 | /* End PBXResourcesBuildPhase section */
197 |
198 | /* Begin PBXSourcesBuildPhase section */
199 | 037D04A61CBB694700AE0C83 /* Sources */ = {
200 | isa = PBXSourcesBuildPhase;
201 | buildActionMask = 2147483647;
202 | files = (
203 | 037ABD6F1CC5C0CF00BE05CC /* MMPGraphDelegate.swift in Sources */,
204 | 037D04B01CBB694700AE0C83 /* ViewController.swift in Sources */,
205 | 0319604E1CC5F29200A56782 /* MMPGraphViewController.swift in Sources */,
206 | 037D04CD1CBB697800AE0C83 /* MMPGraphView.swift in Sources */,
207 | 037ABD6D1CC5C0CF00BE05CC /* MMPGraphDataPoint.swift in Sources */,
208 | 030A3E401CC9CA77004DE83A /* MMPGraphTableViewCell.swift in Sources */,
209 | 037D04D11CBB6A9A00AE0C83 /* MMPTestData.swift in Sources */,
210 | 037D04AE1CBB694700AE0C83 /* AppDelegate.swift in Sources */,
211 | 037ABD671CC5BB7A00BE05CC /* MMPGraphView+Visuals.swift in Sources */,
212 | 037ABD6E1CC5C0CF00BE05CC /* MMPGraphDataSet.swift in Sources */,
213 | );
214 | runOnlyForDeploymentPostprocessing = 0;
215 | };
216 | /* End PBXSourcesBuildPhase section */
217 |
218 | /* Begin PBXVariantGroup section */
219 | 037D04B11CBB694700AE0C83 /* Main.storyboard */ = {
220 | isa = PBXVariantGroup;
221 | children = (
222 | 037D04B21CBB694700AE0C83 /* Base */,
223 | );
224 | name = Main.storyboard;
225 | sourceTree = "";
226 | };
227 | 037D04B61CBB694700AE0C83 /* LaunchScreen.storyboard */ = {
228 | isa = PBXVariantGroup;
229 | children = (
230 | 037D04B71CBB694700AE0C83 /* Base */,
231 | );
232 | name = LaunchScreen.storyboard;
233 | sourceTree = "";
234 | };
235 | /* End PBXVariantGroup section */
236 |
237 | /* Begin XCBuildConfiguration section */
238 | 037D04BA1CBB694700AE0C83 /* Debug */ = {
239 | isa = XCBuildConfiguration;
240 | buildSettings = {
241 | ALWAYS_SEARCH_USER_PATHS = NO;
242 | CLANG_ANALYZER_NONNULL = YES;
243 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
244 | CLANG_CXX_LIBRARY = "libc++";
245 | CLANG_ENABLE_MODULES = YES;
246 | CLANG_ENABLE_OBJC_ARC = YES;
247 | CLANG_WARN_BOOL_CONVERSION = YES;
248 | CLANG_WARN_CONSTANT_CONVERSION = YES;
249 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
250 | CLANG_WARN_EMPTY_BODY = YES;
251 | CLANG_WARN_ENUM_CONVERSION = YES;
252 | CLANG_WARN_INT_CONVERSION = YES;
253 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
254 | CLANG_WARN_UNREACHABLE_CODE = YES;
255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
256 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
257 | COPY_PHASE_STRIP = NO;
258 | DEBUG_INFORMATION_FORMAT = dwarf;
259 | ENABLE_STRICT_OBJC_MSGSEND = YES;
260 | ENABLE_TESTABILITY = YES;
261 | GCC_C_LANGUAGE_STANDARD = gnu99;
262 | GCC_DYNAMIC_NO_PIC = NO;
263 | GCC_NO_COMMON_BLOCKS = YES;
264 | GCC_OPTIMIZATION_LEVEL = 0;
265 | GCC_PREPROCESSOR_DEFINITIONS = (
266 | "DEBUG=1",
267 | "$(inherited)",
268 | );
269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
271 | GCC_WARN_UNDECLARED_SELECTOR = YES;
272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
273 | GCC_WARN_UNUSED_FUNCTION = YES;
274 | GCC_WARN_UNUSED_VARIABLE = YES;
275 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
276 | MTL_ENABLE_DEBUG_INFO = YES;
277 | ONLY_ACTIVE_ARCH = YES;
278 | SDKROOT = iphoneos;
279 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
280 | };
281 | name = Debug;
282 | };
283 | 037D04BB1CBB694700AE0C83 /* Release */ = {
284 | isa = XCBuildConfiguration;
285 | buildSettings = {
286 | ALWAYS_SEARCH_USER_PATHS = NO;
287 | CLANG_ANALYZER_NONNULL = YES;
288 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
289 | CLANG_CXX_LIBRARY = "libc++";
290 | CLANG_ENABLE_MODULES = YES;
291 | CLANG_ENABLE_OBJC_ARC = YES;
292 | CLANG_WARN_BOOL_CONVERSION = YES;
293 | CLANG_WARN_CONSTANT_CONVERSION = YES;
294 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
295 | CLANG_WARN_EMPTY_BODY = YES;
296 | CLANG_WARN_ENUM_CONVERSION = YES;
297 | CLANG_WARN_INT_CONVERSION = YES;
298 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
299 | CLANG_WARN_UNREACHABLE_CODE = YES;
300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
301 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
302 | COPY_PHASE_STRIP = NO;
303 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
304 | ENABLE_NS_ASSERTIONS = NO;
305 | ENABLE_STRICT_OBJC_MSGSEND = YES;
306 | GCC_C_LANGUAGE_STANDARD = gnu99;
307 | GCC_NO_COMMON_BLOCKS = YES;
308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
310 | GCC_WARN_UNDECLARED_SELECTOR = YES;
311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
312 | GCC_WARN_UNUSED_FUNCTION = YES;
313 | GCC_WARN_UNUSED_VARIABLE = YES;
314 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
315 | MTL_ENABLE_DEBUG_INFO = NO;
316 | SDKROOT = iphoneos;
317 | VALIDATE_PRODUCT = YES;
318 | };
319 | name = Release;
320 | };
321 | 037D04BD1CBB694700AE0C83 /* Debug */ = {
322 | isa = XCBuildConfiguration;
323 | buildSettings = {
324 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
325 | INFOPLIST_FILE = MMPGraphView/Info.plist;
326 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
327 | PRODUCT_BUNDLE_IDENTIFIER = com.macros.MMPGraphView;
328 | PRODUCT_NAME = "$(TARGET_NAME)";
329 | };
330 | name = Debug;
331 | };
332 | 037D04BE1CBB694700AE0C83 /* Release */ = {
333 | isa = XCBuildConfiguration;
334 | buildSettings = {
335 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
336 | INFOPLIST_FILE = MMPGraphView/Info.plist;
337 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
338 | PRODUCT_BUNDLE_IDENTIFIER = com.macros.MMPGraphView;
339 | PRODUCT_NAME = "$(TARGET_NAME)";
340 | };
341 | name = Release;
342 | };
343 | /* End XCBuildConfiguration section */
344 |
345 | /* Begin XCConfigurationList section */
346 | 037D04A51CBB694700AE0C83 /* Build configuration list for PBXProject "MMPGraphView" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 037D04BA1CBB694700AE0C83 /* Debug */,
350 | 037D04BB1CBB694700AE0C83 /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | 037D04BC1CBB694700AE0C83 /* Build configuration list for PBXNativeTarget "MMPGraphView" */ = {
356 | isa = XCConfigurationList;
357 | buildConfigurations = (
358 | 037D04BD1CBB694700AE0C83 /* Debug */,
359 | 037D04BE1CBB694700AE0C83 /* Release */,
360 | );
361 | defaultConfigurationIsVisible = 0;
362 | defaultConfigurationName = Release;
363 | };
364 | /* End XCConfigurationList section */
365 | };
366 | rootObject = 037D04A21CBB694700AE0C83 /* Project object */;
367 | }
368 |
--------------------------------------------------------------------------------
/MMPGraphView/MMPGraph/MMPGraphView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMPGraphView.swift
3 | // My Track
4 | //
5 | // Created by Jason Loewy on 3/15/15.
6 | // Copyright (c) 2015 Jason Loewy. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension NSString
12 | {
13 | /**
14 | Responsible for returning a single precision representation of the provided CGFloat
15 |
16 | - parameter currentFloat: The CGFloat being represented
17 | - parameter trim: A boolean flag that tells if you want to trim any trailing .0 or not
18 |
19 | - returns: a string that representation of the cgfloat in the parameters
20 | */
21 | static func singlePrecisionFloat(currentFloat:CGFloat, attemptToTrim trim:Bool)->String {
22 |
23 | let string = NSString(format: "%.1f", currentFloat)
24 | return (trim && string.containsString(".0")) ? string.stringByReplacingOccurrencesOfString(".0", withString: "") : string as String
25 | }
26 |
27 | }
28 |
29 | @IBDesignable class MMPGraphView: UIView {
30 |
31 | // Visual Configuration Objects
32 | var graphInsetFrame = CGRect(x: 0, y: 0, width: 0, height: 0)
33 | @IBInspectable var startColor:UIColor = UIColor(red: 128.0/255.0, green: 182.0/255.0, blue: 248.0/255.0, alpha: 1.0)
34 | @IBInspectable var endColor:UIColor = UIColor(red: 50.0/255.0, green: 118.0/255.0, blue: 255.0/255.0, alpha: 1.0)
35 |
36 | // Loading Variables
37 | @IBInspectable var currentlyLoading = false
38 | let loadingActivityView = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
39 | let loadingDimView = UIView(frame: CGRectZero)
40 |
41 | // Graph Interaction Objects
42 | var delegate:MMPGraphDelegate?;
43 | @IBInspectable var showTouchTitle = true
44 | @IBInspectable var allowFullScreen = true
45 | var fullScreenButton:UIButton?
46 |
47 | // Main Title Objects
48 | @IBInspectable var titleAlignment = NSTextAlignment.Center
49 | var titleLabel:UILabel = UILabel()
50 | var mainTitleText = NSAttributedString(string: "")
51 | var averageValueLabel = UILabel()
52 |
53 | // Graph State Objects
54 | var dataPlots = [MMPGraphDataPlot]()
55 | var currentPlotIdx = 0
56 | var plotSegmentedController:UISegmentedControl?
57 | var isFullScreen = false
58 |
59 | // MARK: - Initialize Methods
60 |
61 | /**
62 | Responsible for getting a new graph view
63 |
64 | - parameter frame: The frame of the graph view
65 | - parameter dataPoints: The datapoints that will be used in creating this graph
66 | - parameter delegate: The object that conforms to the delegate (optional)
67 |
68 | - returns: A newly initialized MMPGraphView
69 | */
70 | static func newGraphView(frame: CGRect, dataPlots:[MMPGraphDataPlot], delegate:MMPGraphDelegate?)->MMPGraphView
71 | {
72 | return MMPGraphView(frame: frame, dataPlots: dataPlots, delegate: delegate)
73 | }
74 |
75 | static func newLoadingGraphView(frame:CGRect, delegate:MMPGraphDelegate?)->MMPGraphView
76 | {
77 | let graphView = MMPGraphView(frame: frame, dataPlots: [MMPGraphDataPlot](), delegate: delegate)
78 | graphView.currentlyLoading = true
79 |
80 | return graphView
81 | }
82 |
83 | private init(frame: CGRect, dataPlots:[MMPGraphDataPlot], delegate:MMPGraphDelegate?) {
84 |
85 | super.init(frame: frame)
86 | self.dataPlots = dataPlots
87 | self.delegate = delegate
88 | initializeSubViews()
89 | }
90 |
91 | required init?(coder aDecoder: NSCoder) {
92 | super.init(coder: aDecoder)
93 | self.dataPlots = []
94 | initializeSubViews()
95 | }
96 |
97 | // Responsble for initializing all of the subviews associated with this graphview instance
98 | func initializeSubViews()
99 | {
100 | // Configure and add the title label
101 | if !subviews.contains(titleLabel) {
102 | titleLabel.font = MMPGraphView.regularFont()
103 | titleLabel.textColor = UIColor.whiteColor()
104 | titleLabel.textAlignment = titleAlignment
105 | titleLabel.attributedText = mainTitleText
106 | addSubview(titleLabel)
107 | }
108 |
109 | // Configure and add the average label
110 | if !subviews.contains(averageValueLabel) {
111 | averageValueLabel.font = MMPGraphView.regularFont(12.0)
112 | averageValueLabel.textColor = UIColor.whiteColor()
113 | averageValueLabel.textAlignment = titleAlignment
114 | averageValueLabel.text = ""
115 | insertSubview(averageValueLabel, belowSubview: titleLabel)
116 | }
117 |
118 | if plotSegmentedController == nil && dataPlots.count > 1 {
119 |
120 | // Initialize, configure and add the plot switching segmented controller
121 | var plotTitles = [String]()
122 | for currentPlot in dataPlots {
123 | plotTitles.append(currentPlot.plotTitle)
124 | }
125 | plotSegmentedController = UISegmentedControl(items: plotTitles)
126 | plotSegmentedController!.tintColor = UIColor.whiteColor()
127 | plotSegmentedController!.setTitleTextAttributes([NSFontAttributeName : MMPGraphView.lightFont()], forState: .Normal)
128 | plotSegmentedController!.setTitleTextAttributes([NSFontAttributeName : MMPGraphView.boldFont()], forState: .Selected)
129 | plotSegmentedController!.addTarget(self, action: #selector(MMPGraphView.activePlotValueChanged(_:)), forControlEvents: .ValueChanged)
130 | plotSegmentedController!.selectedSegmentIndex = 0
131 | addSubview(plotSegmentedController!)
132 | }
133 |
134 | if !subviews.contains(loadingDimView) {
135 | // Initialize all of the loading indicator objects
136 | loadingDimView.frame = bounds
137 | loadingDimView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
138 | loadingDimView.hidden = true
139 |
140 | loadingActivityView.tintColor = UIColor.whiteColor()
141 | loadingActivityView.hidesWhenStopped = true
142 | loadingDimView.addSubview(loadingActivityView)
143 | addSubview(loadingDimView)
144 | }
145 |
146 | if (allowFullScreen && fullScreenButton == nil)
147 | {
148 | let fullScreenButtonSide:CGFloat = 15.0
149 |
150 | // Initialize the full screen button and functionality
151 | fullScreenButton = UIButton(type: .Custom)
152 | fullScreenButton!.tintColor = UIColor.whiteColor()
153 | fullScreenButton!.frame = CGRect(x: 15.0, y: 15.0, width: fullScreenButtonSide, height: fullScreenButtonSide)
154 |
155 | fullScreenButton!.autoresizingMask = [.FlexibleRightMargin, .FlexibleBottomMargin]
156 | fullScreenButton?.setBackgroundImage(MMPGraphView.fullScreenImage(), forState: .Normal)
157 | fullScreenButton!.addTarget(self, action: #selector(MMPGraphView.fullScreenButtonTapped(_:)), forControlEvents: .TouchUpInside)
158 | addSubview(fullScreenButton!)
159 | }
160 | }
161 |
162 | /**
163 | Responsible for copying all of the state attributes from one graph view to another
164 |
165 | - parameter idealGraph: The MMPGraphView that you are copying the attribute values from
166 | */
167 | func copyGraphAttributes(idealGraph:MMPGraphView)
168 | {
169 | titleAlignment = idealGraph.titleAlignment
170 | showTouchTitle = idealGraph.showTouchTitle
171 | currentlyLoading = idealGraph.currentlyLoading
172 | currentPlotIdx = idealGraph.currentPlotIdx
173 | }
174 |
175 |
176 | // MARK: - View Layout Methods
177 |
178 | func finishLoading(dataPlots:[MMPGraphDataPlot]) {
179 | self.dataPlots = dataPlots
180 | currentlyLoading = false
181 | initializeSubViews()
182 | setNeedsDisplay()
183 | }
184 |
185 | override func layoutSubviews() {
186 |
187 | super.layoutSubviews()
188 | let labelWidth = CGRectGetWidth(bounds) * 0.5
189 | let titleYPadding:CGFloat = isFullScreen ? 15.0 : 5.0
190 | let labelHeight:CGFloat = 20.0
191 |
192 | titleLabel.font = isFullScreen ? MMPGraphView.boldFont(18.0) : MMPGraphView.boldFont()
193 | titleLabel.textAlignment = titleAlignment
194 | let xOrigin = (titleAlignment == .Center) ? (CGRectGetWidth(bounds)*0.25) : max(graphInsetFrame.origin.x, 40.0)
195 | titleLabel.frame = CGRect(x: xOrigin, y: titleYPadding, width: labelWidth, height: labelHeight)
196 | averageValueLabel.frame = CGRect(x: xOrigin, y: CGRectGetMaxY(titleLabel.frame), width: labelWidth, height: 16.0)
197 | averageValueLabel.textAlignment = titleAlignment
198 |
199 | loadingDimView.frame = bounds
200 | loadingActivityView.center = CGPoint(x: loadingDimView.bounds.width/2.0, y: loadingDimView.bounds.height/2.0)
201 | }
202 |
203 | // Only override drawRect: if you perform custom drawing.
204 | // An empty implementation adversely affects performance during animation.
205 | override func drawRect(rect: CGRect) {
206 |
207 | fullScreenButton?.hidden = !allowFullScreen
208 | if currentPlotIdx < dataPlots.count
209 | {
210 | // Set up the title text
211 | // This is in draw rect becuase it needs to get reset everytime that the segmented controller value changes
212 | let titleFont = isFullScreen ? MMPGraphView.boldFont(18.0) : MMPGraphView.boldFont()
213 | if let secondaryData = dataPlots[currentPlotIdx].secondaryDataSet
214 | {
215 | let primaryDataSet = dataPlots[currentPlotIdx].primaryDataSet
216 | let titleAttributeText = NSMutableAttributedString(string: "\(primaryDataSet.dataTitle) vs \(secondaryData.dataTitle)",
217 | attributes: [NSFontAttributeName : titleFont,
218 | NSForegroundColorAttributeName : UIColor.whiteColor()])
219 | titleAttributeText.addAttributes([NSForegroundColorAttributeName:primaryDataSet.color], range: NSMakeRange(0, primaryDataSet.dataTitle.characters.count))
220 | titleAttributeText.addAttributes([NSForegroundColorAttributeName:secondaryData.color], range: NSMakeRange(primaryDataSet.dataTitle.characters.count + 4, secondaryData.dataTitle.characters.count))
221 | mainTitleText = titleAttributeText
222 | }
223 | else {
224 | mainTitleText = NSAttributedString(string: "\(dataPlots[currentPlotIdx].primaryDataSet.dataTitle)", attributes: [NSFontAttributeName : titleFont, NSForegroundColorAttributeName : UIColor.whiteColor()])
225 | }
226 | }
227 | else {
228 | mainTitleText = NSAttributedString(string: "")
229 | }
230 | titleLabel.attributedText = mainTitleText
231 | averageValueLabel.text = ""
232 |
233 | // Calculate the boundaries
234 | graphInsetFrame.origin.y = bounds.height * 0.25
235 | if dataPlots.count > 1 {
236 | graphInsetFrame.origin.y -= 20.0
237 | }
238 |
239 | graphInsetFrame.size.height = bounds.height - graphInsetFrame.origin.y*2.0
240 | if dataPlots.count > 1 {
241 | graphInsetFrame.size.height -= 40.0
242 | }
243 | graphInsetFrame.origin.x = bounds.width * 0.125
244 | graphInsetFrame.size.width = CGRectGetWidth(bounds) - (graphInsetFrame.origin.x * 2.0)
245 | let columnWidth:CGFloat = (currentPlotIdx < dataPlots.count) ? (graphInsetFrame.width / max(1,(CGFloat(dataPlots[currentPlotIdx].primaryDataSet.dataPoints.count)) - 1.0)) : 0
246 |
247 | // Now that you have the inset bounds calculated you can layout the segmented controller to sit right underneath it
248 | if let segmentedController = plotSegmentedController {
249 |
250 | let yOffset = ((CGRectGetHeight(bounds) - CGRectGetMaxY(graphInsetFrame)) / 2.0) - (CGRectGetHeight(segmentedController.bounds) / 4.0)
251 | if !isFullScreen {
252 | segmentedController.frame = CGRect(x: 15.0, y: CGRectGetMaxY(graphInsetFrame) + yOffset, width: bounds.width - 30.0, height: segmentedController.frame.size.height)
253 | }
254 | else {
255 | segmentedController.frame = CGRect(x: graphInsetFrame.origin.x, y: CGRectGetMaxY(graphInsetFrame) + yOffset, width: graphInsetFrame.size.width, height: segmentedController.frame.size.height)
256 | }
257 | }
258 |
259 | // Draw the background gradient
260 | let context:CGContext? = UIGraphicsGetCurrentContext()
261 | let colorRGB = CGColorSpaceCreateDeviceRGB()
262 | let gradiant = CGGradientCreateWithColors(colorRGB,
263 | [startColor.CGColor, endColor.CGColor], [0.0,1.0])
264 | CGContextDrawLinearGradient(context, gradiant, CGPointZero, CGPoint(x: 0, y: self.bounds.height), .DrawsBeforeStartLocation)
265 |
266 | // MARK: - column X|Y positioning closures
267 |
268 | let columnXPosition = {(dataPoints:[MMPGraphDataPoint], xColumn:CGFloat)->CGFloat in
269 | return (dataPoints.count > 1) ? self.graphInsetFrame.origin.x + columnWidth*xColumn : CGRectGetWidth(self.bounds)/2.0
270 | }
271 |
272 | let columnYPosition = {(graphValue:CGFloat, minValue:MMPGraphDataPoint, maxValue:MMPGraphDataPoint)->CGFloat in
273 |
274 | let normalizedCurrentValue = graphValue - minValue.mmpGraphValue()
275 | let yPosition = (self.graphInsetFrame.size.height + self.graphInsetFrame.origin.y) - ((normalizedCurrentValue/(maxValue.mmpGraphValue() - minValue.mmpGraphValue())) * self.graphInsetFrame.size.height)
276 | return isnan(yPosition) ? self.graphInsetFrame.origin.y + self.graphInsetFrame.size.height/2.0 : yPosition
277 | }
278 |
279 | // Draw the actual data plots
280 | loadingDimView.hidden = true
281 | if currentPlotIdx < dataPlots.count && dataPlots[currentPlotIdx].primaryDataSet.dataPoints.count > 0
282 | {
283 | var dataSets = [self.dataPlots[currentPlotIdx].primaryDataSet];
284 | if let secondDataSet = self.dataPlots[currentPlotIdx].secondaryDataSet {
285 | dataSets.append(secondDataSet)
286 | }
287 |
288 | var index = -1
289 | for currentDataSet in dataSets
290 | {
291 | index += 1
292 | let minMaxValues = currentDataSet.maxMinElement()
293 | let maxValue = minMaxValues.maxValue
294 | let minValue = minMaxValues.minValue
295 |
296 | // Move to the start add the main graph plot
297 | let graphPath = UIBezierPath()
298 | graphPath.moveToPoint(CGPoint(x: columnXPosition(currentDataSet.dataPoints, 0),
299 | y: columnYPosition(currentDataSet.dataPoints[0].mmpGraphValue(), minValue, maxValue)))
300 | for i in 1.. 0) ? "\(averageValueLabel.text!) | \(avgString)" : "Average: \(avgString)"
361 |
362 | // Draw the 3 horizontal lines and the indicator lablese
363 | let graphMidPtY = graphInsetFrame.origin.y + graphInsetFrame.size.height/2.0
364 | let averageWeight = (maxValue.mmpGraphValue() + minValue.mmpGraphValue())/2.0
365 | var bgLinePoints = [graphInsetFrame.origin.y+graphInsetFrame.size.height, graphMidPtY, graphInsetFrame.origin.y]
366 | var bgLabelValues:[NSString] = [NSString.singlePrecisionFloat(minValue.mmpGraphValue(), attemptToTrim: true),
367 | NSString.singlePrecisionFloat(averageWeight, attemptToTrim: true),
368 | NSString.singlePrecisionFloat(maxValue.mmpGraphValue(), attemptToTrim: true)]
369 |
370 | // Enter here if there are a lot of graph points and you could have more than just 3 lines
371 | if currentDataSet.dataPoints.count > 30
372 | {
373 | bgLinePoints.insert(graphMidPtY + graphInsetFrame.size.height/4.0, atIndex: 1)
374 | bgLabelValues.insert(NSString.singlePrecisionFloat((averageWeight + minValue.mmpGraphValue()) / 2.0, attemptToTrim: true), atIndex: 1)
375 |
376 | bgLinePoints.insert(graphMidPtY - graphInsetFrame.size.height/4.0, atIndex: 3)
377 | bgLabelValues.insert(NSString.singlePrecisionFloat((averageWeight + maxValue.mmpGraphValue()) / 2.0, attemptToTrim: true), atIndex: 3)
378 | }
379 |
380 | // MARK: - Draw side markers
381 | let bgLinePath = UIBezierPath()
382 | let bgLineWidth = bounds.width-graphInsetFrame.origin.x
383 | counter = 0
384 | for currentBGYPoint in bgLinePoints
385 | {
386 | bgLinePath.moveToPoint(CGPoint(x: graphInsetFrame.origin.x, y: currentBGYPoint))
387 | bgLinePath.addLineToPoint(CGPoint(x: bgLineWidth, y: currentBGYPoint))
388 |
389 | let currentLabel = UILabel()
390 | currentLabel.text = bgLabelValues[counter] as String
391 | counter += 1
392 | currentLabel.textColor = UIColor.whiteColor()
393 | currentLabel.font = MMPGraphView.lightFont()
394 | currentLabel.sizeToFit()
395 |
396 | // Primary data points are drawn on the right margin, secondary on the left
397 | let xOrigin = (index == 0) ? (bgLineWidth+10.0) : (graphInsetFrame.origin.x - (CGRectGetWidth(currentLabel.bounds) + 10.0))
398 | currentLabel.drawTextInRect(CGRect(x: xOrigin, y: currentBGYPoint-10, width: bounds.width-(bgLineWidth + 5), height: 20))
399 | }
400 |
401 | UIColor(white: 1.0, alpha: 0.5).setStroke()
402 | bgLinePath.lineWidth = 1.0
403 | bgLinePath.stroke()
404 |
405 | // Draw the x axis labels
406 | var previousXAxisFrame = CGRectZero
407 | let xLabelWidth:CGFloat = 80.0
408 | let xLabelCount:Int = min(currentDataSet.dataPoints.count, 6)
409 | if xLabelCount > 1 && index == 0 // Currently only draw the primary data plots x label
410 | {
411 | for i in 0.., withEvent event: UIEvent?) {
471 | handleTouch(touches, withEvent: event)
472 | }
473 |
474 | override func touchesMoved(touches: Set, withEvent event: UIEvent?) {
475 | handleTouch(touches, withEvent: event)
476 | }
477 |
478 | /// Responsible for reacting to the touch inside of the graph inset view - Updates the top label
479 | func handleTouch(touches: Set, withEvent event:UIEvent?) {
480 |
481 | if showTouchTitle {
482 |
483 | // If the touch point is in the drawn graph (or a little outside of it) handle it as a data touch
484 | let locationPoint = touches.first!.locationInView(self)
485 | let touchArea = CGRectInset(graphInsetFrame, -10, -10)
486 | if CGRectContainsPoint(touchArea, locationPoint)
487 | {
488 | let normalizedX = locationPoint.x - touchArea.origin.x
489 | let insetPercentage = normalizedX / touchArea.size.width
490 | let index = Int(floor(CGFloat(dataPlots[currentPlotIdx].primaryDataSet.dataPoints.count) * insetPercentage))
491 | let dataPointTouched = dataPlots[currentPlotIdx].primaryDataSet.dataPoints[index]
492 |
493 | titleLabel.attributedText = NSAttributedString(string: "\(dataPointTouched.mmpGraphTitle()):\(dataPointTouched.mmpGraphValue())")
494 | }
495 | else {
496 | titleLabel.attributedText = mainTitleText
497 | }
498 | }
499 | }
500 |
501 | /// Responsible for reacting to the user tapping the full screen presentation button
502 | func fullScreenButtonTapped(sender:UIButton)
503 | {
504 | delegate?.showFullScreenGraph?(self)
505 | }
506 | }
507 |
--------------------------------------------------------------------------------