Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b87b394d5 | |||
| 4d8339e5a0 | |||
| 389d62c3a3 | |||
| 56ba13a6c9 | |||
| bc92697057 | |||
| fe4b092c93 | |||
| ce4804d312 | |||
| 9f667c36ee | |||
| 693a202cb3 | |||
| 463378ea05 | |||
| 29a3d41153 | |||
| 3f679eec13 | |||
| db4b7daf63 | |||
| 2bae885a90 | |||
| 7de33f9b35 | |||
| ce2cda8169 | |||
| 2fc1655e38 | |||
| ae753b0143 | |||
| e75a9322ab | |||
| aba0069c5b | |||
| af8d0f8034 | |||
| 1beb08c624 | |||
| 3c4d63c923 | |||
| e070aa6adf | |||
| 0fb01f8e68 | |||
| 749f7ae791 | |||
| 3585fd1931 | |||
| 17935febb6 | |||
| 248de92212 | |||
| 9cd945c23b | |||
| e43db2cbb1 | |||
| d1b7c13e8c | |||
| 95e5750400 | |||
| c8493610f5 | |||
| 48879d8bb7 | |||
| 403d067112 | |||
| 03d8a003ff | |||
| 6a67d26823 | |||
| 435a2f10c3 | |||
| 12850655ad | |||
| d5636cc52e | |||
| 8c39279ac6 | |||
| 16a8050282 | |||
| 3489cfc55a | |||
| 2445b49f63 | |||
| a0cfae180d | |||
| 60f805c57e | |||
| 532d4da903 | |||
| ec39feec10 | |||
| 1a79f12190 | |||
| 9a1e6c6671 | |||
| 0f1944be24 | |||
| 5cb25a0a5b | |||
| e7c84aa4de | |||
| e8fda5bcf5 | |||
| c9f3012e5c | |||
| 880d85adda | |||
| d5f2782308 | |||
| e40620498e | |||
| d3ee78fb6d | |||
| b7f7324d96 | |||
| 78b2aa1964 | |||
| d9ab659422 | |||
| 71df60f9a3 | |||
| 728b9420a9 | |||
| fe42e4f61d | |||
| 45cf0d66ba | |||
| 5aecb0ba92 | |||
| 6f14f4099f | |||
| 9fa369ce8d | |||
| 9828034f6b | |||
| 58f57fcef2 | |||
| 78390657d7 | |||
| 87a5de93af | |||
| edfca9b780 | |||
| 14c7aa7c98 | |||
| b928cd1849 | |||
| 2d6c6e291d | |||
| 5addeeae83 | |||
| ec14cc002e | |||
| e3174efa9e | |||
| 4a1380668e | |||
| 3752b7d598 | |||
| e1d10ffb7e | |||
| f2e5641d58 | |||
| b0c68377a3 | |||
| d3cb39b566 | |||
| 99f727c0d6 | |||
| b6bc9c29ca | |||
| 124eabe98c | |||
| 1f0f3351c3 | |||
| 5f8863ede1 | |||
| bf8d5de61e | |||
| 23935a3b55 | |||
| f7d15d4905 | |||
| 372b69ff4a | |||
| b17a37c1d7 | |||
| 69ce275d1a | |||
| a1f34f78fb |
+95
-169
@@ -1,7 +1,101 @@
|
||||
# Change Log
|
||||
|
||||
## [v2.8.13](https://github.com/Jawbone/JBChartView/tree/v2.8.13) (2015-04-16)
|
||||
## [v2.9.1](https://github.com/Jawbone/JBChartView/tree/v2.9.1) (2015-12-17)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.9.0...v2.9.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Min bars shouldn't animate bars when animating setState [\#192](https://github.com/Jawbone/JBChartView/issues/192)
|
||||
|
||||
## [v2.9.0](https://github.com/Jawbone/JBChartView/tree/v2.9.0) (2015-12-04)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.19...v2.9.0)
|
||||
|
||||
## [v2.8.19](https://github.com/Jawbone/JBChartView/tree/v2.8.19) (2015-11-23)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.18...v2.8.19)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Straight lines [\#94](https://github.com/Jawbone/JBChartView/issues/94)
|
||||
|
||||
## [v2.8.18](https://github.com/Jawbone/JBChartView/tree/v2.8.18) (2015-11-22)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.17...v2.8.18)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Vertical values should be supplied by the DataSource delegate for LineCharts [\#180](https://github.com/Jawbone/JBChartView/issues/180)
|
||||
- JBLineChartDotsView reloadData performance issues [\#179](https://github.com/Jawbone/JBChartView/issues/179)
|
||||
|
||||
## [v2.8.17](https://github.com/Jawbone/JBChartView/tree/v2.8.17) (2015-11-21)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.16...v2.8.17)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Disabling/Enabling certain functionality: line chart selection and x-axis values [\#185](https://github.com/Jawbone/JBChartView/issues/185)
|
||||
- Add ability to set selection permanently. [\#153](https://github.com/Jawbone/JBChartView/issues/153)
|
||||
- The line fill color doesn't match the bottom of the drawn line [\#146](https://github.com/Jawbone/JBChartView/issues/146)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Bug when quickly tapping a line chart in two different places [\#159](https://github.com/Jawbone/JBChartView/issues/159)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Show dot only on vertical line selection [\#190](https://github.com/Jawbone/JBChartView/issues/190)
|
||||
- Needs to support negative values [\#189](https://github.com/Jawbone/JBChartView/issues/189)
|
||||
|
||||
## [v2.8.16](https://github.com/Jawbone/JBChartView/tree/v2.8.16) (2015-09-18)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.15...v2.8.16)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Partially filled bar [\#186](https://github.com/Jawbone/JBChartView/issues/186)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Delegate call to ignore touches on selected lines [\#188](https://github.com/Jawbone/JBChartView/pull/188) ([andrewloyola](https://github.com/andrewloyola))
|
||||
|
||||
## [v2.8.15](https://github.com/Jawbone/JBChartView/tree/v2.8.15) (2015-08-12)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.14...v2.8.15)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Suggestion for the README.md [\#176](https://github.com/Jawbone/JBChartView/issues/176)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Line color overlap issue [\#168](https://github.com/Jawbone/JBChartView/issues/168)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- JBLineChart reloadData problem [\#181](https://github.com/Jawbone/JBChartView/issues/181)
|
||||
- JBLineChartView: wrong width on iPhone6/6+ [\#177](https://github.com/Jawbone/JBChartView/issues/177)
|
||||
- Is there anyway to plot two vertical values for one horizontal index in the bar chart? [\#172](https://github.com/Jawbone/JBChartView/issues/172)
|
||||
- Charts not rendering properly [\#171](https://github.com/Jawbone/JBChartView/issues/171)
|
||||
- How can you set the bar chart to fill the width of a UIView? [\#170](https://github.com/Jawbone/JBChartView/issues/170)
|
||||
- Adding X and Y Values [\#169](https://github.com/Jawbone/JBChartView/issues/169)
|
||||
- Undefined symbols for architecture i386 [\#167](https://github.com/Jawbone/JBChartView/issues/167)
|
||||
- Not All Data Points Visible in Flexible View [\#166](https://github.com/Jawbone/JBChartView/issues/166)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Return the available height instead of 0 [\#183](https://github.com/Jawbone/JBChartView/pull/183) ([0xPr0xy](https://github.com/0xPr0xy))
|
||||
- Adds support for gradients to line charts [\#175](https://github.com/Jawbone/JBChartView/pull/175) ([benjaminsnorris](https://github.com/benjaminsnorris))
|
||||
|
||||
## [v2.8.14](https://github.com/Jawbone/JBChartView/tree/v2.8.14) (2015-04-30)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.13...v2.8.14)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Will JBChartView integrated with React Native? [\#164](https://github.com/Jawbone/JBChartView/issues/164)
|
||||
- white dot+dotted lines sometimes does not appear when touched. [\#161](https://github.com/Jawbone/JBChartView/issues/161)
|
||||
- Small offset when using a footer for a x-axis [\#160](https://github.com/Jawbone/JBChartView/issues/160)
|
||||
- gradient for bar chart is lost / sizing problem [\#158](https://github.com/Jawbone/JBChartView/issues/158)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Nil out delegate and datasource on dealloc [\#165](https://github.com/Jawbone/JBChartView/pull/165) ([eventualbuddha](https://github.com/eventualbuddha))
|
||||
|
||||
## [v2.8.13](https://github.com/Jawbone/JBChartView/tree/v2.8.13) (2015-04-16)
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.12...v2.8.13)
|
||||
|
||||
**Fixed bugs:**
|
||||
@@ -13,7 +107,6 @@
|
||||
- Swift round up for respondsToSelector\(\) and sizeWithFont\(\) [\#156](https://github.com/Jawbone/JBChartView/issues/156)
|
||||
|
||||
## [v2.8.12](https://github.com/Jawbone/JBChartView/tree/v2.8.12) (2015-04-13)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.11...v2.8.12)
|
||||
|
||||
**Fixed bugs:**
|
||||
@@ -23,111 +116,65 @@
|
||||
**Closed issues:**
|
||||
|
||||
- Problems with selection when JBLineChartView is within UITableView [\#155](https://github.com/Jawbone/JBChartView/issues/155)
|
||||
|
||||
- Show chart selection permanently [\#151](https://github.com/Jawbone/JBChartView/issues/151)
|
||||
|
||||
- Crash when returning NAN [\#150](https://github.com/Jawbone/JBChartView/issues/150)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix warnings when building w/ Xcode 6.3 [\#154](https://github.com/Jawbone/JBChartView/pull/154) ([amro](https://github.com/amro))
|
||||
|
||||
- Add ability for the graphs to always show the selected data value and to pre-set which index is selected [\#147](https://github.com/Jawbone/JBChartView/pull/147) ([MobileVet](https://github.com/MobileVet))
|
||||
|
||||
## [v2.8.11](https://github.com/Jawbone/JBChartView/tree/v2.8.11) (2015-03-31)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.10...v2.8.11)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Pass back barView & dot instances on selection [\#118](https://github.com/Jawbone/JBChartView/issues/118)
|
||||
|
||||
- Add ability to have the line start or end at any given point [\#115](https://github.com/Jawbone/JBChartView/issues/115)
|
||||
|
||||
- Consolidate dot view selection/unselection/color calls [\#101](https://github.com/Jawbone/JBChartView/issues/101)
|
||||
|
||||
- Change dot radius to dot diameter [\#99](https://github.com/Jawbone/JBChartView/issues/99)
|
||||
|
||||
- need Swift implementation details and example [\#80](https://github.com/Jawbone/JBChartView/issues/80)
|
||||
|
||||
- Add bar/line caching for only visible content. [\#61](https://github.com/Jawbone/JBChartView/issues/61)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Pie Chart support [\#149](https://github.com/Jawbone/JBChartView/issues/149)
|
||||
|
||||
- hi is it possible to have x,y value because in example there is no y value and only 2 x value [\#141](https://github.com/Jawbone/JBChartView/issues/141)
|
||||
|
||||
- imports without pods [\#140](https://github.com/Jawbone/JBChartView/issues/140)
|
||||
|
||||
- How can I group Bars? [\#139](https://github.com/Jawbone/JBChartView/issues/139)
|
||||
|
||||
- request: AreaChart - fillColorForLineAtLineIndex - add custom color for a specific X axis range [\#137](https://github.com/Jawbone/JBChartView/issues/137)
|
||||
|
||||
- smallest value is shown as a blank bar [\#136](https://github.com/Jawbone/JBChartView/issues/136)
|
||||
|
||||
- Is there any way I can use JBBarChartView as horizontal bars? [\#135](https://github.com/Jawbone/JBChartView/issues/135)
|
||||
|
||||
- Multiple colors to a line graph [\#134](https://github.com/Jawbone/JBChartView/issues/134)
|
||||
|
||||
- Overriding - \(void\)barChartView:\(JBBarChartView \*\)barChartView didSelectBarAtIndex:\(NSUInteger\)index is not showing selection view? [\#133](https://github.com/Jawbone/JBChartView/issues/133)
|
||||
|
||||
- Exception thrown on setState: [\#132](https://github.com/Jawbone/JBChartView/issues/132)
|
||||
|
||||
- X and Y scale and reference values [\#131](https://github.com/Jawbone/JBChartView/issues/131)
|
||||
|
||||
- Determine x-value of a certain point by a number instead of it's index in the input array? [\#130](https://github.com/Jawbone/JBChartView/issues/130)
|
||||
|
||||
- How to accomplish similar didUnselectBarChartView [\#129](https://github.com/Jawbone/JBChartView/issues/129)
|
||||
|
||||
- Is posible - scroll chart [\#126](https://github.com/Jawbone/JBChartView/issues/126)
|
||||
|
||||
- Crash when vertical value for horizontal at index x less than zero [\#125](https://github.com/Jawbone/JBChartView/issues/125)
|
||||
|
||||
- JBBarChartView does not display all the bars [\#124](https://github.com/Jawbone/JBChartView/issues/124)
|
||||
|
||||
- "automatically normalized across the entire chart" [\#123](https://github.com/Jawbone/JBChartView/issues/123)
|
||||
|
||||
- reloadData in viewDidLayoutSubviews does not trigger didDeselectLineInLineChartView on first selection [\#122](https://github.com/Jawbone/JBChartView/issues/122)
|
||||
|
||||
- Question [\#121](https://github.com/Jawbone/JBChartView/issues/121)
|
||||
|
||||
- Sublayers not properly resized [\#120](https://github.com/Jawbone/JBChartView/issues/120)
|
||||
|
||||
- It will be really cool if "while on tap on line charts dots I can get the dot object do some scale animation" or something? [\#119](https://github.com/Jawbone/JBChartView/issues/119)
|
||||
|
||||
- Is there any way I can pass new bar view while user selects particular bar? [\#117](https://github.com/Jawbone/JBChartView/issues/117)
|
||||
|
||||
- Typo error in README [\#114](https://github.com/Jawbone/JBChartView/issues/114)
|
||||
|
||||
- Dynamic/append values [\#113](https://github.com/Jawbone/JBChartView/issues/113)
|
||||
|
||||
- It needs better documentation [\#112](https://github.com/Jawbone/JBChartView/issues/112)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Keep tooltip subviews above line subviews [\#144](https://github.com/Jawbone/JBChartView/pull/144) ([vocaro](https://github.com/vocaro))
|
||||
|
||||
- Fix crash and warnings that can occur when a chart line has no data [\#143](https://github.com/Jawbone/JBChartView/pull/143) ([vocaro](https://github.com/vocaro))
|
||||
|
||||
- Add automatically generated change log file. [\#142](https://github.com/Jawbone/JBChartView/pull/142) ([skywinder](https://github.com/skywinder))
|
||||
|
||||
- Update README.md [\#127](https://github.com/Jawbone/JBChartView/pull/127) ([paal123](https://github.com/paal123))
|
||||
|
||||
- Add ability to have the line start or end at any given point [\#116](https://github.com/Jawbone/JBChartView/pull/116) ([sebastianreloaded](https://github.com/sebastianreloaded))
|
||||
|
||||
- Horizontal Bar Graphs [\#148](https://github.com/Jawbone/JBChartView/pull/148) ([tomhoag](https://github.com/tomhoag))
|
||||
|
||||
- Add optional gradient fill to line chart [\#138](https://github.com/Jawbone/JBChartView/pull/138) ([mbachrach](https://github.com/mbachrach))
|
||||
|
||||
- fix for updating graphs on view resize [\#128](https://github.com/Jawbone/JBChartView/pull/128) ([sobakasu](https://github.com/sobakasu))
|
||||
|
||||
## [v2.8.10](https://github.com/Jawbone/JBChartView/tree/v2.8.10) (2014-09-23)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.9...v2.8.10)
|
||||
|
||||
## [v2.8.9](https://github.com/Jawbone/JBChartView/tree/v2.8.9) (2014-09-22)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.8...v2.8.9)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -135,7 +182,6 @@
|
||||
- JBLineChartView doesn't resize its subviews [\#111](https://github.com/Jawbone/JBChartView/issues/111)
|
||||
|
||||
## [v2.8.8](https://github.com/Jawbone/JBChartView/tree/v2.8.8) (2014-09-20)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.7...v2.8.8)
|
||||
|
||||
**Fixed bugs:**
|
||||
@@ -145,47 +191,32 @@
|
||||
**Closed issues:**
|
||||
|
||||
- Noob level tutorial [\#109](https://github.com/Jawbone/JBChartView/issues/109)
|
||||
|
||||
- Data points with uneavenly distributed x-values? [\#108](https://github.com/Jawbone/JBChartView/issues/108)
|
||||
|
||||
## [v2.8.7](https://github.com/Jawbone/JBChartView/tree/v2.8.7) (2014-09-09)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.6...v2.8.7)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add hide custom dot functionality [\#100](https://github.com/Jawbone/JBChartView/issues/100)
|
||||
|
||||
- Add footer padding [\#98](https://github.com/Jawbone/JBChartView/issues/98)
|
||||
|
||||
- DotView is hidden when inside selected table view cell [\#82](https://github.com/Jawbone/JBChartView/issues/82)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- There should be flexibility to change to touch point to nearest data point so vertical selection view just jumps from one data point to another in JBLineChartView. [\#105](https://github.com/Jawbone/JBChartView/issues/105)
|
||||
|
||||
- barWidth Method and barPadding property should be in JBBarChartView header file. [\#104](https://github.com/Jawbone/JBChartView/issues/104)
|
||||
|
||||
- Why minimum and maximum should be positive? [\#102](https://github.com/Jawbone/JBChartView/issues/102)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- iOS Framework Support [\#107](https://github.com/Jawbone/JBChartView/pull/107) ([thefirstnikhil](https://github.com/thefirstnikhil))
|
||||
|
||||
- Only call `numberOfLinesInLineChartView:` once per loop. [\#106](https://github.com/Jawbone/JBChartView/pull/106) ([eventualbuddha](https://github.com/eventualbuddha))
|
||||
|
||||
- Allowed to use negative values [\#103](https://github.com/Jawbone/JBChartView/pull/103) ([ReDetection](https://github.com/ReDetection))
|
||||
|
||||
- Multitouch support [\#84](https://github.com/Jawbone/JBChartView/pull/84) ([nopshusang](https://github.com/nopshusang))
|
||||
|
||||
- Added block chart [\#81](https://github.com/Jawbone/JBChartView/pull/81) ([jhogervorst](https://github.com/jhogervorst))
|
||||
|
||||
## [v2.8.6](https://github.com/Jawbone/JBChartView/tree/v2.8.6) (2014-08-29)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.5...v2.8.6)
|
||||
|
||||
## [v2.8.5](https://github.com/Jawbone/JBChartView/tree/v2.8.5) (2014-08-28)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.4...v2.8.5)
|
||||
|
||||
**Implemented enhancements:**
|
||||
@@ -193,21 +224,17 @@
|
||||
- Invert option for bar charts [\#97](https://github.com/Jawbone/JBChartView/issues/97)
|
||||
|
||||
## [v2.8.4](https://github.com/Jawbone/JBChartView/tree/v2.8.4) (2014-08-27)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.3...v2.8.4)
|
||||
|
||||
## [v2.8.3](https://github.com/Jawbone/JBChartView/tree/v2.8.3) (2014-08-26)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.2...v2.8.3)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- return type of barPaddingForBarChartView: [\#93](https://github.com/Jawbone/JBChartView/issues/93)
|
||||
|
||||
- Custom dot functionality [\#92](https://github.com/Jawbone/JBChartView/issues/92)
|
||||
|
||||
## [v2.8.2](https://github.com/Jawbone/JBChartView/tree/v2.8.2) (2014-08-26)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.1...v2.8.2)
|
||||
|
||||
**Fixed bugs:**
|
||||
@@ -219,11 +246,9 @@
|
||||
- Changing dot radius in dotRadiusForLineAtLineIndex has no effect [\#95](https://github.com/Jawbone/JBChartView/issues/95)
|
||||
|
||||
## [v2.8.1](https://github.com/Jawbone/JBChartView/tree/v2.8.1) (2014-08-23)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.8.0...v2.8.1)
|
||||
|
||||
## [v2.8.0](https://github.com/Jawbone/JBChartView/tree/v2.8.0) (2014-08-15)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.7.4...v2.8.0)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -235,7 +260,6 @@
|
||||
- Add per-line vertical selection color support. [\#90](https://github.com/Jawbone/JBChartView/pull/90) ([mszaro](https://github.com/mszaro))
|
||||
|
||||
## [v2.7.4](https://github.com/Jawbone/JBChartView/tree/v2.7.4) (2014-08-15)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.7.3...v2.7.4)
|
||||
|
||||
**Implemented enhancements:**
|
||||
@@ -245,41 +269,31 @@
|
||||
**Closed issues:**
|
||||
|
||||
- \[Enhancement\] Provide an option to disable Y-axis normalization for line graphs [\#88](https://github.com/Jawbone/JBChartView/issues/88)
|
||||
|
||||
- Add left padding to the graph for y-axis labels [\#87](https://github.com/Jawbone/JBChartView/issues/87)
|
||||
|
||||
- Error Protocol not implemented [\#86](https://github.com/Jawbone/JBChartView/issues/86)
|
||||
|
||||
- What if I want to animate the LineChartView progression? [\#85](https://github.com/Jawbone/JBChartView/issues/85)
|
||||
|
||||
- Enabling smooth lines in line chart view results in line graph clipping \(Out of bounds\)? [\#83](https://github.com/Jawbone/JBChartView/issues/83)
|
||||
|
||||
## [v2.7.3](https://github.com/Jawbone/JBChartView/tree/v2.7.3) (2014-08-07)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.7.2...v2.7.3)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Can I animate bar charts? [\#79](https://github.com/Jawbone/JBChartView/issues/79)
|
||||
|
||||
- Create generic dataSource and delegate for extensibility. [\#78](https://github.com/Jawbone/JBChartView/issues/78)
|
||||
|
||||
## [v2.7.2](https://github.com/Jawbone/JBChartView/tree/v2.7.2) (2014-08-04)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.7.1...v2.7.2)
|
||||
|
||||
## [v2.7.1](https://github.com/Jawbone/JBChartView/tree/v2.7.1) (2014-08-04)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.7.0...v2.7.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Typo in JBBarChartView.h [\#77](https://github.com/Jawbone/JBChartView/issues/77)
|
||||
|
||||
- Refactor datasource and delegate functions [\#76](https://github.com/Jawbone/JBChartView/issues/76)
|
||||
|
||||
## [v2.7.0](https://github.com/Jawbone/JBChartView/tree/v2.7.0) (2014-08-02)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.6.3...v2.7.0)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -287,15 +301,12 @@
|
||||
- Incorrect padding on line widths [\#75](https://github.com/Jawbone/JBChartView/issues/75)
|
||||
|
||||
## [v2.6.3](https://github.com/Jawbone/JBChartView/tree/v2.6.3) (2014-07-31)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.6.2...v2.6.3)
|
||||
|
||||
## [v2.6.2](https://github.com/Jawbone/JBChartView/tree/v2.6.2) (2014-07-25)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.6.1...v2.6.2)
|
||||
|
||||
## [v2.6.1](https://github.com/Jawbone/JBChartView/tree/v2.6.1) (2014-07-25)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.6.0...v2.6.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -303,45 +314,28 @@
|
||||
- iOS 6 support [\#74](https://github.com/Jawbone/JBChartView/pull/74) ([luosheng](https://github.com/luosheng))
|
||||
|
||||
## [v2.6.0](https://github.com/Jawbone/JBChartView/tree/v2.6.0) (2014-07-24)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.5...v2.6.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Allow setting views for lines as well as bars [\#73](https://github.com/Jawbone/JBChartView/issues/73)
|
||||
|
||||
- Requires iOS 7 or later [\#72](https://github.com/Jawbone/JBChartView/issues/72)
|
||||
|
||||
- How to Know the x,width of each bar [\#70](https://github.com/Jawbone/JBChartView/issues/70)
|
||||
|
||||
- Multiple y-axis [\#69](https://github.com/Jawbone/JBChartView/issues/69)
|
||||
|
||||
- NSInternalInconsistencyException - JBLineChartView // dataSource must implement [\#68](https://github.com/Jawbone/JBChartView/issues/68)
|
||||
|
||||
- Zoom and scrolling? \(Question\) [\#65](https://github.com/Jawbone/JBChartView/issues/65)
|
||||
|
||||
- Support for missing data points rather than going to 0? [\#63](https://github.com/Jawbone/JBChartView/issues/63)
|
||||
|
||||
- Interesting behavior 'barViewAtIndex' and 'colorForBarViewAtIndex'. [\#62](https://github.com/Jawbone/JBChartView/issues/62)
|
||||
|
||||
- In LineChart x-axis, splits into more numbers and not able view x-axis last point [\#60](https://github.com/Jawbone/JBChartView/issues/60)
|
||||
|
||||
- Overlay more than 1 chart? [\#59](https://github.com/Jawbone/JBChartView/issues/59)
|
||||
|
||||
- Footer Views - Can they be perpendicular to the x-axis [\#58](https://github.com/Jawbone/JBChartView/issues/58)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fill Color on Line Chart [\#71](https://github.com/Jawbone/JBChartView/pull/71) ([legranddamien](https://github.com/legranddamien))
|
||||
|
||||
- Added documentation for usage in a Swift project [\#67](https://github.com/Jawbone/JBChartView/pull/67) ([jonparker](https://github.com/jonparker))
|
||||
|
||||
- barChartView corner radius support [\#66](https://github.com/Jawbone/JBChartView/pull/66) ([gabybarsky](https://github.com/gabybarsky))
|
||||
|
||||
- barWidth selector and better height normalization [\#64](https://github.com/Jawbone/JBChartView/pull/64) ([gabybarsky](https://github.com/gabybarsky))
|
||||
|
||||
## [v2.5.5](https://github.com/Jawbone/JBChartView/tree/v2.5.5) (2014-05-14)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.4...v2.5.5)
|
||||
|
||||
**Implemented enhancements:**
|
||||
@@ -351,55 +345,44 @@
|
||||
**Closed issues:**
|
||||
|
||||
- How to adjust selected bar height for LineChart in demo project [\#56](https://github.com/Jawbone/JBChartView/issues/56)
|
||||
|
||||
- Line Chart Collapse Animation Changed? [\#54](https://github.com/Jawbone/JBChartView/issues/54)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fixed barView's frame. [\#57](https://github.com/Jawbone/JBChartView/pull/57) ([pala](https://github.com/pala))
|
||||
|
||||
- Update sample code in readme [\#55](https://github.com/Jawbone/JBChartView/pull/55) ([pala](https://github.com/pala))
|
||||
|
||||
## [v2.5.4](https://github.com/Jawbone/JBChartView/tree/v2.5.4) (2014-05-07)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.3...v2.5.4)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add delegate method barChartView:colorForBarViewAtIndex: to JBBarChartViewDataSource [\#53](https://github.com/Jawbone/JBChartView/pull/53) ([skywinder](https://github.com/skywinder))
|
||||
|
||||
- Compiling warrnings and typos [\#52](https://github.com/Jawbone/JBChartView/pull/52) ([skywinder](https://github.com/skywinder))
|
||||
|
||||
## [v2.5.3](https://github.com/Jawbone/JBChartView/tree/v2.5.3) (2014-05-06)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.2...v2.5.3)
|
||||
|
||||
## [v2.5.2](https://github.com/Jawbone/JBChartView/tree/v2.5.2) (2014-05-06)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.1...v2.5.2)
|
||||
|
||||
## [v2.5.1](https://github.com/Jawbone/JBChartView/tree/v2.5.1) (2014-05-05)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.5.0...v2.5.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- non-evenly distributed plots [\#50](https://github.com/Jawbone/JBChartView/issues/50)
|
||||
|
||||
- Using minimum of 0 as default [\#48](https://github.com/Jawbone/JBChartView/issues/48)
|
||||
|
||||
## [v2.5.0](https://github.com/Jawbone/JBChartView/tree/v2.5.0) (2014-05-04)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.4.3...v2.5.0)
|
||||
|
||||
## [v2.4.3](https://github.com/Jawbone/JBChartView/tree/v2.4.3) (2014-05-04)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.4.2...v2.4.3)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Bars not drawn when all have the same height [\#47](https://github.com/Jawbone/JBChartView/issues/47)
|
||||
|
||||
- Moving podspec to root [\#46](https://github.com/Jawbone/JBChartView/issues/46)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -407,11 +390,9 @@
|
||||
- fixed typo, renamed mininum -\> minimum [\#49](https://github.com/Jawbone/JBChartView/pull/49) ([simonnickel](https://github.com/simonnickel))
|
||||
|
||||
## [v2.4.2](https://github.com/Jawbone/JBChartView/tree/v2.4.2) (2014-05-01)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.4.1...v2.4.2)
|
||||
|
||||
## [v2.4.1](https://github.com/Jawbone/JBChartView/tree/v2.4.1) (2014-04-30)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.4.0...v2.4.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
@@ -427,23 +408,18 @@
|
||||
- Smooth curve fix [\#45](https://github.com/Jawbone/JBChartView/pull/45) ([ktran03](https://github.com/ktran03))
|
||||
|
||||
## [v2.4.0](https://github.com/Jawbone/JBChartView/tree/v2.4.0) (2014-04-28)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.3.0...v2.4.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Aligning X-axis, padding? [\#43](https://github.com/Jawbone/JBChartView/issues/43)
|
||||
|
||||
- `JBLineChartFooterView` Value Marks are Inaccurate [\#41](https://github.com/Jawbone/JBChartView/issues/41)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- smooth curve using bezierpath [\#39](https://github.com/Jawbone/JBChartView/pull/39) ([ktran03](https://github.com/ktran03))
|
||||
|
||||
- Allowing custom views for dots [\#40](https://github.com/Jawbone/JBChartView/pull/40) ([simonbromberg](https://github.com/simonbromberg))
|
||||
|
||||
## [v2.3.0](https://github.com/Jawbone/JBChartView/tree/v2.3.0) (2014-04-18)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.2.0...v2.3.0)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -451,37 +427,26 @@
|
||||
- Crash, lineChartView "must implement numberOfPointsInLineChartView" ? [\#38](https://github.com/Jawbone/JBChartView/issues/38)
|
||||
|
||||
## [v2.2.0](https://github.com/Jawbone/JBChartView/tree/v2.2.0) (2014-04-14)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.6...v2.2.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Typo in readme? [\#36](https://github.com/Jawbone/JBChartView/issues/36)
|
||||
|
||||
- Nav bar background remains black [\#35](https://github.com/Jawbone/JBChartView/issues/35)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added delegate method to configure the width of the vertical selection bar in a line chart. [\#37](https://github.com/Jawbone/JBChartView/pull/37) ([ghost](https://github.com/ghost))
|
||||
|
||||
- Add variable dot colors [\#34](https://github.com/Jawbone/JBChartView/pull/34) ([alexdunn](https://github.com/alexdunn))
|
||||
|
||||
## [v2.1.6](https://github.com/Jawbone/JBChartView/tree/v2.1.6) (2014-04-11)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.5...v2.1.6)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- `showsLineSelection` Does Nothing [\#33](https://github.com/Jawbone/JBChartView/issues/33)
|
||||
|
||||
- cocoapods [\#31](https://github.com/Jawbone/JBChartView/issues/31)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add variable dot colors [\#32](https://github.com/Jawbone/JBChartView/pull/32) ([alexdunn](https://github.com/alexdunn))
|
||||
|
||||
## [v2.1.5](https://github.com/Jawbone/JBChartView/tree/v2.1.5) (2014-04-10)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.4...v2.1.5)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -489,41 +454,32 @@
|
||||
- JBChartView: needs initWithCoder initializer for inflation from a xib [\#30](https://github.com/Jawbone/JBChartView/pull/30) ([gumbypp](https://github.com/gumbypp))
|
||||
|
||||
## [v2.1.4](https://github.com/Jawbone/JBChartView/tree/v2.1.4) (2014-04-10)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.3...v2.1.4)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Stach bars? [\#29](https://github.com/Jawbone/JBChartView/issues/29)
|
||||
|
||||
- Problem with Bar Height [\#28](https://github.com/Jawbone/JBChartView/issues/28)
|
||||
|
||||
- Selection does not work without Auto Layout in iPhone 3.5" [\#27](https://github.com/Jawbone/JBChartView/issues/27)
|
||||
|
||||
## [v2.1.3](https://github.com/Jawbone/JBChartView/tree/v2.1.3) (2014-04-03)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.2...v2.1.3)
|
||||
|
||||
## [v2.1.2](https://github.com/Jawbone/JBChartView/tree/v2.1.2) (2014-04-02)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.1...v2.1.2)
|
||||
|
||||
## [v2.1.1](https://github.com/Jawbone/JBChartView/tree/v2.1.1) (2014-04-02)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.1.0...v2.1.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- representing negative values [\#26](https://github.com/Jawbone/JBChartView/issues/26)
|
||||
|
||||
- Real time [\#25](https://github.com/Jawbone/JBChartView/issues/25)
|
||||
|
||||
## [v2.1.0](https://github.com/Jawbone/JBChartView/tree/v2.1.0) (2014-03-31)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.0.2...v2.1.0)
|
||||
|
||||
## [v2.0.2](https://github.com/Jawbone/JBChartView/tree/v2.0.2) (2014-03-27)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.0.1...v2.0.2)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -531,7 +487,6 @@
|
||||
- Negative Bars? [\#24](https://github.com/Jawbone/JBChartView/issues/24)
|
||||
|
||||
## [v2.0.1](https://github.com/Jawbone/JBChartView/tree/v2.0.1) (2014-03-19)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v2.0.0...v2.0.1)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -539,7 +494,6 @@
|
||||
- NSAssert on bar height in JBBarChartView, \> 0 when it should be \>= [\#22](https://github.com/Jawbone/JBChartView/issues/22)
|
||||
|
||||
## [v2.0.0](https://github.com/Jawbone/JBChartView/tree/v2.0.0) (2014-03-19)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.6...v2.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
@@ -549,11 +503,8 @@
|
||||
**Closed issues:**
|
||||
|
||||
- JBBarChartView Bar Height Problem With Storyboard/Autolayout [\#19](https://github.com/Jawbone/JBChartView/issues/19)
|
||||
|
||||
- blank result [\#18](https://github.com/Jawbone/JBChartView/issues/18)
|
||||
|
||||
- Bar Highlight Offset Issue [\#17](https://github.com/Jawbone/JBChartView/issues/17)
|
||||
|
||||
- Following the tutorial creates an empty/blank view [\#16](https://github.com/Jawbone/JBChartView/issues/16)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -561,35 +512,25 @@
|
||||
- Multiple Line Support & Tooltips [\#21](https://github.com/Jawbone/JBChartView/pull/21) ([terryworona](https://github.com/terryworona))
|
||||
|
||||
## [v1.1.6](https://github.com/Jawbone/JBChartView/tree/v1.1.6) (2014-03-03)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.5...v1.1.6)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added customization for line width [\#15](https://github.com/Jawbone/JBChartView/pull/15) ([kmcbride](https://github.com/kmcbride))
|
||||
|
||||
- fix spelling mistake [\#14](https://github.com/Jawbone/JBChartView/pull/14) ([Undo1](https://github.com/Undo1))
|
||||
|
||||
## [v1.1.5](https://github.com/Jawbone/JBChartView/tree/v1.1.5) (2014-02-12)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.4...v1.1.5)
|
||||
|
||||
## [v1.1.4](https://github.com/Jawbone/JBChartView/tree/v1.1.4) (2014-02-06)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.3...v1.1.4)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- What about ios6 [\#13](https://github.com/Jawbone/JBChartView/issues/13)
|
||||
|
||||
- Could the Bar Chart data be nil ? [\#12](https://github.com/Jawbone/JBChartView/issues/12)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added minimum height and touch point on delegate method [\#10](https://github.com/Jawbone/JBChartView/pull/10) ([didats](https://github.com/didats))
|
||||
|
||||
## [v1.1.3](https://github.com/Jawbone/JBChartView/tree/v1.1.3) (2014-01-06)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.2...v1.1.3)
|
||||
|
||||
**Closed issues:**
|
||||
@@ -601,17 +542,14 @@
|
||||
- Fixes small typos in README [\#11](https://github.com/Jawbone/JBChartView/pull/11) ([sampage](https://github.com/sampage))
|
||||
|
||||
## [v1.1.2](https://github.com/Jawbone/JBChartView/tree/v1.1.2) (2014-01-03)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.1...v1.1.2)
|
||||
|
||||
## [v1.1.1](https://github.com/Jawbone/JBChartView/tree/v1.1.1) (2014-01-02)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.1.0...v1.1.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- reloadData is not reducing height of other bars [\#7](https://github.com/Jawbone/JBChartView/issues/7)
|
||||
|
||||
- Add barViewAtIndex: to datasource [\#5](https://github.com/Jawbone/JBChartView/issues/5)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -619,27 +557,19 @@
|
||||
- Reset cached max height on data reload Fixes \#7 [\#8](https://github.com/Jawbone/JBChartView/pull/8) ([l4u](https://github.com/l4u))
|
||||
|
||||
## [v1.1.0](https://github.com/Jawbone/JBChartView/tree/v1.1.0) (2013-12-26)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.0.3...v1.1.0)
|
||||
|
||||
## [v1.0.3](https://github.com/Jawbone/JBChartView/tree/v1.0.3) (2013-12-23)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.0.2...v1.0.3)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Single press on bar [\#3](https://github.com/Jawbone/JBChartView/issues/3)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added barShadowEnabled property and improved performance of the shadow [\#4](https://github.com/Jawbone/JBChartView/pull/4) ([wtsnz](https://github.com/wtsnz))
|
||||
|
||||
## [v1.0.2](https://github.com/Jawbone/JBChartView/tree/v1.0.2) (2013-12-17)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.0.1...v1.0.2)
|
||||
|
||||
## [v1.0.1](https://github.com/Jawbone/JBChartView/tree/v1.0.1) (2013-12-15)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/v1.0...v1.0.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -647,7 +577,6 @@
|
||||
- If there is no footer view, bars won't be added to the chart. [\#2](https://github.com/Jawbone/JBChartView/pull/2) ([joelkraut](https://github.com/joelkraut))
|
||||
|
||||
## [v1.0](https://github.com/Jawbone/JBChartView/tree/v1.0) (2013-12-11)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/0.2.0...v1.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
@@ -655,15 +584,12 @@
|
||||
- JBChartViewDemo and JBChartView [\#1](https://github.com/Jawbone/JBChartView/pull/1) ([terryworona](https://github.com/terryworona))
|
||||
|
||||
## [0.2.0](https://github.com/Jawbone/JBChartView/tree/0.2.0) (2013-08-20)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/1.0...0.2.0)
|
||||
|
||||
## [1.0](https://github.com/Jawbone/JBChartView/tree/1.0) (2012-04-23)
|
||||
|
||||
[Full Changelog](https://github.com/Jawbone/JBChartView/compare/1.0.0...1.0)
|
||||
|
||||
## [1.0.0](https://github.com/Jawbone/JBChartView/tree/1.0.0) (2012-04-23)
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// NSMutableArray+JBStack.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSMutableArray (JBStack)
|
||||
|
||||
- (void)jb_push:(id)object;
|
||||
- (id)jb_pop;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NSMutableArray+JBStack.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSMutableArray+JBStack.h"
|
||||
|
||||
@implementation NSMutableArray (JBStack)
|
||||
|
||||
#pragma mark - Operations
|
||||
|
||||
- (void)jb_push:(id)object
|
||||
{
|
||||
if (object != nil)
|
||||
{
|
||||
[self insertObject:object atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)jb_pop
|
||||
{
|
||||
id object = [self firstObject];
|
||||
if (object != nil)
|
||||
{
|
||||
[self removeObjectAtIndex:0];
|
||||
return object;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -80,7 +80,7 @@
|
||||
* If you already implement barChartView:barViewAtIndex: delegate - this method has no effect.
|
||||
* If a custom UIView isn't supplied, a flat bar will be made automatically (default color black).
|
||||
*
|
||||
* Default: if none specified - calls barChartView:barViewAtIndex:.
|
||||
* Default: black color.
|
||||
*
|
||||
* @param barChartView The bar chart object requesting this information.
|
||||
* @param index The 0-based index of a given bar (left to right, x-axis).
|
||||
@@ -89,6 +89,19 @@
|
||||
*/
|
||||
- (UIColor *)barChartView:(JBBarChartView *)barChartView colorForBarViewAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
* If you already implement barChartView:barViewAtIndex: delegate - this method has no effect.
|
||||
* If a custom UIView isn't supplied and barChartView:colorForBarViewAtIndex: isn't implemented, then
|
||||
* a gradient layer may be supplied to be used across all bars within the chart.
|
||||
*
|
||||
* Default: black color.
|
||||
*
|
||||
* @param barChartView The bar chart object requesting this information.
|
||||
*
|
||||
* @return The gradient layer to be used as a mask over all bars within the chart.
|
||||
*/
|
||||
- (CAGradientLayer *)barGradientForBarChartView:(JBBarChartView *)barChartView;
|
||||
|
||||
/**
|
||||
* The selection color to be overlayed on a bar during touch events.
|
||||
* The color is automatically faded to transparent (vertically). The property showsVerticalSelection
|
||||
@@ -120,13 +133,6 @@
|
||||
@property (nonatomic, weak) id<JBBarChartViewDataSource> dataSource;
|
||||
@property (nonatomic, weak) id<JBBarChartViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Vertical highlight overlayed on bar during touch events.
|
||||
*
|
||||
* Default: YES.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL showsVerticalSelection;
|
||||
|
||||
/*
|
||||
* Bars can be (vertically) positoned top to bottom instead of bottom up.
|
||||
* If this property is set to YES, both the bar and the selection view will be inverted.
|
||||
@@ -136,6 +142,34 @@
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isInverted) BOOL inverted;
|
||||
|
||||
/*
|
||||
* Reloads the bar chart with a custom animation.
|
||||
* Adding, removing or modifying existing bars will be animated (collapsing, expanding, etc) if animated = YES.
|
||||
* Reloading (animated) data is thread safe and can be executed any number of times in succession.
|
||||
*
|
||||
* Default: a non-animated reload (via reloadData).
|
||||
*/
|
||||
- (void)reloadDataAnimated:(BOOL)animated;
|
||||
|
||||
/*
|
||||
* When reloadData or reloadDataAnimated: is called, the reloading bit is turned on.
|
||||
* State changes during a reload will be ignored. As well, subsequent calls to reloadData:
|
||||
* or reloadDataAnimated: before any previous reloads are complete, will also be ignored.
|
||||
* Lastly, all touch events will be ignored until a reload has compeleted.
|
||||
*
|
||||
* Note: the above restrictions apply only to animated reloads, as non-animated reloads are synchronous.
|
||||
*
|
||||
* Default: NO.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL reloading;
|
||||
|
||||
/**
|
||||
* Vertical highlight overlayed on bar during touch events.
|
||||
*
|
||||
* Default: YES.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL showsVerticalSelection;
|
||||
|
||||
/**
|
||||
* The bar view at a particular index.
|
||||
*
|
||||
@@ -0,0 +1,914 @@
|
||||
//
|
||||
// JBBarChartView.m
|
||||
// Nudge
|
||||
//
|
||||
// Created by Terry Worona on 9/3/13.
|
||||
// Copyright (c) 2013 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBBarChartView.h"
|
||||
|
||||
// Views
|
||||
#import "JBGradientBarView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBBarChartViewBarBasePaddingMutliplier = 50.0f;
|
||||
CGFloat const kJBBarChartViewUndefinedCachedHeight = -1.0f;
|
||||
CGFloat const kJBBarChartViewStateAnimationDuration = 0.05f;
|
||||
CGFloat const kJBBarChartViewReloadDataAnimationDuration = 0.15f;
|
||||
CGFloat const kJBBarChartViewStatePopOffset = 10.0f;
|
||||
NSInteger const kJBBarChartViewUndefinedBarIndex = -1;
|
||||
|
||||
// Colors (JBChartView)
|
||||
static UIColor *kJBBarChartViewDefaultBarColor = nil;
|
||||
|
||||
@interface JBChartView (Private)
|
||||
|
||||
- (BOOL)hasMaximumValue;
|
||||
- (BOOL)hasMinimumValue;
|
||||
|
||||
@end
|
||||
|
||||
@interface JBBarChartView () <JBGradientBarViewDataSource>
|
||||
|
||||
@property (nonatomic, strong) NSArray *chartData; // index = column, value = height
|
||||
@property (nonatomic, strong) NSArray *barViews;
|
||||
@property (nonatomic, strong) NSArray *cachedBarViewHeights;
|
||||
@property (nonatomic, assign) CGFloat barPadding;
|
||||
@property (nonatomic, assign) CGFloat cachedMaxHeight;
|
||||
@property (nonatomic, assign) CGFloat cachedMinHeight;
|
||||
@property (nonatomic, strong) JBChartVerticalSelectionView *verticalSelectionView;
|
||||
@property (nonatomic, assign) BOOL verticalSelectionViewVisible;
|
||||
@property (nonatomic, assign) BOOL reloading;
|
||||
|
||||
// Initialization
|
||||
- (void)construct;
|
||||
|
||||
// View quick accessors
|
||||
- (CGFloat)availableHeight;
|
||||
- (CGFloat)normalizedHeightForRawHeight:(NSNumber *)rawHeight;
|
||||
- (CGFloat)barWidth;
|
||||
|
||||
// Touch helpers
|
||||
- (NSInteger)barViewIndexForPoint:(CGPoint)point;
|
||||
- (UIView *)barViewForForPoint:(CGPoint)point;
|
||||
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches;
|
||||
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches;
|
||||
|
||||
// Setters
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated;
|
||||
|
||||
// Helpers
|
||||
- (UIView *)createBarViewForIndex:(NSUInteger)index;
|
||||
- (void)insertBarView:(UIView *)barView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBBarChartView
|
||||
|
||||
@dynamic dataSource;
|
||||
@dynamic delegate;
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [JBBarChartView class])
|
||||
{
|
||||
kJBBarChartViewDefaultBarColor = [UIColor blackColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)construct
|
||||
{
|
||||
_showsVerticalSelection = YES;
|
||||
_cachedMinHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
_cachedMaxHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
}
|
||||
|
||||
#pragma mark - Memory Management
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated
|
||||
{
|
||||
if (self.reloading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.reloading = YES;
|
||||
|
||||
// Reset cached max height
|
||||
self.cachedMinHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
self.cachedMaxHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
|
||||
// Animation check
|
||||
BOOL shouldAnimate = (animated && self.state == JBChartViewStateExpanded);
|
||||
|
||||
/*
|
||||
* Final block to refresh state and turn off reloading bit
|
||||
*/
|
||||
dispatch_block_t completionBlock = ^{
|
||||
self.reloading = NO;
|
||||
[self setState:self.state animated:NO force:YES callback:nil];
|
||||
};
|
||||
|
||||
/*
|
||||
* The data collection holds all position information:
|
||||
* constructed via datasource and delegate functions
|
||||
*/
|
||||
dispatch_block_t createDataDictionariesBlock = ^{
|
||||
|
||||
// Grab the count
|
||||
NSAssert([self.dataSource respondsToSelector:@selector(numberOfBarsInBarChartView:)], @"JBBarChartView // datasource must implement - (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView");
|
||||
NSUInteger dataCount = [self.dataSource numberOfBarsInBarChartView:self];
|
||||
|
||||
// Build up the data collection
|
||||
NSAssert([self.delegate respondsToSelector:@selector(barChartView:heightForBarViewAtIndex:)], @"JBBarChartView // delegate must implement - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index");
|
||||
NSMutableArray *mutableChartData = [NSMutableArray array];
|
||||
for (NSUInteger index=0; index<dataCount; index++)
|
||||
{
|
||||
CGFloat height = [self.delegate barChartView:self heightForBarViewAtIndex:index];
|
||||
NSAssert(height >= 0, @"JBBarChartView // datasource function - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index must return a CGFloat >= 0");
|
||||
[mutableChartData addObject:[NSNumber numberWithFloat:height]];
|
||||
}
|
||||
self.chartData = [NSArray arrayWithArray:mutableChartData];
|
||||
};
|
||||
|
||||
/*
|
||||
* Determines the padding between bars as a function of # of bars
|
||||
*/
|
||||
dispatch_block_t createBarPaddingBlock = ^{
|
||||
if ([self.delegate respondsToSelector:@selector(barPaddingForBarChartView:)])
|
||||
{
|
||||
self.barPadding = [self.delegate barPaddingForBarChartView:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger totalBars = [self.chartData count];
|
||||
self.barPadding = (1/(float)totalBars) * kJBBarChartViewBarBasePaddingMutliplier;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a vertical selection view for touch events
|
||||
*/
|
||||
dispatch_block_t createSelectionViewBlock = ^{
|
||||
|
||||
// Remove old selection bar
|
||||
if (self.verticalSelectionView)
|
||||
{
|
||||
[self.verticalSelectionView removeFromSuperview];
|
||||
self.verticalSelectionView = nil;
|
||||
}
|
||||
|
||||
CGFloat verticalSelectionViewHeight = self.bounds.size.height - self.headerView.frame.size.height - self.footerView.frame.size.height - self.headerPadding - self.footerPadding;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoHeaderPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoHeaderPaddingForChartView:self])
|
||||
{
|
||||
verticalSelectionViewHeight += self.headerPadding;
|
||||
}
|
||||
}
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoFooterPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoFooterPaddingForChartView:self])
|
||||
{
|
||||
verticalSelectionViewHeight += self.footerPadding;
|
||||
}
|
||||
}
|
||||
|
||||
self.verticalSelectionView = [[JBChartVerticalSelectionView alloc] initWithFrame:CGRectMake(0, 0, [self barWidth], verticalSelectionViewHeight)];
|
||||
self.verticalSelectionView.alpha = 0.0;
|
||||
self.verticalSelectionView.hidden = !self.showsVerticalSelection;
|
||||
if ([self.delegate respondsToSelector:@selector(barSelectionColorForBarChartView:)])
|
||||
{
|
||||
UIColor *selectionViewBackgroundColor = [self.delegate barSelectionColorForBarChartView:self];
|
||||
NSAssert(selectionViewBackgroundColor != nil, @"JBBarChartView // delegate function - (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView must return a non-nil UIColor");
|
||||
self.verticalSelectionView.bgColor = selectionViewBackgroundColor;
|
||||
}
|
||||
|
||||
// Add new selection bar
|
||||
if (self.footerView)
|
||||
{
|
||||
[self insertSubview:self.verticalSelectionView belowSubview:self.footerView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self addSubview:self.verticalSelectionView];
|
||||
}
|
||||
|
||||
self.verticalSelectionView.transform = self.inverted ? CGAffineTransformMakeScale(1.0, -1.0) : CGAffineTransformIdentity;
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a new bar graph view using the previously calculated data model
|
||||
*/
|
||||
dispatch_block_t createBarViewsBlock = ^{
|
||||
|
||||
__weak JBBarChartView* weakSelf = self;
|
||||
|
||||
if (shouldAnimate)
|
||||
{
|
||||
self.cachedBarViewHeights = nil;
|
||||
__block NSUInteger barViewsCount = [self.barViews count];
|
||||
|
||||
dispatch_block_t updateExistingBarViewsBlock = ^{
|
||||
__block CGFloat xOffset = 0;
|
||||
[weakSelf.chartData enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
CGFloat height = [weakSelf normalizedHeightForRawHeight:(NSNumber *)obj];
|
||||
if (index < [weakSelf.barViews count])
|
||||
{
|
||||
// Update bar
|
||||
UIView *barView = [weakSelf.barViews objectAtIndex:index];
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
barView.frame = CGRectMake(xOffset, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, [weakSelf barWidth], height);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(xOffset, weakSelf.bounds.size.height - height - weakSelf.footerView.frame.size.height, [weakSelf barWidth], height);
|
||||
}
|
||||
xOffset += ([weakSelf barWidth] + weakSelf.barPadding);
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
dispatch_block_t preAddBarViewsBlock = ^{
|
||||
__block CGFloat xOffset = 0;
|
||||
[self.chartData enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
if (index >= barViewsCount)
|
||||
{
|
||||
// Create bar
|
||||
UIView *barView = [weakSelf createBarViewForIndex:index];
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
barView.frame = CGRectMake(xOffset, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, [weakSelf barWidth], 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(xOffset, self.bounds.size.height, [weakSelf barWidth], 0.0f);
|
||||
}
|
||||
|
||||
// Update stored view
|
||||
weakSelf.barViews = [NSArray arrayWithArray:[weakSelf.barViews arrayByAddingObject:barView]];
|
||||
|
||||
// Add bar to view
|
||||
[weakSelf insertBarView:barView];
|
||||
}
|
||||
xOffset += ([weakSelf barWidth] + weakSelf.barPadding);
|
||||
}];
|
||||
};
|
||||
|
||||
dispatch_block_t postAddBarViewsBlock = ^{
|
||||
[self.chartData enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
if (index >= barViewsCount)
|
||||
{
|
||||
CGFloat height = [weakSelf normalizedHeightForRawHeight:(NSNumber *)obj];
|
||||
UIView *barView = [weakSelf.barViews objectAtIndex:index];
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, [weakSelf barWidth], height);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, self.bounds.size.height - height - weakSelf.footerView.frame.size.height, [weakSelf barWidth], height);
|
||||
}
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
dispatch_block_t preRemoveBarViewsBlock = ^{
|
||||
|
||||
// Move existing (removed) bars down
|
||||
for (NSUInteger index=[weakSelf.chartData count]; index<[weakSelf.barViews count]; index++)
|
||||
{
|
||||
UIView *barView = [weakSelf.barViews objectAtIndex:index];
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height, barView.frame.size.width, barView.frame.size.height);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_block_t postRemoveBarViewsBlock = ^{
|
||||
|
||||
// Remove existing (removed) bars
|
||||
for (NSUInteger index=[weakSelf.chartData count]; index<[weakSelf.barViews count]; index++)
|
||||
{
|
||||
UIView *barView = [weakSelf.barViews objectAtIndex:index];
|
||||
[barView removeFromSuperview];
|
||||
}
|
||||
|
||||
// Update bar view collection
|
||||
NSMutableArray *mutableBarViews = [NSMutableArray arrayWithArray:weakSelf.barViews];
|
||||
[mutableBarViews removeObjectsInRange:(NSRange){[weakSelf.chartData count], [weakSelf.barViews count] - [weakSelf.chartData count]}];
|
||||
weakSelf.barViews = [NSArray arrayWithArray:mutableBarViews];
|
||||
};
|
||||
|
||||
dispatch_block_t refreshedCachedBarViewHeightsBlock = ^{
|
||||
NSMutableArray *mutableCachedBarViewHeights = [NSMutableArray arrayWithArray:weakSelf.cachedBarViewHeights];
|
||||
for (UIView *barView in weakSelf.barViews)
|
||||
{
|
||||
[mutableCachedBarViewHeights addObject:[NSNumber numberWithFloat:barView.frame.size.height]];
|
||||
}
|
||||
weakSelf.cachedBarViewHeights = [NSArray arrayWithArray:mutableCachedBarViewHeights];
|
||||
};
|
||||
|
||||
/*
|
||||
* New data model equal;
|
||||
* Update existing bars to accomodate new model.
|
||||
*/
|
||||
if ([self.chartData count] == [self.barViews count])
|
||||
{
|
||||
[UIView animateWithDuration:kJBBarChartViewReloadDataAnimationDuration animations:^{
|
||||
updateExistingBarViewsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
refreshedCachedBarViewHeightsBlock();
|
||||
completionBlock();
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
* New data model greater;
|
||||
* Update existing bars to accomodate new model & add new bars.
|
||||
*/
|
||||
else if ([self.chartData count] > [self.barViews count])
|
||||
{
|
||||
[UIView animateWithDuration:kJBBarChartViewReloadDataAnimationDuration animations:^{
|
||||
updateExistingBarViewsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
preAddBarViewsBlock();
|
||||
[UIView animateWithDuration:kJBBarChartViewReloadDataAnimationDuration delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
|
||||
postAddBarViewsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
refreshedCachedBarViewHeightsBlock();
|
||||
completionBlock();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
* New data model less;
|
||||
* Update existing bars to accomodate new model & remove legacy bars.
|
||||
*/
|
||||
else if ([self.chartData count] < [self.barViews count])
|
||||
{
|
||||
[UIView animateWithDuration:kJBBarChartViewReloadDataAnimationDuration animations:^{
|
||||
preRemoveBarViewsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
postRemoveBarViewsBlock();
|
||||
[UIView animateWithDuration:kJBBarChartViewReloadDataAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
updateExistingBarViewsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
refreshedCachedBarViewHeightsBlock();
|
||||
completionBlock();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove old bars
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
[barView removeFromSuperview];
|
||||
}
|
||||
|
||||
self.cachedBarViewHeights = nil;
|
||||
|
||||
__block CGFloat xOffset = 0;
|
||||
__block NSMutableArray *mutableBarViews = [NSMutableArray array];
|
||||
__block NSMutableArray *mutableCachedBarViewHeights = [NSMutableArray array];
|
||||
[self.chartData enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
UIView *barView = [weakSelf createBarViewForIndex:index];
|
||||
|
||||
CGFloat height = [weakSelf normalizedHeightForRawHeight:(NSNumber *)obj];
|
||||
barView.frame = CGRectMake(xOffset, self.bounds.size.height - height - weakSelf.footerView.frame.size.height, [weakSelf barWidth], height);
|
||||
[mutableBarViews addObject:barView];
|
||||
[mutableCachedBarViewHeights addObject:[NSNumber numberWithFloat:height]];
|
||||
|
||||
[weakSelf insertBarView:barView];
|
||||
|
||||
xOffset += ([weakSelf barWidth] + weakSelf.barPadding);
|
||||
index++;
|
||||
}];
|
||||
self.barViews = [NSArray arrayWithArray:mutableBarViews];
|
||||
self.cachedBarViewHeights = [NSArray arrayWithArray:mutableCachedBarViewHeights];
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_block_t layoutHeaderAndFooterBlock = ^{
|
||||
self.headerView.frame = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.headerView.frame.size.height);
|
||||
self.footerView.frame = CGRectMake(self.bounds.origin.x, self.bounds.size.height - self.footerView.frame.size.height, self.bounds.size.width, self.footerView.frame.size.height);
|
||||
};
|
||||
|
||||
/*
|
||||
* Reload data is broken down into various smaller units of work:
|
||||
*
|
||||
* 1. Create a data model
|
||||
* 2. Fetch the bar padding
|
||||
* 3. Create a (vertical) selection view
|
||||
* 4. Create and position bar view(s)
|
||||
* 5. Layout header & footer
|
||||
* 6. Refresh chart state
|
||||
*
|
||||
*/
|
||||
createDataDictionariesBlock();
|
||||
createBarPaddingBlock();
|
||||
createSelectionViewBlock();
|
||||
createBarViewsBlock();
|
||||
layoutHeaderAndFooterBlock();
|
||||
|
||||
if (!shouldAnimate)
|
||||
{
|
||||
completionBlock(); // animated versions call this internally
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
[self reloadDataAnimated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - View Quick Accessors
|
||||
|
||||
- (CGFloat)availableHeight
|
||||
{
|
||||
return self.bounds.size.height - self.headerView.frame.size.height - self.footerView.frame.size.height - self.headerPadding - self.footerPadding;
|
||||
}
|
||||
|
||||
- (CGFloat)normalizedHeightForRawHeight:(NSNumber *)rawHeight
|
||||
{
|
||||
CGFloat minHeight = [self minimumValue];
|
||||
CGFloat maxHeight = [self maximumValue];
|
||||
CGFloat value = [rawHeight floatValue];
|
||||
|
||||
if ((maxHeight - minHeight) <= 0)
|
||||
{
|
||||
return [self availableHeight];
|
||||
}
|
||||
|
||||
return ((value - minHeight) / (maxHeight - minHeight)) * [self availableHeight];
|
||||
}
|
||||
|
||||
- (CGFloat)barWidth
|
||||
{
|
||||
NSUInteger barCount = [self.chartData count];
|
||||
if (barCount > 0)
|
||||
{
|
||||
CGFloat totalPadding = (barCount - 1) * self.barPadding;
|
||||
CGFloat availableWidth = self.bounds.size.width - totalPadding;
|
||||
return availableWidth / barCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated force:(BOOL)force callback:(void (^)())callback
|
||||
{
|
||||
if (self.reloading)
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
return; // ignore state changes when reloading
|
||||
}
|
||||
|
||||
[super setState:state animated:animated force:force callback:callback];
|
||||
|
||||
__weak JBBarChartView* weakSelf = self;
|
||||
|
||||
void (^updateBarView)(UIView *barView, BOOL popBar);
|
||||
|
||||
updateBarView = ^(UIView *barView, BOOL popBar) {
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
if (weakSelf.state == JBChartViewStateExpanded)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue]);
|
||||
}
|
||||
}
|
||||
else if (weakSelf.state == JBChartViewStateCollapsed)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weakSelf.state == JBChartViewStateExpanded)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] - kJBBarChartViewStatePopOffset, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue], barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue]);
|
||||
}
|
||||
}
|
||||
else if (weakSelf.state == JBChartViewStateCollapsed)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] - kJBBarChartViewStatePopOffset, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height, barView.frame.size.width, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_block_t callbackCopy = [callback copy];
|
||||
|
||||
if ([self.barViews count] > 0)
|
||||
{
|
||||
if (animated)
|
||||
{
|
||||
dispatch_block_t animationCompletionBlock = ^{
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
};
|
||||
|
||||
NSUInteger animationDelayIndex = 0;
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
BOOL lastIndex = ((NSUInteger)barView.tag == [self.barViews count] - 1);
|
||||
if ([[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] > [self minimumValue])
|
||||
{
|
||||
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:(kJBBarChartViewStateAnimationDuration * 0.5) * animationDelayIndex options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
updateBarView(barView, YES);
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
updateBarView(barView, NO);
|
||||
} completion:^(BOOL lastBarFinished) {
|
||||
if (lastIndex)
|
||||
{
|
||||
animationCompletionBlock();
|
||||
}
|
||||
}];
|
||||
}];
|
||||
animationDelayIndex++;
|
||||
}
|
||||
else if (lastIndex)
|
||||
{
|
||||
animationCompletionBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
updateBarView(barView, NO);
|
||||
}
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
[self setState:state animated:animated force:NO callback:callback];
|
||||
}
|
||||
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated
|
||||
{
|
||||
_verticalSelectionViewVisible = verticalSelectionViewVisible;
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
self.verticalSelectionView.alpha = self.verticalSelectionViewVisible ? 1.0 : 0.0;
|
||||
} completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.verticalSelectionView.alpha = _verticalSelectionViewVisible ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible
|
||||
{
|
||||
[self setVerticalSelectionViewVisible:verticalSelectionViewVisible animated:NO];
|
||||
}
|
||||
|
||||
- (void)setShowsVerticalSelection:(BOOL)showsVerticalSelection
|
||||
{
|
||||
_showsVerticalSelection = showsVerticalSelection;
|
||||
self.verticalSelectionView.hidden = _showsVerticalSelection ? NO : YES;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (CGFloat)cachedMinHeight
|
||||
{
|
||||
if(_cachedMinHeight == kJBBarChartViewUndefinedCachedHeight)
|
||||
{
|
||||
NSArray *chartValues = [self.chartData sortedArrayUsingSelector:@selector(compare:)];
|
||||
_cachedMinHeight = [[chartValues firstObject] floatValue];
|
||||
}
|
||||
return _cachedMinHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)cachedMaxHeight
|
||||
{
|
||||
if (_cachedMaxHeight == kJBBarChartViewUndefinedCachedHeight)
|
||||
{
|
||||
NSArray *chartValues = [self.chartData sortedArrayUsingSelector:@selector(compare:)];
|
||||
_cachedMaxHeight = [[chartValues lastObject] floatValue];
|
||||
}
|
||||
return _cachedMaxHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)minimumValue
|
||||
{
|
||||
if ([self hasMinimumValue])
|
||||
{
|
||||
return fminf(self.cachedMinHeight, [super minimumValue]);
|
||||
}
|
||||
return self.cachedMinHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)maximumValue
|
||||
{
|
||||
if ([self hasMaximumValue])
|
||||
{
|
||||
return fmaxf(self.cachedMaxHeight, [super maximumValue]);
|
||||
}
|
||||
return self.cachedMaxHeight;
|
||||
}
|
||||
|
||||
- (UIView *)barViewAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index < [self.barViews count])
|
||||
{
|
||||
return [self.barViews objectAtIndex:index];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (UIView *)createBarViewForIndex:(NSUInteger)index
|
||||
{
|
||||
UIView *barView = nil;
|
||||
{
|
||||
// Custom bar
|
||||
if ([self.dataSource respondsToSelector:@selector(barChartView:barViewAtIndex:)])
|
||||
{
|
||||
UIView *customBarView = [self.dataSource barChartView:self barViewAtIndex:index];
|
||||
if (customBarView != nil)
|
||||
{
|
||||
barView = customBarView;
|
||||
}
|
||||
}
|
||||
|
||||
// Color bar
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:colorForBarViewAtIndex:)] && barView == nil)
|
||||
{
|
||||
UIColor *backgroundColor = [self.delegate barChartView:self colorForBarViewAtIndex:index];
|
||||
if (backgroundColor != nil)
|
||||
{
|
||||
barView = [[UIView alloc] init];
|
||||
barView.backgroundColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Gradient
|
||||
if ([self.delegate respondsToSelector:@selector(barGradientForBarChartView:)] && barView == nil)
|
||||
{
|
||||
CAGradientLayer *gradientLayer = [self.delegate barGradientForBarChartView:self];
|
||||
if (gradientLayer != nil)
|
||||
{
|
||||
barView = [[JBGradientBarView alloc] init];
|
||||
((JBGradientBarView *)barView).delegate = self;
|
||||
((JBGradientBarView *)barView).gradientLayer = gradientLayer;
|
||||
}
|
||||
}
|
||||
|
||||
// Default
|
||||
if (barView == nil)
|
||||
{
|
||||
barView = [[UIView alloc] init];
|
||||
barView.backgroundColor = kJBBarChartViewDefaultBarColor;
|
||||
}
|
||||
}
|
||||
|
||||
barView.tag = index;
|
||||
|
||||
return barView;
|
||||
}
|
||||
|
||||
- (void)insertBarView:(UIView *)barView
|
||||
{
|
||||
(self.footerView != nil) ? [self insertSubview:barView belowSubview:self.footerView] : [self addSubview:barView];
|
||||
[self bringSubviewToFront:self.verticalSelectionView];
|
||||
[self bringSubviewToFront:self.footerView];
|
||||
}
|
||||
|
||||
#pragma mark - Touch Helpers
|
||||
|
||||
- (NSInteger)barViewIndexForPoint:(CGPoint)point
|
||||
{
|
||||
NSUInteger index = 0;
|
||||
NSUInteger selectedIndex = kJBBarChartViewUndefinedBarIndex;
|
||||
|
||||
if (point.x < 0 || point.x > self.bounds.size.width)
|
||||
{
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
CGFloat padding = ceil(self.barPadding * 0.5);
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
CGFloat minX = CGRectGetMinX(barView.frame) - padding;
|
||||
CGFloat maxX = CGRectGetMaxX(barView.frame) + padding;
|
||||
if ((point.x >= minX) && (point.x <= maxX))
|
||||
{
|
||||
selectedIndex = index;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
- (UIView *)barViewForForPoint:(CGPoint)point
|
||||
{
|
||||
UIView *barView = nil;
|
||||
NSInteger selectedIndex = [self barViewIndexForPoint:point];
|
||||
if (selectedIndex >= 0)
|
||||
{
|
||||
return [self.barViews objectAtIndex:[self barViewIndexForPoint:point]];
|
||||
}
|
||||
return barView;
|
||||
}
|
||||
|
||||
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches
|
||||
{
|
||||
if (self.state == JBChartViewStateCollapsed || [self.chartData count] <= 0 || self.reloading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UITouch *touch = [touches anyObject];
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
UIView *barView = [self barViewForForPoint:touchPoint];
|
||||
if (barView == nil)
|
||||
{
|
||||
[self setVerticalSelectionViewVisible:NO animated:YES];
|
||||
return;
|
||||
}
|
||||
CGRect barViewFrame = barView.frame;
|
||||
CGRect selectionViewFrame = self.verticalSelectionView.frame;
|
||||
selectionViewFrame.origin.x = barViewFrame.origin.x;
|
||||
selectionViewFrame.size.width = barViewFrame.size.width;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoHeaderPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoHeaderPaddingForChartView:self])
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height + self.headerPadding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height + self.headerPadding;
|
||||
}
|
||||
|
||||
self.verticalSelectionView.frame = selectionViewFrame;
|
||||
[self setVerticalSelectionViewVisible:YES animated:YES];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:touchPoint:)])
|
||||
{
|
||||
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint] touchPoint:touchPoint];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:)])
|
||||
{
|
||||
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches
|
||||
{
|
||||
if (self.state == JBChartViewStateCollapsed || [self.chartData count] <= 0 || self.reloading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self setVerticalSelectionViewVisible:NO animated:YES];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(didDeselectBarChartView:)])
|
||||
{
|
||||
[self.delegate didDeselectBarChartView:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Touches
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self setVerticalSelectionViewVisible:NO animated:NO];
|
||||
[self touchesBeganOrMovedWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesBeganOrMovedWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesEndedOrCancelledWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesEndedOrCancelledWithTouches:touches];
|
||||
}
|
||||
|
||||
#pragma mark - JBGradientBarViewDataSource
|
||||
|
||||
- (CGRect)chartViewBoundsForGradientBarView:(JBGradientBarView *)gradientBarView
|
||||
{
|
||||
return self.bounds;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// JBGradientBarView.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class JBGradientBarView;
|
||||
|
||||
@protocol JBGradientBarViewDataSource;
|
||||
|
||||
@protocol JBGradientBarViewDataSource <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (CGRect)chartViewBoundsForGradientBarView:(JBGradientBarView *)gradientBarView;
|
||||
|
||||
@end
|
||||
|
||||
@interface JBGradientBarView: UIView
|
||||
|
||||
@property (nonatomic, weak) id<JBGradientBarViewDataSource> delegate;
|
||||
@property (nonatomic, strong) CAGradientLayer *gradientLayer;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// JBGradientBarView.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBGradientBarView.h"
|
||||
|
||||
@interface JBGradientBarView ()
|
||||
|
||||
- (void)construct;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBGradientBarView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setGradientLayer:(CAGradientLayer *)gradientLayer
|
||||
{
|
||||
if (_gradientLayer != nil)
|
||||
{
|
||||
[_gradientLayer removeFromSuperlayer];
|
||||
_gradientLayer = nil;
|
||||
}
|
||||
|
||||
_gradientLayer = gradientLayer;
|
||||
_gradientLayer.masksToBounds = YES;
|
||||
[self.layer insertSublayer:_gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
#pragma mark - Construction
|
||||
|
||||
- (void)construct
|
||||
{
|
||||
self.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(chartViewBoundsForGradientBarView:)])
|
||||
{
|
||||
_gradientLayer.frame = [self.delegate chartViewBoundsForGradientBarView:self]; // gradient is as large as the chart
|
||||
_gradientLayer.frame = CGRectOffset(_gradientLayer.frame, -CGRectGetMinX(frame), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gradientLayer.frame = self.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,14 +17,14 @@ extern CGFloat const kJBChartViewDefaultAnimationDuration;
|
||||
* At a minimum, a chart can support two states, along with animations to-and-from.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, JBChartViewState){
|
||||
/**
|
||||
* Expanded state: chart supports touches, interaction, etc.
|
||||
*/
|
||||
JBChartViewStateExpanded,
|
||||
/**
|
||||
* Collapse state: chart is more-or-less disabled at this point.
|
||||
*/
|
||||
JBChartViewStateCollapsed
|
||||
/**
|
||||
* Expanded state: chart supports touches, interaction, etc.
|
||||
*/
|
||||
JBChartViewStateExpanded,
|
||||
/**
|
||||
* Collapse state: chart is more-or-less disabled at this point.
|
||||
*/
|
||||
JBChartViewStateCollapsed
|
||||
};
|
||||
|
||||
@protocol JBChartViewDataSource <NSObject>
|
||||
@@ -64,7 +64,7 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
|
||||
@interface JBChartView : UIView
|
||||
|
||||
/*
|
||||
* Base dataSource and delegate protocols.
|
||||
* Base dataSource and delegate protocols.
|
||||
*/
|
||||
@property (nonatomic, weak) id<JBChartViewDataSource> dataSource;
|
||||
@property (nonatomic, weak) id<JBChartViewDelegate> delegate;
|
||||
@@ -72,7 +72,7 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
|
||||
/**
|
||||
* Header and footer views are shown above and below the chart respectively.
|
||||
* Each view will be stretched horizontally to fill width of chart.
|
||||
* Each view's bounds are clipped to support chart state animations.
|
||||
* Each view's bounds are clipped to support chart state animations.
|
||||
*/
|
||||
@property (nonatomic, strong) UIView *footerView;
|
||||
@property (nonatomic, strong) UIView *headerView;
|
||||
@@ -92,13 +92,13 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
|
||||
@property (nonatomic, assign) CGFloat footerPadding;
|
||||
|
||||
/**
|
||||
* The minimum and maxmimum values of the chart.
|
||||
* The minimum and maxmimum values of the chart.
|
||||
* If no value(s) are supplied:
|
||||
*
|
||||
* minimumValue = chart's data source min value.
|
||||
*
|
||||
* minimumValue = chart's data source min value.
|
||||
* maxmimumValue = chart's data source max value.
|
||||
*
|
||||
* If value(s) are supplied, they must be >= 0, otherwise an assertion will be thrown.
|
||||
* If value(s) are supplied, they must be >= 0, otherwise an assertion will be thrown.
|
||||
* The min/max values are clamped to the ceiling and floor of the actual min/max values of the chart's data source;
|
||||
* for example, if a maximumValue of 20 is supplied & the chart's actual max is 100, then 100 will be used.
|
||||
*
|
||||
@@ -112,7 +112,7 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
|
||||
- (void)resetMaximumValue;
|
||||
|
||||
/**
|
||||
* Charts can either be expanded or contracted.
|
||||
* Charts can either be expanded or contracted.
|
||||
* By default, a chart should be expanded on initialization.
|
||||
*/
|
||||
@property (nonatomic, assign) JBChartViewState state;
|
||||
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// JBChartView.m
|
||||
// Nudge
|
||||
//
|
||||
// Created by Terry Worona on 9/4/13.
|
||||
// Copyright (c) 2013 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBChartView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBChartViewDefaultAnimationDuration = 0.25f;
|
||||
|
||||
// Color (JBChartSelectionView)
|
||||
static UIColor *kJBChartVerticalSelectionViewDefaultBgColor = nil;
|
||||
|
||||
@interface JBChartView ()
|
||||
|
||||
@property (nonatomic, assign) BOOL hasMaximumValue;
|
||||
@property (nonatomic, assign) BOOL hasMinimumValue;
|
||||
|
||||
// Construction
|
||||
- (void)constructChartView;
|
||||
|
||||
// Validation
|
||||
- (void)validateHeaderAndFooterHeights;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBChartView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self constructChartView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self constructChartView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
#pragma mark - Construction
|
||||
|
||||
- (void)constructChartView
|
||||
{
|
||||
self.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
// Override
|
||||
}
|
||||
|
||||
#pragma mark - Validation
|
||||
|
||||
- (void)validateHeaderAndFooterHeights
|
||||
{
|
||||
NSAssert((self.headerView.bounds.size.height + self.footerView.bounds.size.height) <= self.bounds.size.height, @"JBChartView // the combined height of the footer and header can not be greater than the total height of the chart.");
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setHeaderView:(UIView *)headerView
|
||||
{
|
||||
if (_headerView)
|
||||
{
|
||||
[_headerView removeFromSuperview];
|
||||
_headerView = nil;
|
||||
}
|
||||
_headerView = headerView;
|
||||
_headerView.clipsToBounds = YES;
|
||||
|
||||
[self validateHeaderAndFooterHeights];
|
||||
|
||||
[self addSubview:_headerView];
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)setFooterView:(UIView *)footerView
|
||||
{
|
||||
if (_footerView)
|
||||
{
|
||||
[_footerView removeFromSuperview];
|
||||
_footerView = nil;
|
||||
}
|
||||
_footerView = footerView;
|
||||
_footerView.clipsToBounds = YES;
|
||||
|
||||
[self validateHeaderAndFooterHeights];
|
||||
|
||||
[self addSubview:_footerView];
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated force:(BOOL)force callback:(void (^)())callback
|
||||
{
|
||||
if ((_state == state) && !force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_state = state;
|
||||
|
||||
// Override
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
[self setState:state animated:animated force:NO callback:callback];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated
|
||||
{
|
||||
[self setState:state animated:animated callback:nil];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state
|
||||
{
|
||||
[self setState:state animated:NO];
|
||||
}
|
||||
|
||||
- (void)setMinimumValue:(CGFloat)minimumValue
|
||||
{
|
||||
NSAssert(minimumValue >= 0, @"JBChartView // the minimumValue must be >= 0.");
|
||||
_minimumValue = minimumValue;
|
||||
_hasMinimumValue = YES;
|
||||
}
|
||||
|
||||
- (void)setMaximumValue:(CGFloat)maximumValue
|
||||
{
|
||||
NSAssert(maximumValue >= 0, @"JBChartView // the maximumValue must be >= 0.");
|
||||
_maximumValue = maximumValue;
|
||||
_hasMaximumValue = YES;
|
||||
}
|
||||
|
||||
- (void)resetMinimumValue
|
||||
{
|
||||
_hasMinimumValue = NO; // clears min
|
||||
}
|
||||
|
||||
- (void)resetMaximumValue
|
||||
{
|
||||
_hasMaximumValue = NO; // clears max
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBChartVerticalSelectionView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [JBChartVerticalSelectionView class])
|
||||
{
|
||||
kJBChartVerticalSelectionViewDefaultBgColor = [UIColor whiteColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[[UIColor clearColor] set];
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGFloat locations[] = { 0.0, 1.0 };
|
||||
|
||||
NSArray *colors = nil;
|
||||
if (self.bgColor != nil)
|
||||
{
|
||||
colors = @[(__bridge id)self.bgColor.CGColor, (__bridge id)[self.bgColor colorWithAlphaComponent:0.0].CGColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
colors = @[(__bridge id)kJBChartVerticalSelectionViewDefaultBgColor.CGColor, (__bridge id)[kJBChartVerticalSelectionViewDefaultBgColor colorWithAlphaComponent:0.0].CGColor];
|
||||
}
|
||||
|
||||
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
|
||||
|
||||
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
|
||||
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
||||
|
||||
CGContextSaveGState(context);
|
||||
{
|
||||
CGContextAddRect(context, rect);
|
||||
CGContextClip(context);
|
||||
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
||||
}
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
CGGradientRelease(gradient);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setBgColor:(UIColor *)bgColor
|
||||
{
|
||||
_bgColor = bgColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,645 +0,0 @@
|
||||
//
|
||||
// JBBarChartView.m
|
||||
// Nudge
|
||||
//
|
||||
// Created by Terry Worona on 9/3/13.
|
||||
// Copyright (c) 2013 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBBarChartView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat static const kJBBarChartViewBarBasePaddingMutliplier = 50.0f;
|
||||
CGFloat static const kJBBarChartViewUndefinedCachedHeight = -1.0f;
|
||||
CGFloat static const kJBBarChartViewStateAnimationDuration = 0.05f;
|
||||
CGFloat static const kJBBarChartViewStatePopOffset = 10.0f;
|
||||
NSInteger static const kJBBarChartViewUndefinedBarIndex = -1;
|
||||
|
||||
// Colors (JBChartView)
|
||||
static UIColor *kJBBarChartViewDefaultBarColor = nil;
|
||||
|
||||
@interface JBChartView (Private)
|
||||
|
||||
- (BOOL)hasMaximumValue;
|
||||
- (BOOL)hasMinimumValue;
|
||||
|
||||
@end
|
||||
|
||||
@interface JBBarChartView ()
|
||||
|
||||
@property (nonatomic, strong) NSDictionary *chartDataDictionary; // key = column, value = height
|
||||
@property (nonatomic, strong) NSArray *barViews;
|
||||
@property (nonatomic, strong) NSArray *cachedBarViewHeights;
|
||||
@property (nonatomic, assign) CGFloat barPadding;
|
||||
@property (nonatomic, assign) CGFloat cachedMaxHeight;
|
||||
@property (nonatomic, assign) CGFloat cachedMinHeight;
|
||||
@property (nonatomic, strong) JBChartVerticalSelectionView *verticalSelectionView;
|
||||
@property (nonatomic, assign) BOOL verticalSelectionViewVisible;
|
||||
|
||||
// Initialization
|
||||
- (void)construct;
|
||||
|
||||
// View quick accessors
|
||||
- (CGFloat)availableHeight;
|
||||
- (CGFloat)normalizedHeightForRawHeight:(NSNumber*)rawHeight;
|
||||
- (CGFloat)barWidth;
|
||||
|
||||
// Touch helpers
|
||||
- (NSInteger)barViewIndexForPoint:(CGPoint)point;
|
||||
- (UIView *)barViewForForPoint:(CGPoint)point;
|
||||
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches;
|
||||
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches;
|
||||
|
||||
// Setters
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBBarChartView
|
||||
|
||||
@dynamic dataSource;
|
||||
@dynamic delegate;
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [JBBarChartView class])
|
||||
{
|
||||
kJBBarChartViewDefaultBarColor = [UIColor blackColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self construct];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)construct
|
||||
{
|
||||
_showsVerticalSelection = YES;
|
||||
_cachedMinHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
_cachedMaxHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
}
|
||||
|
||||
#pragma mark - Memory Management
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
// reset cached max height
|
||||
self.cachedMinHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
self.cachedMaxHeight = kJBBarChartViewUndefinedCachedHeight;
|
||||
|
||||
/*
|
||||
* The data collection holds all position information:
|
||||
* constructed via datasource and delegate functions
|
||||
*/
|
||||
dispatch_block_t createDataDictionaries = ^{
|
||||
|
||||
// Grab the count
|
||||
NSAssert([self.dataSource respondsToSelector:@selector(numberOfBarsInBarChartView:)], @"JBBarChartView // datasource must implement - (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView");
|
||||
NSUInteger dataCount = [self.dataSource numberOfBarsInBarChartView:self];
|
||||
|
||||
// Build up the data collection
|
||||
NSAssert([self.delegate respondsToSelector:@selector(barChartView:heightForBarViewAtIndex:)], @"JBBarChartView // delegate must implement - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index");
|
||||
NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
|
||||
for (NSUInteger index=0; index<dataCount; index++)
|
||||
{
|
||||
CGFloat height = [self.delegate barChartView:self heightForBarViewAtIndex:index];
|
||||
NSAssert(height >= 0, @"JBBarChartView // datasource function - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index must return a CGFloat >= 0");
|
||||
[dataDictionary setObject:[NSNumber numberWithFloat:height] forKey:[NSNumber numberWithInt:(int)index]];
|
||||
}
|
||||
self.chartDataDictionary = [NSDictionary dictionaryWithDictionary:dataDictionary];
|
||||
};
|
||||
|
||||
/*
|
||||
* Determines the padding between bars as a function of # of bars
|
||||
*/
|
||||
dispatch_block_t createBarPadding = ^{
|
||||
if ([self.delegate respondsToSelector:@selector(barPaddingForBarChartView:)])
|
||||
{
|
||||
self.barPadding = [self.delegate barPaddingForBarChartView:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger totalBars = [[self.chartDataDictionary allKeys] count];
|
||||
self.barPadding = (1/(float)totalBars) * kJBBarChartViewBarBasePaddingMutliplier;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a new bar graph view using the previously calculated data model
|
||||
*/
|
||||
dispatch_block_t createBars = ^{
|
||||
|
||||
// Remove old bars
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
[barView removeFromSuperview];
|
||||
}
|
||||
|
||||
self.cachedBarViewHeights = nil;
|
||||
|
||||
CGFloat xOffset = 0;
|
||||
NSUInteger index = 0;
|
||||
NSMutableArray *mutableBarViews = [NSMutableArray array];
|
||||
NSMutableArray *mutableCachedBarViewHeights = [NSMutableArray array];
|
||||
for (NSNumber *key in [[self.chartDataDictionary allKeys] sortedArrayUsingSelector:@selector(compare:)])
|
||||
{
|
||||
UIView *barView = nil; // since all bars are visible at once, no need to cache this view
|
||||
if ([self.dataSource respondsToSelector:@selector(barChartView:barViewAtIndex:)])
|
||||
{
|
||||
barView = [self.dataSource barChartView:self barViewAtIndex:index];
|
||||
NSAssert(barView != nil, @"JBBarChartView // datasource function - (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index must return a non-nil UIView subclass");
|
||||
}
|
||||
else
|
||||
{
|
||||
barView = [[UIView alloc] init];
|
||||
UIColor *backgroundColor = nil;
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:colorForBarViewAtIndex:)])
|
||||
{
|
||||
backgroundColor = [self.delegate barChartView:self colorForBarViewAtIndex:index];
|
||||
NSAssert(backgroundColor != nil, @"JBBarChartView // delegate function - (UIColor *)barChartView:(JBBarChartView *)barChartView colorForBarViewAtIndex:(NSUInteger)index must return a non-nil UIColor");
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundColor = kJBBarChartViewDefaultBarColor;
|
||||
}
|
||||
|
||||
barView.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
barView.tag = index;
|
||||
|
||||
CGFloat height = [self normalizedHeightForRawHeight:[self.chartDataDictionary objectForKey:key]];
|
||||
barView.frame = CGRectMake(xOffset, self.bounds.size.height - height - self.footerView.frame.size.height, [self barWidth], height);
|
||||
[mutableBarViews addObject:barView];
|
||||
[mutableCachedBarViewHeights addObject:[NSNumber numberWithFloat:height]];
|
||||
|
||||
// Add new bar
|
||||
if (self.footerView)
|
||||
{
|
||||
[self insertSubview:barView belowSubview:self.footerView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self addSubview:barView];
|
||||
}
|
||||
|
||||
xOffset += ([self barWidth] + self.barPadding);
|
||||
index++;
|
||||
}
|
||||
self.barViews = [NSArray arrayWithArray:mutableBarViews];
|
||||
self.cachedBarViewHeights = [NSArray arrayWithArray:mutableCachedBarViewHeights];
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a vertical selection view for touch events
|
||||
*/
|
||||
dispatch_block_t createSelectionView = ^{
|
||||
|
||||
// Remove old selection bar
|
||||
if (self.verticalSelectionView)
|
||||
{
|
||||
[self.verticalSelectionView removeFromSuperview];
|
||||
self.verticalSelectionView = nil;
|
||||
}
|
||||
|
||||
CGFloat verticalSelectionViewHeight = self.bounds.size.height - self.headerView.frame.size.height - self.footerView.frame.size.height - self.headerPadding - self.footerPadding;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoHeaderPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoHeaderPaddingForChartView:self])
|
||||
{
|
||||
verticalSelectionViewHeight += self.headerPadding;
|
||||
}
|
||||
}
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoFooterPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoFooterPaddingForChartView:self])
|
||||
{
|
||||
verticalSelectionViewHeight += self.footerPadding;
|
||||
}
|
||||
}
|
||||
|
||||
self.verticalSelectionView = [[JBChartVerticalSelectionView alloc] initWithFrame:CGRectMake(0, 0, [self barWidth], verticalSelectionViewHeight)];
|
||||
self.verticalSelectionView.alpha = 0.0;
|
||||
self.verticalSelectionView.hidden = !self.showsVerticalSelection;
|
||||
if ([self.delegate respondsToSelector:@selector(barSelectionColorForBarChartView:)])
|
||||
{
|
||||
UIColor *selectionViewBackgroundColor = [self.delegate barSelectionColorForBarChartView:self];
|
||||
NSAssert(selectionViewBackgroundColor != nil, @"JBBarChartView // delegate function - (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView must return a non-nil UIColor");
|
||||
self.verticalSelectionView.bgColor = selectionViewBackgroundColor;
|
||||
}
|
||||
|
||||
// Add new selection bar
|
||||
if (self.footerView)
|
||||
{
|
||||
[self insertSubview:self.verticalSelectionView belowSubview:self.footerView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self addSubview:self.verticalSelectionView];
|
||||
}
|
||||
|
||||
self.verticalSelectionView.transform = self.inverted ? CGAffineTransformMakeScale(1.0, -1.0) : CGAffineTransformIdentity;
|
||||
};
|
||||
|
||||
createDataDictionaries();
|
||||
createBarPadding();
|
||||
createBars();
|
||||
createSelectionView();
|
||||
|
||||
// Position header and footer
|
||||
self.headerView.frame = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.headerView.frame.size.height);
|
||||
self.footerView.frame = CGRectMake(self.bounds.origin.x, self.bounds.size.height - self.footerView.frame.size.height, self.bounds.size.width, self.footerView.frame.size.height);
|
||||
|
||||
// Refresh state
|
||||
[self setState:self.state animated:NO force:YES callback:nil];
|
||||
}
|
||||
|
||||
#pragma mark - View Quick Accessors
|
||||
|
||||
- (CGFloat)availableHeight
|
||||
{
|
||||
return self.bounds.size.height - self.headerView.frame.size.height - self.footerView.frame.size.height - self.headerPadding - self.footerPadding;
|
||||
}
|
||||
|
||||
- (CGFloat)normalizedHeightForRawHeight:(NSNumber*)rawHeight
|
||||
{
|
||||
CGFloat minHeight = [self minimumValue];
|
||||
CGFloat maxHeight = [self maximumValue];
|
||||
CGFloat value = [rawHeight floatValue];
|
||||
|
||||
if ((maxHeight - minHeight) <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((value - minHeight) / (maxHeight - minHeight)) * [self availableHeight];
|
||||
}
|
||||
|
||||
- (CGFloat)barWidth
|
||||
{
|
||||
NSUInteger barCount = [[self.chartDataDictionary allKeys] count];
|
||||
if (barCount > 0)
|
||||
{
|
||||
CGFloat totalPadding = (barCount - 1) * self.barPadding;
|
||||
CGFloat availableWidth = self.bounds.size.width - totalPadding;
|
||||
return availableWidth / barCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated force:(BOOL)force callback:(void (^)())callback
|
||||
{
|
||||
[super setState:state animated:animated force:force callback:callback];
|
||||
|
||||
__weak JBBarChartView* weakSelf = self;
|
||||
|
||||
void (^updateBarView)(UIView *barView, BOOL popBar);
|
||||
|
||||
updateBarView = ^(UIView *barView, BOOL popBar) {
|
||||
if (weakSelf.inverted)
|
||||
{
|
||||
if (weakSelf.state == JBChartViewStateExpanded)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue]);
|
||||
}
|
||||
}
|
||||
else if (weakSelf.state == JBChartViewStateCollapsed)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.headerView.frame.size.height + weakSelf.headerPadding, barView.frame.size.width, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weakSelf.state == JBChartViewStateExpanded)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] - kJBBarChartViewStatePopOffset, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue], barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue]);
|
||||
}
|
||||
}
|
||||
else if (weakSelf.state == JBChartViewStateCollapsed)
|
||||
{
|
||||
if (popBar)
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height - weakSelf.footerView.frame.size.height - weakSelf.footerPadding - [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] - kJBBarChartViewStatePopOffset, barView.frame.size.width, [[weakSelf.cachedBarViewHeights objectAtIndex:barView.tag] floatValue] + kJBBarChartViewStatePopOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
barView.frame = CGRectMake(barView.frame.origin.x, weakSelf.bounds.size.height, barView.frame.size.width, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_block_t callbackCopy = [callback copy];
|
||||
|
||||
if ([self.barViews count] > 0)
|
||||
{
|
||||
if (animated)
|
||||
{
|
||||
NSUInteger index = 0;
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:(kJBBarChartViewStateAnimationDuration * 0.5) * index options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
updateBarView(barView, YES);
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
updateBarView(barView, NO);
|
||||
} completion:^(BOOL lastBarFinished) {
|
||||
if ((NSUInteger)barView.tag == [self.barViews count] - 1)
|
||||
{
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
updateBarView(barView, NO);
|
||||
}
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callbackCopy)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
[self setState:state animated:animated force:NO callback:callback];
|
||||
}
|
||||
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated
|
||||
{
|
||||
_verticalSelectionViewVisible = verticalSelectionViewVisible;
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
self.verticalSelectionView.alpha = self.verticalSelectionViewVisible ? 1.0 : 0.0;
|
||||
} completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.verticalSelectionView.alpha = _verticalSelectionViewVisible ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible
|
||||
{
|
||||
[self setVerticalSelectionViewVisible:verticalSelectionViewVisible animated:NO];
|
||||
}
|
||||
|
||||
- (void)setShowsVerticalSelection:(BOOL)showsVerticalSelection
|
||||
{
|
||||
_showsVerticalSelection = showsVerticalSelection;
|
||||
self.verticalSelectionView.hidden = _showsVerticalSelection ? NO : YES;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (CGFloat)cachedMinHeight
|
||||
{
|
||||
if(_cachedMinHeight == kJBBarChartViewUndefinedCachedHeight)
|
||||
{
|
||||
NSArray *chartValues = [[NSMutableArray arrayWithArray:[self.chartDataDictionary allValues]] sortedArrayUsingSelector:@selector(compare:)];
|
||||
_cachedMinHeight = [[chartValues firstObject] floatValue];
|
||||
}
|
||||
return _cachedMinHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)cachedMaxHeight
|
||||
{
|
||||
if (_cachedMaxHeight == kJBBarChartViewUndefinedCachedHeight)
|
||||
{
|
||||
NSArray *chartValues = [[NSMutableArray arrayWithArray:[self.chartDataDictionary allValues]] sortedArrayUsingSelector:@selector(compare:)];
|
||||
_cachedMaxHeight = [[chartValues lastObject] floatValue];
|
||||
}
|
||||
return _cachedMaxHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)minimumValue
|
||||
{
|
||||
if ([self hasMinimumValue])
|
||||
{
|
||||
return fminf(self.cachedMinHeight, [super minimumValue]);
|
||||
}
|
||||
return self.cachedMinHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)maximumValue
|
||||
{
|
||||
if ([self hasMaximumValue])
|
||||
{
|
||||
return fmaxf(self.cachedMaxHeight, [super maximumValue]);
|
||||
}
|
||||
return self.cachedMaxHeight;
|
||||
}
|
||||
|
||||
- (UIView *)barViewAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index < [self.barViews count])
|
||||
{
|
||||
return [self.barViews objectAtIndex:index];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Touch Helpers
|
||||
|
||||
- (NSInteger)barViewIndexForPoint:(CGPoint)point
|
||||
{
|
||||
NSUInteger index = 0;
|
||||
NSUInteger selectedIndex = kJBBarChartViewUndefinedBarIndex;
|
||||
|
||||
if (point.x < 0 || point.x > self.bounds.size.width)
|
||||
{
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
CGFloat padding = ceil(self.barPadding * 0.5);
|
||||
for (UIView *barView in self.barViews)
|
||||
{
|
||||
CGFloat minX = CGRectGetMinX(barView.frame) - padding;
|
||||
CGFloat maxX = CGRectGetMaxX(barView.frame) + padding;
|
||||
if ((point.x >= minX) && (point.x <= maxX))
|
||||
{
|
||||
selectedIndex = index;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
- (UIView *)barViewForForPoint:(CGPoint)point
|
||||
{
|
||||
UIView *barView = nil;
|
||||
NSInteger selectedIndex = [self barViewIndexForPoint:point];
|
||||
if (selectedIndex >= 0)
|
||||
{
|
||||
return [self.barViews objectAtIndex:[self barViewIndexForPoint:point]];
|
||||
}
|
||||
return barView;
|
||||
}
|
||||
|
||||
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches
|
||||
{
|
||||
if (self.state == JBChartViewStateCollapsed || [[self.chartDataDictionary allKeys] count] <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UITouch *touch = [touches anyObject];
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
UIView *barView = [self barViewForForPoint:touchPoint];
|
||||
if (barView == nil)
|
||||
{
|
||||
[self setVerticalSelectionViewVisible:NO animated:YES];
|
||||
return;
|
||||
}
|
||||
CGRect barViewFrame = barView.frame;
|
||||
CGRect selectionViewFrame = self.verticalSelectionView.frame;
|
||||
selectionViewFrame.origin.x = barViewFrame.origin.x;
|
||||
selectionViewFrame.size.width = barViewFrame.size.width;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(shouldExtendSelectionViewIntoHeaderPaddingForChartView:)])
|
||||
{
|
||||
if ([self.dataSource shouldExtendSelectionViewIntoHeaderPaddingForChartView:self])
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height + self.headerPadding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionViewFrame.origin.y = self.headerView.frame.size.height + self.headerPadding;
|
||||
}
|
||||
|
||||
self.verticalSelectionView.frame = selectionViewFrame;
|
||||
[self setVerticalSelectionViewVisible:YES animated:YES];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:touchPoint:)])
|
||||
{
|
||||
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint] touchPoint:touchPoint];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:)])
|
||||
{
|
||||
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches
|
||||
{
|
||||
if (self.state == JBChartViewStateCollapsed || [[self.chartDataDictionary allKeys] count] <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self setVerticalSelectionViewVisible:NO animated:YES];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(didDeselectBarChartView:)])
|
||||
{
|
||||
[self.delegate didDeselectBarChartView:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Touches
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesBeganOrMovedWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesBeganOrMovedWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesEndedOrCancelledWithTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesEndedOrCancelledWithTouches:touches];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,235 +0,0 @@
|
||||
//
|
||||
// JBChartView.m
|
||||
// Nudge
|
||||
//
|
||||
// Created by Terry Worona on 9/4/13.
|
||||
// Copyright (c) 2013 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBChartView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBChartViewDefaultAnimationDuration = 0.25f;
|
||||
|
||||
// Color (JBChartSelectionView)
|
||||
static UIColor *kJBChartVerticalSelectionViewDefaultBgColor = nil;
|
||||
|
||||
@interface JBChartView ()
|
||||
|
||||
@property (nonatomic, assign) BOOL hasMaximumValue;
|
||||
@property (nonatomic, assign) BOOL hasMinimumValue;
|
||||
|
||||
// Construction
|
||||
- (void)constructChartView;
|
||||
|
||||
// Validation
|
||||
- (void)validateHeaderAndFooterHeights;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBChartView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self constructChartView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self constructChartView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
#pragma mark - Construction
|
||||
|
||||
- (void)constructChartView
|
||||
{
|
||||
self.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
// Override
|
||||
}
|
||||
|
||||
#pragma mark - Validation
|
||||
|
||||
- (void)validateHeaderAndFooterHeights
|
||||
{
|
||||
NSAssert((self.headerView.bounds.size.height + self.footerView.bounds.size.height) <= self.bounds.size.height, @"JBChartView // the combined height of the footer and header can not be greater than the total height of the chart.");
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setHeaderView:(UIView *)headerView
|
||||
{
|
||||
if (_headerView)
|
||||
{
|
||||
[_headerView removeFromSuperview];
|
||||
_headerView = nil;
|
||||
}
|
||||
_headerView = headerView;
|
||||
_headerView.clipsToBounds = YES;
|
||||
|
||||
[self validateHeaderAndFooterHeights];
|
||||
|
||||
[self addSubview:_headerView];
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)setFooterView:(UIView *)footerView
|
||||
{
|
||||
if (_footerView)
|
||||
{
|
||||
[_footerView removeFromSuperview];
|
||||
_footerView = nil;
|
||||
}
|
||||
_footerView = footerView;
|
||||
_footerView.clipsToBounds = YES;
|
||||
|
||||
[self validateHeaderAndFooterHeights];
|
||||
|
||||
[self addSubview:_footerView];
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated force:(BOOL)force callback:(void (^)())callback
|
||||
{
|
||||
if ((_state == state) && !force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_state = state;
|
||||
|
||||
// Override
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
[self setState:state animated:animated force:NO callback:callback];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated
|
||||
{
|
||||
[self setState:state animated:animated callback:nil];
|
||||
}
|
||||
|
||||
- (void)setState:(JBChartViewState)state
|
||||
{
|
||||
[self setState:state animated:NO];
|
||||
}
|
||||
|
||||
- (void)setMinimumValue:(CGFloat)minimumValue
|
||||
{
|
||||
NSAssert(minimumValue >= 0, @"JBChartView // the minimumValue must be >= 0.");
|
||||
_minimumValue = minimumValue;
|
||||
_hasMinimumValue = YES;
|
||||
}
|
||||
|
||||
- (void)setMaximumValue:(CGFloat)maximumValue
|
||||
{
|
||||
NSAssert(maximumValue >= 0, @"JBChartView // the maximumValue must be >= 0.");
|
||||
_maximumValue = maximumValue;
|
||||
_hasMaximumValue = YES;
|
||||
}
|
||||
|
||||
- (void)resetMinimumValue
|
||||
{
|
||||
_hasMinimumValue = NO; // clears min
|
||||
}
|
||||
|
||||
- (void)resetMaximumValue
|
||||
{
|
||||
_hasMaximumValue = NO; // clears max
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBChartVerticalSelectionView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [JBChartVerticalSelectionView class])
|
||||
{
|
||||
kJBChartVerticalSelectionViewDefaultBgColor = [UIColor whiteColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[[UIColor clearColor] set];
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGFloat locations[] = { 0.0, 1.0 };
|
||||
|
||||
NSArray *colors = nil;
|
||||
if (self.bgColor != nil)
|
||||
{
|
||||
colors = @[(__bridge id)self.bgColor.CGColor, (__bridge id)[self.bgColor colorWithAlphaComponent:0.0].CGColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
colors = @[(__bridge id)kJBChartVerticalSelectionViewDefaultBgColor.CGColor, (__bridge id)[kJBChartVerticalSelectionViewDefaultBgColor colorWithAlphaComponent:0.0].CGColor];
|
||||
}
|
||||
|
||||
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
|
||||
|
||||
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
|
||||
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
||||
|
||||
CGContextSaveGState(context);
|
||||
{
|
||||
CGContextAddRect(context, rect);
|
||||
CGContextClip(context);
|
||||
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
||||
}
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
CGGradientRelease(gradient);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setBgColor:(UIColor *)bgColor
|
||||
{
|
||||
_bgColor = bgColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,19 +11,34 @@
|
||||
@class JBLineChartView;
|
||||
|
||||
/**
|
||||
* Current support for two line styles: solid (default) and dashed.
|
||||
* Indicates how a line's main path will be drawn.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
/**
|
||||
* Solid line.
|
||||
*/
|
||||
JBLineChartViewLineStyleSolid,
|
||||
/**
|
||||
* Dashed line with a phase of 3:2 (3 points dashed, 2 points spaced).
|
||||
*/
|
||||
/**
|
||||
* Solid line.
|
||||
*/
|
||||
JBLineChartViewLineStyleSolid,
|
||||
/**
|
||||
* Dashed with a 3:2 phase (3 points dashed, 2 points spaced).
|
||||
*/
|
||||
JBLineChartViewLineStyleDashed
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates how a line's main path or fill (including selections)
|
||||
* will be decorated (via color options).
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, JBLineChartViewColorStyle) {
|
||||
/**
|
||||
* A solid color (with alpha support via UIColor).
|
||||
*/
|
||||
JBLineChartViewColorStyleSolid,
|
||||
/**
|
||||
* a gradient (via CAGradientLayer).
|
||||
*/
|
||||
JBLineChartViewColorStyleGradient
|
||||
};
|
||||
|
||||
@protocol JBLineChartViewDataSource <JBChartViewDataSource>
|
||||
|
||||
@required
|
||||
@@ -75,12 +90,26 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
*/
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView smoothLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the opacity value to be used for dimming the line & fill during selection events.
|
||||
* This value is applied to the line or fill's opacity anytime it's not selected (but another line is).
|
||||
* This applies to both solid and gradient color styles.
|
||||
*
|
||||
* Default: 0.2.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return A value between 0.0 and 1.0 (will be clamped accordingly).
|
||||
*/
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns a (custom) UIView instance representing a dot (x,y point) within the chart.
|
||||
* For this value to apply, showsDotsForLineAtLineIndex: must return YES for the line at lineIndex.
|
||||
* This protocol supercedes colorForDotAtHorizontalIndex: and dotRadiusForDotAtHorizontalIndex:.
|
||||
* If nil is returned. the original dot protocols will take precedence. During selection events, a custom
|
||||
* dot view will not be hidden unless lineChartView:shouldHideDotViewOnSelectionAtHorizontalIndex:atLineIndex:
|
||||
* dot view will not be hidden unless lineChartView:shouldHideDotViewOnSelectionAtHorizontalIndex:atLineIndex:
|
||||
* is implemented.
|
||||
*
|
||||
* Default: nil.
|
||||
@@ -94,7 +123,7 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
- (UIView *)lineChartView:(JBLineChartView *)lineChartView dotViewAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns whether or not a (custom) dot view should be hidden on selection events.
|
||||
* Returns whether or not a (custom) dot view should be hidden on selection events.
|
||||
*
|
||||
* Default: NO.
|
||||
*
|
||||
@@ -102,7 +131,7 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
* @param horizontalIndex The 0-based horizontal index of a selection point (left to right, x-axis).
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return Whether or not a (custom) dot view should be hidden on selection events.
|
||||
* @return Whether or not a (custom) dot view should be hidden on selection events.
|
||||
*/
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView shouldHideDotViewOnSelectionAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
@@ -115,7 +144,7 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
/**
|
||||
* Vertical value for a line point at a given index (left to right). There is no ceiling on the the height;
|
||||
* the chart will automatically normalize all values between the overal min and max heights.
|
||||
* NAN may able be retuend to indicate missing values. The chart's line will begin at the first non-NAN value and end at the last non-NAN value.
|
||||
* NAN may able be retuend to indicate missing values. The chart's line will begin at the first non-NAN value and end at the last non-NAN value.
|
||||
* Furthermore, the line will interopolate any NAN values in between (ie. the line will not be interrupted).
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
@@ -132,8 +161,8 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
* Occurs whenever there is a touch gesture on the chart (chart must be expanded).
|
||||
* The horizontal index is the closest index to the touch point & is clamped to it's max/min value if it moves outside of the view's bounds.
|
||||
* The lineIndex remains constant until the line is deselected and will be highlighted using the (optional) selectionColorForLineAtLineIndex: protocol.
|
||||
* Futhermore, all other lines that aren't selected will be dimmed to 20%% opacity throughout the duration of the touch/move. Any dotted line that isn't the
|
||||
* primary selection will have it's dots dimmed to hidden (to avoid transparency issues).
|
||||
* Futhermore, all other lines that aren't selected will be dimmed to 20% opacity (default) throughout the duration of the touch/move.
|
||||
* Any dotted line that isn't the primary selection will have it's dots dimmed to hidden (to avoid transparency issues).
|
||||
*
|
||||
* @param lineChartView A line chart object informing the delegate about the new selection.
|
||||
* @param lineIndex An index number identifying the closest line in the chart to the current touch
|
||||
@@ -151,7 +180,18 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
- (void)didDeselectLineInLineChartView:(JBLineChartView *)lineChartView;
|
||||
|
||||
/**
|
||||
* Returns the color of particular line at lineIndex within the chart.
|
||||
* Returns whether or not a line at a particular index responds to selection events.
|
||||
*
|
||||
* Default: YES
|
||||
*
|
||||
* @param lineChartView A line chart object informing the delegate about the new selection.
|
||||
* @param lineIndex An index number identifying the closest line in the chart to the current touch
|
||||
*/
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView shouldIgnoreSelectionAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the color of particular line at lineIndex.
|
||||
* For this to apply, lineChartView:colorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleSolid (default).
|
||||
*
|
||||
* Default: black color.
|
||||
*
|
||||
@@ -163,9 +203,25 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the fill color of particular line at lineIndex within the chart.
|
||||
* Returns the gradient layer to be used for a particular line at lineIndex within the chart.
|
||||
* For this to apply, lineChartView:colorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleGradient.
|
||||
*
|
||||
* Default: clear color.
|
||||
* Note: gradients do not support multiple alphas. The alpha of gradient's first color be used throughout.
|
||||
*
|
||||
* Default: black to light gray.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The gradient layer to be used to shade a line in the chart.
|
||||
*/
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView gradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the fill color of particular line at lineIndex within the chart.
|
||||
* For this to apply, lineChartView:fillColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleSolid (default).
|
||||
*
|
||||
* Default: clear color (none).
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
@@ -174,6 +230,21 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
*/
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView fillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the gradient layer to be used for a fill of a particular line at lineIndex within the chart.
|
||||
* For this to apply, lineChartView:fillColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleGradient.
|
||||
*
|
||||
* Note: gradients do not support multiple alphas. The alpha of gradient's first color be used throughout.
|
||||
*
|
||||
* Default: clear gradient (none).
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The fill gradient to show under a line in the chart.
|
||||
*/
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView fillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the color of a particular dot in a line at lineIndex within the chart.
|
||||
* For this value to apply, showsDotsForLineAtLineIndex: must return YES for the line at lineIndex.
|
||||
@@ -234,6 +305,8 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
* The color is automically faded to transparent (vertically). The property showsVerticalSelection
|
||||
* must be YES for the color to apply.
|
||||
*
|
||||
* Default: white color.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
@@ -242,10 +315,11 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView verticalSelectionColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the selection color to be overlayed on a line within the chart during touch events.
|
||||
* Returns the selection color of a line within the chart during touch events.
|
||||
* The property showsLineSelection must be YES for the color to apply.
|
||||
* As well, lineChartView:selectionColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleSolid (default).
|
||||
*
|
||||
* Default: white color.
|
||||
* Default: matches lineChartView:colorForLineAtLineIndex:.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
@@ -255,10 +329,27 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the selection fill color to be overlayed under a line within the chart during touch events.
|
||||
* Returns the selection gradient layer of a line within the chart during touch events.
|
||||
* The property showsLineSelection must be YES for the color to apply.
|
||||
* As well, lineChartView:selectionColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleGradient.
|
||||
*
|
||||
* Default: clear color.
|
||||
* Note: gradients do not support multiple alphas. The alpha of gradient's first color be used throughout.
|
||||
*
|
||||
* Default: matches lineChartView:gradientForLineAtLineIndex:.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The gradient layer to be used to highlight a line during chart selections.
|
||||
*/
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the selection fill color under a line within the chart during touch events.
|
||||
* The property showsLineSelection must be YES for the color to apply.
|
||||
* As well, lineChartView:selectionFillColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleSolid (default).
|
||||
*
|
||||
* Default: matches lineChartView:fillColorForLineAtLineIndex:.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
@@ -267,11 +358,27 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
*/
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the selection fill gradient layer under a line within the chart during touch events.
|
||||
* The property showsLineSelection must be YES for the color to apply.
|
||||
* As well, lineChartView:selectionFillColorStyleForLineAtLineIndex: must return JBLineChartViewColorStyleGrdient.
|
||||
*
|
||||
* Note: gradients do not support multiple alphas. The alpha of gradient's first color be used throughout.
|
||||
*
|
||||
* Default: matches lineChartView:fillGradientForLineAtLineIndex.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The gradient layer to be used to highlight under a line during chart selections.
|
||||
*/
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionFillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the selection color to be overlayed on a line within the chart during touch events.
|
||||
* The property showsLineSelection must be YES for the color to apply.
|
||||
*
|
||||
* Default: white color.
|
||||
* Default: matches lineChartView:colorForDotAtHorizontalIndex:atLineIndex:(NSUInteger)lineIndex.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param horizontalIndex The 0-based horizontal index of a selection point (left to right, x-axis).
|
||||
@@ -294,6 +401,34 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
*/
|
||||
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the line color style of a particular line at lineIndex within the chart.
|
||||
* The line color style applies to both selected and non-selected scenarios.
|
||||
* See JBLineChartViewColorStyle for color style descriptions.
|
||||
*
|
||||
* Default: JBLineChartViewColorStyleSolid.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The color style to be used to shade a line in the chart.
|
||||
*/
|
||||
- (JBLineChartViewColorStyle)lineChartView:(JBLineChartView *)lineChartView colorStyleForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
/**
|
||||
* Returns the fill color style of a particular line at lineIndex within the chart.
|
||||
* The fill color style applies to both selected and non-selected scenarios.
|
||||
* See JBLineChartViewColorStyle for color style descriptions.
|
||||
*
|
||||
* Default: JBLineChartViewColorStyleSolid.
|
||||
*
|
||||
* @param lineChartView The line chart object requesting this information.
|
||||
* @param lineIndex An index number identifying a line in the chart.
|
||||
*
|
||||
* @return The fill color style to show under a line in the chart.
|
||||
*/
|
||||
- (JBLineChartViewColorStyle)lineChartView:(JBLineChartView *)lineChartView fillColorStyleForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
@end
|
||||
|
||||
@interface JBLineChartView : JBChartView
|
||||
@@ -301,6 +436,29 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
@property (nonatomic, weak) id<JBLineChartViewDataSource> dataSource;
|
||||
@property (nonatomic, weak) id<JBLineChartViewDelegate> delegate;
|
||||
|
||||
/*
|
||||
* Reloads the line chart with a custom animation.
|
||||
* Adding, removing or modifying existing lines or dot views (via growing/shrinking & fading) if animated = YES;
|
||||
* Reloading (animated) data is thread safe and can be executed any number of times in succession.
|
||||
*
|
||||
* Note: fills will not be animated (technical limitation of Apple's CG API).
|
||||
*
|
||||
* Default: a non-animated reload (via reloadData).
|
||||
*/
|
||||
- (void)reloadDataAnimated:(BOOL)animated;
|
||||
|
||||
/*
|
||||
* When reloadData or reloadDataAnimated: is called, the reloading bit is turned on.
|
||||
* State changes during a reload will be ignored. As well, subsequent calls to reloadData:
|
||||
* or reloadDataAnimated: before any previous reloads are complete, will also be ignored.
|
||||
* Lastly, all touch events will be ignored until a reload has compeleted.
|
||||
*
|
||||
* Note: the above restrictions apply only to animated reloads, as non-animated reloads are synchronous.
|
||||
*
|
||||
* Default: NO.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL reloading;
|
||||
|
||||
/**
|
||||
* Vertical highlight overlayed on a line graph during touch events.
|
||||
*
|
||||
@@ -310,7 +468,7 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
|
||||
/**
|
||||
* A highlight shown on a line within the graph during touch events. The highlighted line
|
||||
* is the closest line to the touch point and corresponds to the lineIndex delegatd back via
|
||||
* is the closest line to the touch point and corresponds to the lineIndex delegatd back via
|
||||
* didSelectChartAtHorizontalIndex:atLineIndex: and didUnSlectChartAtHorizontalIndex:atLineIndex:
|
||||
*
|
||||
* Default: YES.
|
||||
@@ -318,7 +476,7 @@ typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
|
||||
@property (nonatomic, assign) BOOL showsLineSelection;
|
||||
|
||||
/**
|
||||
* The dot view within a particular line at a horizontalIndex.
|
||||
* The dot view within a particular line at a horizontalIndex.
|
||||
*
|
||||
* Default: nil.
|
||||
*
|
||||
Executable
+1149
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// JBGradientLineLayer.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JBGradientLineLayer : CAGradientLayer
|
||||
|
||||
- (instancetype)initWithGradientLayer:(CAGradientLayer *)gradientLayer tag:(NSUInteger)tag filled:(BOOL)filled currentPath:(UIBezierPath *)currentPath;
|
||||
|
||||
@property (nonatomic, readonly) NSUInteger tag;
|
||||
@property (nonatomic, readonly) BOOL filled;
|
||||
@property (nonatomic, strong) UIBezierPath *currentPath;
|
||||
@property (nonatomic, readonly) CGFloat alpha; // alpha of gradient, based on first color
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// JBGradientLineLayer.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBGradientLineLayer.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBGradientLineLayerDefaultAlpha = 1.0f;
|
||||
|
||||
@implementation JBGradientLineLayer
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (instancetype)initWithGradientLayer:(CAGradientLayer *)gradientLayer tag:(NSUInteger)tag filled:(BOOL)filled currentPath:(UIBezierPath *)currentPath
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
self.colors = gradientLayer.colors;
|
||||
self.locations = gradientLayer.locations;
|
||||
self.startPoint = gradientLayer.startPoint;
|
||||
self.endPoint = gradientLayer.endPoint;
|
||||
self.type = gradientLayer.type;
|
||||
|
||||
_tag = tag;
|
||||
_filled = filled;
|
||||
_currentPath = [currentPath copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (CGFloat)alpha
|
||||
{
|
||||
if (self.colors.firstObject != nil)
|
||||
{
|
||||
return CGColorGetAlpha((CGColorRef)self.colors.firstObject);
|
||||
}
|
||||
return kJBGradientLineLayerDefaultAlpha;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// JBShapeLineLayer.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Views
|
||||
#import "JBLineChartView.h"
|
||||
|
||||
@interface JBShapeLineLayer : CAShapeLayer
|
||||
|
||||
- (instancetype)initWithTag:(NSUInteger)tag filled:(BOOL)filled smoothedLine:(BOOL)smoothedLine lineStyle:(JBLineChartViewLineStyle)lineStyle currentPath:(UIBezierPath *)currentPath;
|
||||
|
||||
@property (nonatomic, readonly) NSUInteger tag;
|
||||
@property (nonatomic, readonly) BOOL filled;
|
||||
@property (nonatomic, strong) UIBezierPath *currentPath;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// JBShapeLineLayer.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBShapeLineLayer.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBShapeLineLayerDefaultLinePhase = 1.0f;
|
||||
|
||||
// Structures
|
||||
static NSArray *kJBShapeLineLayerDefaultDashPattern = nil;
|
||||
|
||||
@implementation JBShapeLineLayer
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [JBShapeLineLayer class])
|
||||
{
|
||||
kJBShapeLineLayerDefaultDashPattern = @[@(3), @(2)];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithTag:(NSUInteger)tag filled:(BOOL)filled smoothedLine:(BOOL)smoothedLine lineStyle:(JBLineChartViewLineStyle)lineStyle currentPath:(UIBezierPath *)currentPath
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_tag = tag;
|
||||
_filled = filled;
|
||||
_currentPath = [currentPath copy];
|
||||
|
||||
// Position
|
||||
self.zPosition = filled ? 0.0f : 0.1f;
|
||||
self.fillColor = [UIColor clearColor].CGColor;
|
||||
|
||||
// Style
|
||||
if (lineStyle == JBLineChartViewLineStyleSolid)
|
||||
{
|
||||
self.lineDashPhase = 0.0;
|
||||
self.lineDashPattern = nil;
|
||||
}
|
||||
else if (lineStyle == JBLineChartViewLineStyleDashed)
|
||||
{
|
||||
self.lineDashPhase = kJBShapeLineLayerDefaultLinePhase;
|
||||
self.lineDashPattern = kJBShapeLineLayerDefaultDashPattern;
|
||||
}
|
||||
|
||||
// Smoothing
|
||||
if (smoothedLine)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
self.lineCap = kCALineCapRound;
|
||||
self.lineJoin = kCALineJoinRound;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lineStyle == JBLineChartViewLineStyleDashed)
|
||||
{
|
||||
self.lineCap = kCALineCapButt; // smoothed, dashed lines need butt caps
|
||||
}
|
||||
else
|
||||
{
|
||||
self.lineCap = kCALineCapRound;
|
||||
}
|
||||
self.lineJoin = kCALineJoinRound;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
self.lineCap = kCALineCapButt;
|
||||
self.lineJoin = kCALineJoinMiter;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.lineCap = kCALineCapButt;
|
||||
self.lineJoin = kCALineJoinMiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// JBLineChartLine.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Charts
|
||||
#import "JBLineChartView.h"
|
||||
|
||||
@interface JBLineChartLine : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSArray *lineChartPoints;
|
||||
@property (nonatomic, assign) BOOL smoothedLine;
|
||||
@property (nonatomic, assign) JBLineChartViewLineStyle lineStyle;
|
||||
@property (nonatomic, assign) JBLineChartViewColorStyle colorStyle;
|
||||
@property (nonatomic, assign) JBLineChartViewColorStyle fillColorStyle;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// JBLineChartLine.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBLineChartLine.h"
|
||||
|
||||
@implementation JBLineChartLine
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_lineChartPoints = [NSArray array];
|
||||
_smoothedLine = NO;
|
||||
_lineStyle = JBLineChartViewLineStyleSolid;
|
||||
_colorStyle = JBLineChartViewColorStyleSolid;
|
||||
_fillColorStyle = JBLineChartViewColorStyleSolid;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// JBLineChartPoint.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JBLineChartPoint : NSObject
|
||||
|
||||
@property (nonatomic, assign) CGPoint position;
|
||||
@property (nonatomic, assign) BOOL hidden;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// JBLineChartPoint.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBLineChartPoint.h"
|
||||
|
||||
@implementation JBLineChartPoint
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_position = CGPointZero;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Compare
|
||||
|
||||
- (NSComparisonResult)compare:(JBLineChartPoint *)otherObject
|
||||
{
|
||||
return self.position.x > otherObject.position.x;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// JBLineChartDotView.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JBLineChartDotView : UIView
|
||||
|
||||
- (id)initWithRadius:(CGFloat)radius;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// JBLineChartDotView.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBLineChartDotView.h"
|
||||
|
||||
@implementation JBLineChartDotView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)initWithRadius:(CGFloat)radius
|
||||
{
|
||||
self = [super initWithFrame:CGRectMake(0, 0, (radius * 2.0f), (radius * 2.0f))];
|
||||
if (self)
|
||||
{
|
||||
self.clipsToBounds = YES;
|
||||
self.layer.cornerRadius = ((radius * 2.0f) * 0.5f);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// JBLineChartDotsView.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Numerics
|
||||
extern NSInteger const kJBLineChartDotsViewUnselectedLineIndex;
|
||||
|
||||
@protocol JBLineChartDotsViewDataSource;
|
||||
|
||||
@interface JBLineChartDotsView : UIView
|
||||
|
||||
@property (nonatomic, assign) id<JBLineChartDotsViewDataSource> delegate;
|
||||
@property (nonatomic, assign) NSInteger selectedLineIndex;
|
||||
@property (nonatomic, strong) NSDictionary *dotViewsDict;
|
||||
|
||||
// Data
|
||||
- (void)reloadDataAnimated:(BOOL)animated callback:(void (^)())callback;
|
||||
- (void)reloadDataAnimated:(BOOL)animated;
|
||||
- (void)reloadData;
|
||||
|
||||
// Setters
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex animated:(BOOL)animated;
|
||||
|
||||
// Getters
|
||||
- (UIView *)dotViewForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
@end
|
||||
|
||||
@protocol JBLineChartDotsViewDataSource <NSObject>
|
||||
|
||||
- (NSArray *)lineChartLinesForLineChartDotsView:(JBLineChartDotsView*)lineChartDotsView;
|
||||
- (UIColor *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView selectedColorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (CGFloat)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView dotRadiusForLineAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIView *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView dotViewAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (BOOL)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView shouldHideDotViewOnSelectionAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (BOOL)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,236 @@
|
||||
//
|
||||
// JBLineChartDotsView.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/25/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBLineChartDotsView.h"
|
||||
|
||||
// Additions
|
||||
#import "NSMutableArray+JBStack.h"
|
||||
|
||||
// Models
|
||||
#import "JBLineChartLine.h"
|
||||
#import "JBLineChartPoint.h"
|
||||
|
||||
// Views
|
||||
#import "JBLineChartDotView.h"
|
||||
#import "JBLineChartView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBLineChartDotsViewReloadDataAnimationDuration = 0.15f;
|
||||
NSInteger const kJBLineChartDotsViewUnselectedLineIndex = 0.25f;
|
||||
|
||||
@implementation JBLineChartDotsView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesForLineChartDotsView:)], @"JBLineChartDotsView // delegate must implement - (NSArray *)lineChartLinesForLineChartDotsView:(JBLineChartDotsView *)lineChartDotsView");
|
||||
NSArray *lineChartLines = [self.delegate lineChartLinesForLineChartDotsView:self];
|
||||
|
||||
if (animated)
|
||||
{
|
||||
// Reusable dot views
|
||||
__block NSMutableArray *mutableReusableDotViews = [NSMutableArray array];
|
||||
for (id key in [[self.dotViewsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
|
||||
{
|
||||
NSArray *dotViews = [self.dotViewsDict objectForKey:key];
|
||||
[mutableReusableDotViews addObjectsFromArray:dotViews];
|
||||
}
|
||||
|
||||
NSUInteger lineIndex = 0;
|
||||
for (JBLineChartLine *lineChartLine in lineChartLines)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:showsDotsForLineAtLineIndex:)], @"JBLineChartDotsView // delegate must implement - (BOOL)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
if ([self.delegate lineChartDotsView:self showsDotsForLineAtLineIndex:lineIndex]) // line at index contains dots
|
||||
{
|
||||
NSArray *sortedLineChartPoints = [lineChartLine.lineChartPoints sortedArrayUsingSelector:@selector(compare:)];
|
||||
for (NSUInteger horizontalIndex = 0; horizontalIndex < [sortedLineChartPoints count]; horizontalIndex++)
|
||||
{
|
||||
JBLineChartPoint *lineChartPoint = [sortedLineChartPoints objectAtIndex:horizontalIndex];
|
||||
if(lineChartPoint.hidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
__block UIView *dotView = [mutableReusableDotViews jb_pop];
|
||||
if (dotView != nil)
|
||||
{
|
||||
[UIView animateWithDuration:kJBLineChartDotsViewReloadDataAnimationDuration animations:^{
|
||||
dotView.center = CGPointMake(lineChartPoint.position.x, lineChartPoint.position.y); // animate move
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
lineIndex++;
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kJBLineChartDotsViewReloadDataAnimationDuration * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove legacy dots
|
||||
for (JBLineChartDotView *dotView in self.subviews)
|
||||
{
|
||||
[dotView removeFromSuperview];
|
||||
}
|
||||
|
||||
// Create new dots
|
||||
NSUInteger lineIndex = 0;
|
||||
NSMutableDictionary *mutableDotViewsDict = [NSMutableDictionary dictionary];
|
||||
for (JBLineChartLine *lineChartLine in lineChartLines)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:showsDotsForLineAtLineIndex:)], @"JBLineChartDotsView // delegate must implement - (BOOL)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
if ([self.delegate lineChartDotsView:self showsDotsForLineAtLineIndex:lineIndex]) // line at index contains dots
|
||||
{
|
||||
NSMutableArray *mutableDotViews = [NSMutableArray array];
|
||||
NSArray *sortedLineChartPoints = [lineChartLine.lineChartPoints sortedArrayUsingSelector:@selector(compare:)];
|
||||
for (NSUInteger horizontalIndex = 0; horizontalIndex < [sortedLineChartPoints count]; horizontalIndex++)
|
||||
{
|
||||
JBLineChartPoint *lineChartPoint = [sortedLineChartPoints objectAtIndex:horizontalIndex];
|
||||
if(lineChartPoint.hidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UIView *dotView = [self dotViewForHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
dotView.center = CGPointMake(lineChartPoint.position.x, lineChartPoint.position.y);
|
||||
[mutableDotViews addObject:dotView];
|
||||
[self addSubview:dotView];
|
||||
}
|
||||
[mutableDotViewsDict setObject:[NSArray arrayWithArray:mutableDotViews] forKey:[NSNumber numberWithInteger:lineIndex]];
|
||||
}
|
||||
lineIndex++;
|
||||
}
|
||||
self.dotViewsDict = [NSDictionary dictionaryWithDictionary:mutableDotViewsDict];
|
||||
if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated
|
||||
{
|
||||
[self reloadDataAnimated:animated callback:nil];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
[self reloadDataAnimated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex animated:(BOOL)animated
|
||||
{
|
||||
_selectedLineIndex = selectedLineIndex;
|
||||
|
||||
__weak JBLineChartDotsView* weakSelf = self;
|
||||
|
||||
dispatch_block_t adjustDots = ^{
|
||||
[weakSelf.dotViewsDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
NSUInteger horizontalIndex = 0;
|
||||
for (UIView *dotView in (NSArray *)obj)
|
||||
{
|
||||
if ([key isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
NSInteger lineIndex = [((NSNumber *)key) intValue];
|
||||
|
||||
// Internal dot
|
||||
if ([dotView isKindOfClass:[JBLineChartDotView class]])
|
||||
{
|
||||
if (weakSelf.selectedLineIndex == lineIndex)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:selectedColorForDotAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (UIColor *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView selectedColorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
dotView.backgroundColor = [self.delegate lineChartDotsView:self selectedColorForDotAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:colorForDotAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (UIColor *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
dotView.backgroundColor = [self.delegate lineChartDotsView:self colorForDotAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
dotView.alpha = (weakSelf.selectedLineIndex == kJBLineChartDotsViewUnselectedLineIndex) ? 1.0f : 0.0f; // hide dots on off-selection
|
||||
}
|
||||
}
|
||||
// Custom dot
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:shouldHideDotViewOnSelectionAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (BOOL)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView shouldHideDotViewOnSelectionAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
BOOL hideDotView = [self.delegate lineChartDotsView:self shouldHideDotViewOnSelectionAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
if (weakSelf.selectedLineIndex == lineIndex)
|
||||
{
|
||||
dotView.alpha = hideDotView ? 0.0f : 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
dotView.alpha = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
horizontalIndex++;
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration animations:^{
|
||||
adjustDots();
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustDots();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex
|
||||
{
|
||||
[self setSelectedLineIndex:selectedLineIndex animated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIView *)dotViewForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:dotViewAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (UIView *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView dotViewAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
UIView *dotView = [self.delegate lineChartDotsView:self dotViewAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
|
||||
// System dot
|
||||
if (dotView == nil)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:dotRadiusForLineAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (CGFloat)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView dotRadiusForLineAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
CGFloat dotRadius = [self.delegate lineChartDotsView:self dotRadiusForLineAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
|
||||
dotView = [[JBLineChartDotView alloc] initWithRadius:dotRadius];
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartDotsView:colorForDotAtHorizontalIndex:atLineIndex:)], @"JBLineChartDotsView // delegate must implement - (UIColor *)lineChartDotsView:(JBLineChartDotsView *)lineChartDotsView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
|
||||
dotView.backgroundColor = [self.delegate lineChartDotsView:self colorForDotAtHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
|
||||
}
|
||||
|
||||
return dotView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// JBLineChartLinesView.h
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/26/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Numerics
|
||||
extern NSInteger const kJBLineChartLinesViewUnselectedLineIndex;
|
||||
|
||||
@protocol JBLineChartLinesViewDataSource;
|
||||
|
||||
@interface JBLineChartLinesView : UIView
|
||||
|
||||
@property (nonatomic, assign) id<JBLineChartLinesViewDataSource> delegate;
|
||||
@property (nonatomic, assign) NSInteger selectedLineIndex; // -1 to unselect
|
||||
|
||||
// Data
|
||||
- (void)reloadDataAnimated:(BOOL)animated callback:(void (^)())callback;
|
||||
- (void)reloadDataAnimated:(BOOL)animated;
|
||||
- (void)reloadData;
|
||||
|
||||
// Setters
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex animated:(BOOL)animated;
|
||||
|
||||
// Callback helpers
|
||||
- (void)fireCallback:(void (^)())callback;
|
||||
|
||||
@end
|
||||
|
||||
@protocol JBLineChartLinesViewDataSource <NSObject>
|
||||
|
||||
- (NSArray *)lineChartLinesForLineChartLinesView:(JBLineChartLinesView *)lineChartLinesView;
|
||||
- (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView colorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView gradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView widthForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionFillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,594 @@
|
||||
//
|
||||
// JBLineChartLinesView.m
|
||||
// JBChartViewDemo
|
||||
//
|
||||
// Created by Terry Worona on 12/26/15.
|
||||
// Copyright © 2015 Jawbone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JBLineChartLinesView.h"
|
||||
|
||||
// Layers
|
||||
#import "JBGradientLineLayer.h"
|
||||
#import "JBShapeLineLayer.h"
|
||||
|
||||
// Models
|
||||
#import "JBLineChartLine.h"
|
||||
#import "JBLineChartPoint.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBLineChartLinesViewMiterLimit = -5.0;
|
||||
CGFloat const kJBLineChartLinesViewSmoothThresholdSlope = 0.01f;
|
||||
CGFloat const kJBLineChartLinesViewReloadDataAnimationDuration = 0.15f;
|
||||
NSInteger const kJBLineChartLinesViewSmoothThresholdVertical = 1;
|
||||
NSInteger const kJBLineChartLinesViewUnselectedLineIndex = -1;
|
||||
|
||||
@interface JBLineChartLinesView ()
|
||||
|
||||
@property (nonatomic, assign) BOOL animated; // for reload
|
||||
|
||||
// Getters
|
||||
- (UIBezierPath *)bezierPathForLineChartLine:(JBLineChartLine *)lineChartLine filled:(BOOL)filled;
|
||||
- (JBShapeLineLayer *)shapeLineLayerForLineIndex:(NSUInteger)lineIndex filled:(BOOL)filled;
|
||||
- (JBGradientLineLayer *)gradientLineLayerForLineIndex:(NSUInteger)lineIndex filled:(BOOL)filled;
|
||||
- (CABasicAnimation *)basicPathAnimationFromBezierPath:(UIBezierPath *)fromBezierPath toBezierPath:(UIBezierPath *)toBezierPath;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JBLineChartLinesView
|
||||
|
||||
#pragma mark - Alloc/Init
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Memory Management
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
[super drawRect:rect];
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesForLineChartLinesView:)], @"JBLineChartLinesView // delegate must implement - (NSArray *)lineChartLinesForLineChartLinesView:(JBLineChartLinesView *)lineChartLinesView");
|
||||
NSArray *chartData = [self.delegate lineChartLinesForLineChartLinesView:self];
|
||||
|
||||
for (int lineIndex=0; lineIndex<[chartData count]; lineIndex++)
|
||||
{
|
||||
JBLineChartLine *lineChartLine = [chartData objectAtIndex:lineIndex];
|
||||
{
|
||||
UIBezierPath *linePath = [self bezierPathForLineChartLine:lineChartLine filled:NO];
|
||||
UIBezierPath *fillPath = [self bezierPathForLineChartLine:lineChartLine filled:YES];
|
||||
|
||||
if (linePath == nil || fillPath == nil)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JBShapeLineLayer *shapeLineLayer = [self shapeLineLayerForLineIndex:lineIndex filled:NO];
|
||||
if (shapeLineLayer == nil)
|
||||
{
|
||||
shapeLineLayer = [[JBShapeLineLayer alloc] initWithTag:lineIndex filled:NO smoothedLine:lineChartLine.smoothedLine lineStyle:lineChartLine.lineStyle currentPath:linePath];
|
||||
}
|
||||
|
||||
JBShapeLineLayer *shapeLineFillLayer = [self shapeLineLayerForLineIndex:lineIndex filled:YES];
|
||||
if (shapeLineFillLayer == nil)
|
||||
{
|
||||
shapeLineFillLayer = [[JBShapeLineLayer alloc] initWithTag:lineIndex filled:YES smoothedLine:lineChartLine.smoothedLine lineStyle:lineChartLine.lineStyle currentPath:nil]; // path not needed for fills (unsupported)
|
||||
}
|
||||
|
||||
// Width
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:widthForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView widthForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.lineWidth = [self.delegate lineChartLinesView:self widthForLineAtLineIndex:lineIndex];
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:widthForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView widthForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineFillLayer.lineWidth = [self.delegate lineChartLinesView:self widthForLineAtLineIndex:lineIndex];
|
||||
|
||||
// Colors
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:colorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView colorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.strokeColor = [self.delegate lineChartLinesView:self colorForLineAtLineIndex:lineIndex].CGColor;
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:fillColorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillColorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineFillLayer.fillColor = [self.delegate lineChartLinesView:self fillColorForLineAtLineIndex:lineIndex].CGColor;
|
||||
|
||||
// Bounds
|
||||
shapeLineLayer.frame = self.bounds;
|
||||
shapeLineFillLayer.frame = self.bounds;
|
||||
|
||||
// Note: fills go first because the lines must go on top
|
||||
|
||||
/*
|
||||
* Solid fill
|
||||
*/
|
||||
if (lineChartLine.fillColorStyle == JBLineChartViewColorStyleSolid)
|
||||
{
|
||||
shapeLineFillLayer.path = fillPath.CGPath;
|
||||
[self.layer addSublayer:shapeLineFillLayer];
|
||||
}
|
||||
|
||||
/*
|
||||
* Gradient fill
|
||||
*/
|
||||
else if (lineChartLine.fillColorStyle == JBLineChartViewColorStyleGradient)
|
||||
{
|
||||
JBGradientLineLayer *gradientLineFillLayer = [self gradientLineLayerForLineIndex:lineIndex filled:YES];
|
||||
if (gradientLineFillLayer == nil)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:fillGradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillGradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
gradientLineFillLayer = [[JBGradientLineLayer alloc] initWithGradientLayer:[self.delegate lineChartLinesView:self fillGradientForLineAtLineIndex:lineIndex] tag:lineIndex filled:YES currentPath:nil];
|
||||
}
|
||||
gradientLineFillLayer.frame = shapeLineFillLayer.frame;
|
||||
|
||||
shapeLineFillLayer.path = fillPath.CGPath;
|
||||
CGColorRef shapeLayerStrokeColor = shapeLineLayer.strokeColor;
|
||||
shapeLineLayer.strokeColor = [UIColor colorWithWhite:1 alpha:[gradientLineFillLayer alpha]].CGColor; // mask uses alpha only
|
||||
shapeLineFillLayer.fillColor = [UIColor colorWithWhite:1 alpha:[gradientLineFillLayer alpha]].CGColor; // mask uses alpha only
|
||||
gradientLineFillLayer.mask = shapeLineFillLayer;
|
||||
[self.layer addSublayer:gradientLineFillLayer];
|
||||
|
||||
// Refresh shape layer stroke (used below)
|
||||
shapeLineLayer.strokeColor = shapeLayerStrokeColor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Solid line
|
||||
*/
|
||||
if (lineChartLine.colorStyle == JBLineChartViewColorStyleSolid)
|
||||
{
|
||||
if (self.animated)
|
||||
{
|
||||
[shapeLineLayer addAnimation:[self basicPathAnimationFromBezierPath:shapeLineLayer.currentPath toBezierPath:linePath] forKey:@"shapeLayerPathAnimation"];
|
||||
}
|
||||
else
|
||||
{
|
||||
shapeLineLayer.path = linePath.CGPath;
|
||||
}
|
||||
|
||||
shapeLineLayer.currentPath = [linePath copy];
|
||||
[self.layer addSublayer:shapeLineLayer];
|
||||
}
|
||||
|
||||
/*
|
||||
* Gradient line
|
||||
*/
|
||||
else if (lineChartLine.colorStyle == JBLineChartViewColorStyleGradient)
|
||||
{
|
||||
JBGradientLineLayer *gradientLineLayer = [self gradientLineLayerForLineIndex:lineIndex filled:NO];
|
||||
if (gradientLineLayer == nil)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:gradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView gradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
gradientLineLayer = [[JBGradientLineLayer alloc] initWithGradientLayer:[self.delegate lineChartLinesView:self gradientForLineAtLineIndex:lineIndex] tag:lineIndex filled:NO currentPath:linePath];
|
||||
}
|
||||
gradientLineLayer.frame = shapeLineLayer.frame;
|
||||
|
||||
if (self.animated)
|
||||
{
|
||||
[gradientLineLayer.mask addAnimation:[self basicPathAnimationFromBezierPath:gradientLineLayer.currentPath toBezierPath:linePath] forKey:@"gradientLayerMaskAnimation"];
|
||||
}
|
||||
else
|
||||
{
|
||||
shapeLineLayer.path = linePath.CGPath;
|
||||
shapeLineLayer.strokeColor = [UIColor colorWithWhite:1 alpha:[gradientLineLayer alpha]].CGColor; // mask uses alpha only
|
||||
gradientLineLayer.mask = shapeLineLayer;
|
||||
}
|
||||
|
||||
gradientLineLayer.currentPath = [linePath copy];
|
||||
[self.layer addSublayer:gradientLineLayer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.animated = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated callback:(void (^)())callback
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesForLineChartLinesView:)], @"JBLineChartLinesView // delegate must implement - (NSArray *)lineChartLinesForLineChartLinesView:(JBLineChartLinesView *)lineChartLinesView");
|
||||
NSArray *chartData = [self.delegate lineChartLinesForLineChartLinesView:self];
|
||||
|
||||
NSUInteger lineCount = [chartData count];
|
||||
|
||||
__weak JBLineChartLinesView* weakSelf = self;
|
||||
|
||||
dispatch_block_t completionBlock = ^{
|
||||
weakSelf.animated = animated;
|
||||
[weakSelf setNeedsDisplay]; // re-draw layers
|
||||
if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// Mark layers for animation or removal
|
||||
NSMutableArray *mutableRemovedLayers = [NSMutableArray array];
|
||||
for (CALayer *layer in [self.layer sublayers])
|
||||
{
|
||||
BOOL removeLayer = NO;
|
||||
|
||||
if ([layer isKindOfClass:[JBShapeLineLayer class]])
|
||||
{
|
||||
removeLayer = (((JBShapeLineLayer *)layer).tag >= lineCount);
|
||||
}
|
||||
else if ([layer isKindOfClass:[JBGradientLineLayer class]])
|
||||
{
|
||||
removeLayer = (((JBGradientLineLayer *)layer).tag >= lineCount);
|
||||
}
|
||||
|
||||
if (removeLayer)
|
||||
{
|
||||
[mutableRemovedLayers addObject:layer];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove legacy layers
|
||||
NSArray *removedLayers = [NSArray arrayWithArray:mutableRemovedLayers];
|
||||
if ([removedLayers count] > 0)
|
||||
{
|
||||
for (int index=0; index<[removedLayers count]; index++)
|
||||
{
|
||||
CALayer *removedLayer = [removedLayers objectAtIndex:index];
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[CATransaction begin];
|
||||
{
|
||||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||
animation.fromValue = [NSNumber numberWithFloat:1.0f];
|
||||
animation.toValue = [NSNumber numberWithFloat:0.0f];
|
||||
animation.duration = kJBLineChartLinesViewReloadDataAnimationDuration;
|
||||
animation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeInEaseOut"];
|
||||
animation.fillMode = kCAFillModeBoth;
|
||||
animation.removedOnCompletion = NO;
|
||||
|
||||
[CATransaction setCompletionBlock:^{
|
||||
[removedLayer removeFromSuperlayer];
|
||||
if (index == [removedLayers count]-1)
|
||||
{
|
||||
completionBlock();
|
||||
}
|
||||
}];
|
||||
|
||||
[removedLayer addAnimation:animation forKey:@"removeShapeLayerAnimation"];
|
||||
}
|
||||
[CATransaction commit];
|
||||
}
|
||||
else
|
||||
{
|
||||
[removedLayer removeFromSuperlayer];
|
||||
if (index == [removedLayers count]-1)
|
||||
{
|
||||
completionBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
completionBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated
|
||||
{
|
||||
[self reloadDataAnimated:animated callback:nil];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
[self reloadDataAnimated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex animated:(BOOL)animated
|
||||
{
|
||||
_selectedLineIndex = selectedLineIndex;
|
||||
|
||||
__weak JBLineChartLinesView* weakSelf = self;
|
||||
|
||||
dispatch_block_t adjustLines = ^{
|
||||
NSMutableArray *layersToReplace = [NSMutableArray array];
|
||||
|
||||
NSString * const oldLayerKey = @"oldLayer";
|
||||
NSString * const newLayerKey = @"newLayer";
|
||||
|
||||
for (CALayer *layer in [weakSelf.layer sublayers])
|
||||
{
|
||||
/*
|
||||
* Solid line or fill
|
||||
*/
|
||||
if ([layer isKindOfClass:[JBShapeLineLayer class]])
|
||||
{
|
||||
JBShapeLineLayer *shapeLineLayer = (JBShapeLineLayer * )layer;
|
||||
|
||||
if (shapeLineLayer.filled)
|
||||
{
|
||||
// Selected solid fill
|
||||
if (shapeLineLayer.tag == weakSelf.selectedLineIndex)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:selectionFillColorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.fillColor = [self.delegate lineChartLinesView:self selectionFillColorForLineAtLineIndex:shapeLineLayer.tag].CGColor;
|
||||
shapeLineLayer.opacity = 1.0f;
|
||||
}
|
||||
// Unselected solid fill
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:fillColorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillColorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.fillColor = [self.delegate lineChartLinesView:self fillColorForLineAtLineIndex:shapeLineLayer.tag].CGColor;
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:dimmedSelectionOpacityAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.opacity = (weakSelf.selectedLineIndex == kJBLineChartLinesViewUnselectedLineIndex) ? 1.0f : [self.delegate lineChartLinesView:self dimmedSelectionOpacityAtLineIndex:shapeLineLayer.tag];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Selected solid line
|
||||
if (shapeLineLayer.tag == weakSelf.selectedLineIndex)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:selectionColorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.strokeColor = [self.delegate lineChartLinesView:self selectionColorForLineAtLineIndex:shapeLineLayer.tag].CGColor;
|
||||
shapeLineLayer.opacity = 1.0f;
|
||||
}
|
||||
// Unselected solid line
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:colorForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (UIColor *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView colorForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.strokeColor = [self.delegate lineChartLinesView:self colorForLineAtLineIndex:shapeLineLayer.tag].CGColor;
|
||||
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:dimmedSelectionOpacityAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.opacity = (weakSelf.selectedLineIndex == kJBLineChartLinesViewUnselectedLineIndex) ? 1.0f : [self.delegate lineChartLinesView:self dimmedSelectionOpacityAtLineIndex:shapeLineLayer.tag];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gradient line or fill
|
||||
*/
|
||||
else if ([layer isKindOfClass:[CAGradientLayer class]])
|
||||
{
|
||||
CAGradientLayer *gradientLayer = (CAGradientLayer * )layer;
|
||||
|
||||
if ([gradientLayer.mask isKindOfClass:[JBShapeLineLayer class]])
|
||||
{
|
||||
JBShapeLineLayer *shapeLineLayer = (JBShapeLineLayer * )gradientLayer.mask;
|
||||
|
||||
if (shapeLineLayer.filled)
|
||||
{
|
||||
// Selected gradient fill
|
||||
if (shapeLineLayer.tag == weakSelf.selectedLineIndex)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:selectionFillGradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionFillGradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
CAGradientLayer *selectedFillGradient = [self.delegate lineChartLinesView:self selectionFillGradientForLineAtLineIndex:shapeLineLayer.tag];
|
||||
selectedFillGradient.frame = layer.frame;
|
||||
selectedFillGradient.mask = layer.mask;
|
||||
selectedFillGradient.opacity = 1.0f;
|
||||
[layersToReplace addObject:@{oldLayerKey: layer, newLayerKey: selectedFillGradient}];
|
||||
}
|
||||
// Unselected gradient fill
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:fillGradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView fillGradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
CAGradientLayer *unselectedFillGradient = [self.delegate lineChartLinesView:self fillGradientForLineAtLineIndex:shapeLineLayer.tag];
|
||||
unselectedFillGradient.frame = layer.frame;
|
||||
unselectedFillGradient.mask = layer.mask;
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:dimmedSelectionOpacityAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex");
|
||||
unselectedFillGradient.opacity = (weakSelf.selectedLineIndex == kJBLineChartLinesViewUnselectedLineIndex) ? 1.0f : [self.delegate lineChartLinesView:self dimmedSelectionOpacityAtLineIndex:shapeLineLayer.tag];
|
||||
[layersToReplace addObject:@{oldLayerKey: layer, newLayerKey: unselectedFillGradient}];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Selected gradient line
|
||||
if (shapeLineLayer.tag == weakSelf.selectedLineIndex)
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:selectionGradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView selectionGradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
CAGradientLayer *selectedGradient = [self.delegate lineChartLinesView:self selectionGradientForLineAtLineIndex:shapeLineLayer.tag];
|
||||
selectedGradient.frame = layer.frame;
|
||||
selectedGradient.mask = layer.mask;
|
||||
selectedGradient.opacity = 1.0f;
|
||||
[layersToReplace addObject:@{oldLayerKey: layer, newLayerKey: selectedGradient}];
|
||||
}
|
||||
// Unselected gradient line
|
||||
else
|
||||
{
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:gradientForLineAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CAGradientLayer *)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView gradientForLineAtLineIndex:(NSUInteger)lineIndex");
|
||||
CAGradientLayer *unselectedGradient = [self.delegate lineChartLinesView:self gradientForLineAtLineIndex:shapeLineLayer.tag];
|
||||
unselectedGradient.frame = layer.frame;
|
||||
unselectedGradient.mask = layer.mask;
|
||||
NSAssert([self.delegate respondsToSelector:@selector(lineChartLinesView:dimmedSelectionOpacityAtLineIndex:)], @"JBLineChartLinesView // delegate must implement - (CGFloat)lineChartLinesView:(JBLineChartLinesView *)lineChartLinesView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex");
|
||||
shapeLineLayer.opacity = (weakSelf.selectedLineIndex == kJBLineChartLinesViewUnselectedLineIndex) ? 1.0f : [self.delegate lineChartLinesView:self dimmedSelectionOpacityAtLineIndex:shapeLineLayer.tag];
|
||||
[layersToReplace addObject:@{oldLayerKey: layer, newLayerKey: unselectedGradient}];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NSDictionary *layerPair in layersToReplace)
|
||||
{
|
||||
[weakSelf.layer replaceSublayer:layerPair[oldLayerKey] with:layerPair[newLayerKey]];
|
||||
}
|
||||
};
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration animations:^{
|
||||
adjustLines();
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustLines();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex
|
||||
{
|
||||
[self setSelectedLineIndex:selectedLineIndex animated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIBezierPath *)bezierPathForLineChartLine:(JBLineChartLine *)lineChartLine filled:(BOOL)filled
|
||||
{
|
||||
if ([lineChartLine.lineChartPoints count] > 0)
|
||||
{
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
|
||||
|
||||
bezierPath.miterLimit = kJBLineChartLinesViewMiterLimit;
|
||||
|
||||
JBLineChartPoint *previousLineChartPoint = nil;
|
||||
CGFloat previousSlope = 0.0f;
|
||||
|
||||
BOOL visiblePointFound = NO;
|
||||
NSArray *sortedLineChartPoints = [lineChartLine.lineChartPoints sortedArrayUsingSelector:@selector(compare:)];
|
||||
CGFloat firstXPosition = 0.0f;
|
||||
CGFloat firstYPosition = 0.0f;
|
||||
CGFloat lastXPosition = 0.0f;
|
||||
CGFloat lastYPosition = 0.0f;
|
||||
|
||||
for (NSUInteger index=0; index<[sortedLineChartPoints count]; index++)
|
||||
{
|
||||
JBLineChartPoint *lineChartPoint = [sortedLineChartPoints objectAtIndex:index];
|
||||
|
||||
if (lineChartPoint.hidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!visiblePointFound)
|
||||
{
|
||||
[bezierPath moveToPoint:CGPointMake(lineChartPoint.position.x, lineChartPoint.position.y)];
|
||||
firstXPosition = lineChartPoint.position.x;
|
||||
firstYPosition = lineChartPoint.position.y;
|
||||
visiblePointFound = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
JBLineChartPoint *nextLineChartPoint = nil;
|
||||
if (index != ([lineChartLine.lineChartPoints count] - 1))
|
||||
{
|
||||
nextLineChartPoint = [sortedLineChartPoints objectAtIndex:(index + 1)];
|
||||
}
|
||||
|
||||
CGFloat nextSlope = (nextLineChartPoint != nil) ? ((nextLineChartPoint.position.y - lineChartPoint.position.y)) / ((nextLineChartPoint.position.x - lineChartPoint.position.x)) : previousSlope;
|
||||
CGFloat currentSlope = ((lineChartPoint.position.y - previousLineChartPoint.position.y)) / (lineChartPoint.position.x-previousLineChartPoint.position.x);
|
||||
|
||||
BOOL deltaFromNextSlope = ((currentSlope >= (nextSlope + kJBLineChartLinesViewSmoothThresholdSlope)) || (currentSlope <= (nextSlope - kJBLineChartLinesViewSmoothThresholdSlope)));
|
||||
BOOL deltaFromPreviousSlope = ((currentSlope >= (previousSlope + kJBLineChartLinesViewSmoothThresholdSlope)) || (currentSlope <= (previousSlope - kJBLineChartLinesViewSmoothThresholdSlope)));
|
||||
BOOL deltaFromPreviousY = (lineChartPoint.position.y >= previousLineChartPoint.position.y + kJBLineChartLinesViewSmoothThresholdVertical) || (lineChartPoint.position.y <= previousLineChartPoint.position.y - kJBLineChartLinesViewSmoothThresholdVertical);
|
||||
|
||||
if (lineChartLine.smoothedLine && deltaFromNextSlope && deltaFromPreviousSlope && deltaFromPreviousY)
|
||||
{
|
||||
CGFloat deltaX = lineChartPoint.position.x - previousLineChartPoint.position.x;
|
||||
CGFloat controlPointX = previousLineChartPoint.position.x + (deltaX / 2);
|
||||
|
||||
CGPoint controlPoint1 = CGPointMake(controlPointX, previousLineChartPoint.position.y);
|
||||
CGPoint controlPoint2 = CGPointMake(controlPointX, lineChartPoint.position.y);
|
||||
|
||||
[bezierPath addCurveToPoint:CGPointMake(lineChartPoint.position.x, lineChartPoint.position.y) controlPoint1:controlPoint1 controlPoint2:controlPoint2];
|
||||
}
|
||||
else
|
||||
{
|
||||
[bezierPath addLineToPoint:CGPointMake(lineChartPoint.position.x, lineChartPoint.position.y)];
|
||||
}
|
||||
|
||||
lastXPosition = lineChartPoint.position.x;
|
||||
lastYPosition = lineChartPoint.position.y;
|
||||
previousSlope = currentSlope;
|
||||
}
|
||||
previousLineChartPoint = lineChartPoint;
|
||||
}
|
||||
|
||||
if (filled)
|
||||
{
|
||||
UIBezierPath *filledBezierPath = [bezierPath copy];
|
||||
|
||||
if(visiblePointFound)
|
||||
{
|
||||
[filledBezierPath addLineToPoint:CGPointMake(lastXPosition, lastYPosition)];
|
||||
[filledBezierPath addLineToPoint:CGPointMake(lastXPosition, self.bounds.size.height)];
|
||||
|
||||
[filledBezierPath addLineToPoint:CGPointMake(firstXPosition, self.bounds.size.height)];
|
||||
[filledBezierPath addLineToPoint:CGPointMake(firstXPosition, firstYPosition)];
|
||||
}
|
||||
|
||||
return filledBezierPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bezierPath;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (JBShapeLineLayer *)shapeLineLayerForLineIndex:(NSUInteger)lineIndex filled:(BOOL)filled
|
||||
{
|
||||
for (CALayer *layer in [self.layer sublayers])
|
||||
{
|
||||
if ([layer isKindOfClass:[JBShapeLineLayer class]])
|
||||
{
|
||||
if (((JBShapeLineLayer *)layer).tag == lineIndex && ((JBShapeLineLayer *)layer).filled == filled)
|
||||
{
|
||||
return (JBShapeLineLayer *)layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (JBGradientLineLayer *)gradientLineLayerForLineIndex:(NSUInteger)lineIndex filled:(BOOL)filled
|
||||
{
|
||||
for (CALayer *layer in [self.layer sublayers])
|
||||
{
|
||||
if ([layer isKindOfClass:[JBGradientLineLayer class]])
|
||||
{
|
||||
if (((JBGradientLineLayer *)layer).tag == lineIndex && ((JBGradientLineLayer *)layer).filled == filled)
|
||||
{
|
||||
return (JBGradientLineLayer *)layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CABasicAnimation *)basicPathAnimationFromBezierPath:(UIBezierPath *)fromBezierPath toBezierPath:(UIBezierPath *)toBezierPath
|
||||
{
|
||||
CABasicAnimation *basicPathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
|
||||
basicPathAnimation.fromValue = (id)fromBezierPath.CGPath;
|
||||
basicPathAnimation.toValue = (id)toBezierPath.CGPath;
|
||||
basicPathAnimation.duration = kJBLineChartLinesViewReloadDataAnimationDuration;
|
||||
basicPathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeInEaseOut"];
|
||||
basicPathAnimation.fillMode = kCAFillModeBoth;
|
||||
basicPathAnimation.removedOnCompletion = NO;
|
||||
return basicPathAnimation;
|
||||
}
|
||||
|
||||
#pragma mark - Callback Helpers
|
||||
|
||||
- (void)fireCallback:(void (^)())callback
|
||||
{
|
||||
dispatch_block_t callbackCopy = [callback copy];
|
||||
|
||||
if (callbackCopy != nil)
|
||||
{
|
||||
callbackCopy();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,212 @@
|
||||
## Customization
|
||||
|
||||
Both the line and bar charts support a robust set of customization options.
|
||||
|
||||
The background of a bar or line chart can be set just like any other view:
|
||||
|
||||
self.barChartView.backgroundColor = ...; // UIColor
|
||||
self.lineChartView.backgroundColor = ...; // UIColor
|
||||
|
||||
Any <i>JBChartView</i> subclass supports the use of headers and footers (similiar to that of <i>UITableView</i>):
|
||||
|
||||
self.barChartView.footerView = ...; // UIView
|
||||
self.lineChartView.headerView = ...; // UIView
|
||||
|
||||
Lastly, any JBChartView subclass can be collapsed or expanded programmatically via the <i>state</i> property. If you chose to animate state changes, a callback helper can be used to notify you when the animation has completed:
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback;
|
||||
|
||||
#### JBBarChartView
|
||||
|
||||
A bar chart can be inverted such that it's orientation is top->down (including the selection view) by setting the following property:
|
||||
|
||||
@property (nonatomic, assign, getter=isInverted) BOOL inverted;
|
||||
|
||||
By default, a chart's bars will be black and flat. They can be customized by supplying a UIView subclass through the <i>optional</i> protocol:
|
||||
|
||||
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index
|
||||
{
|
||||
return ...; // color of line in chart
|
||||
}
|
||||
|
||||
If you don't require a custom UIView, simply supply a color for the bar instead:
|
||||
|
||||
- (UIColor *)barChartView:(JBBarChartView *)barChartView colorForBarViewAtIndex:(NSUInteger)index;
|
||||
|
||||
If a solid color isn't your cup of tea, you can expose a gradient to be applied across the entire chart:
|
||||
|
||||
- (CAGradientLayer *)barGradientForBarChartView:(JBBarChartView *)barChartView;
|
||||
|
||||
Furthermore, the color of the selection bar (on touch events) can be customized via the <i>optional</i> protocol:
|
||||
|
||||
- (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView
|
||||
{
|
||||
return ...; // color of selection view
|
||||
}
|
||||
|
||||
<b>Note</b>: The delegate will request a custom UIView, followed by a color and lastly a gradient. If nothing is supplied, a plain black bar will be used.
|
||||
|
||||
Lastly, a bar chart's selection events are delegated back via:
|
||||
|
||||
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
- (void)didDeselectBarChartView:(JBBarChartView *)barChartView
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
The <b>touchPoint</b> is especially important as it allows you to add custom elements to your chart during selection events. Refer to the demo project (<b>JBarChartViewController</b>) to see how a tooltip can be used to display additional information during selection events.
|
||||
|
||||
#### JBLineChartView
|
||||
|
||||
The color, width and style of each line in the chart can be customized via the <i>optional</i> protocol:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of line in chart
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView fillColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of area under line in chart
|
||||
}
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView widthForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // width of line in chart
|
||||
}
|
||||
|
||||
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // style of line in chart (solid or dashed)
|
||||
}
|
||||
|
||||
Additionally, the line and fill color style can be customzized via the <i>optional</i> protocols (below). The line & fill color style apply to both selected and non-selected scenarios, meaning, if your line has a solid style, it's selected style will also be solid.
|
||||
|
||||
- (JBLineChartViewColorStyle)lineChartView:(JBLineChartView *)lineChartView colorStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color line style of a line in the chart
|
||||
}
|
||||
|
||||
- (JBLineChartViewColorStyle)lineChartView:(JBLineChartView *)lineChartView fillColorStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color style for the area under a line in the chart
|
||||
}
|
||||
|
||||
If a solid color style is used, the following <i>optional</i> protocols can be implemented:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView fillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Gradient color styles require a CAGradientLayer to be returned:
|
||||
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView gradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView fillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
**Note**: gradients do not support multiple alphas. The alpha of gradient's first color be used throughout.
|
||||
|
||||
Defining a gradient to use is simple and flexible. For example, this would be a horizontal gradient from blue to green:
|
||||
|
||||
CAGradientLayer *gradient = [CAGradientLayer new];
|
||||
gradient.startPoint = CGPointMake(0.0, 0.0);
|
||||
gradient.endPoint = CGPointMake(1.0, 0.0);
|
||||
gradient.colors = @[(id)[UIColor blueColor].CGColor, (id)[UIColor greenColor].CGColor];
|
||||
|
||||
As mentioned prior, the same color style is duplicated for selection events:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionFillGradientForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
The color and width of the selection view along with the color of the selected line can be customized via the <i>optional</i> protocols:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView verticalSelectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of selection view
|
||||
}
|
||||
|
||||
- (CGFloat)verticalSelectionWidthForLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
return ...; // width of selection view
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of selected line
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of area under selected line
|
||||
}
|
||||
|
||||
When using a gradient for the line or fill, a different gradient can provided for selection. If the selection gradient is not provided, it will default to the line and fill gradient provided for the line.
|
||||
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionGradientForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // gradient for selected line
|
||||
}
|
||||
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView selectionFillGradientForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // gradient for area under selected line
|
||||
}
|
||||
|
||||
By default, each line will not show dots for each point. To enable this on a per-line basis:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
To the radius of each dot (default is 6x the line width, or 3x the diameter), implement:
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dotRadiusForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
To customize the color of each dot during selection and non-selection events (default is white and black respectively), implement:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Alternatively, you can supply your own UIView instead of using the default impelmentation:
|
||||
|
||||
- (UIView *)lineChartView:(JBLineChartView *)lineChartView dotViewAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Custom dot views are automatically shown when selected unless the following is implemented:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView shouldHideDotViewOnSelectionAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
As well, by default, each line will have squared off end caps and connection points. To enable rounded connections and end caps:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView smoothLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Furthermore, a line chart's selection events are delegated back via:
|
||||
|
||||
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
- (void)didDeselectLineInLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
Upon selection, all other lines (+ fills) will be dimmed to 20% opacity (default). To change this value, implement:
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
// Return new opacity (0.0 to hide completely, and 1.0 to have no effect)
|
||||
}
|
||||
|
||||
If you don't want a line to be selectable:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView shouldIgnoreSelectionAtIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return NO; // Check line index
|
||||
}
|
||||
|
||||
The <b>touchPoint</b> is especially important as it allows you to add custom elements to your chart during selection events. Refer to the demo project (<b>JBLineChartViewController</b>) to see how a tooltip can be used to display additional information during selection events.
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "JBChartView"
|
||||
s.version = "2.8.13"
|
||||
s.version = "2.9.1"
|
||||
s.summary = "Jawbone's iOS-based charting library for both line and bar graphs."
|
||||
s.homepage = "https://github.com/Jawbone/JBChartView"
|
||||
|
||||
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
||||
s.author = { "Terry Worona" => "tworona@jawbone.com" }
|
||||
s.source = {
|
||||
:git => "https://github.com/Jawbone/JBChartView.git",
|
||||
:tag => "v2.8.13"
|
||||
:tag => "v2.9.1"
|
||||
}
|
||||
|
||||
s.platform = :ios, '6.0'
|
||||
|
||||
@@ -7,8 +7,21 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
566C73A61C026858000EC785 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 566C73A51C026858000EC785 /* Default-568h@2x.png */; };
|
||||
94BDFC3419F933B2007492F6 /* JBLineChartMissingPointsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94BDFC3319F933B2007492F6 /* JBLineChartMissingPointsViewController.m */; };
|
||||
9B0725211829822A0052109B /* JBChartListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0725201829822A0052109B /* JBChartListViewController.m */; };
|
||||
9B13518F1C31FAB3000D4C92 /* NSMutableArray+JBStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351711C31FAB3000D4C92 /* NSMutableArray+JBStack.m */; };
|
||||
9B1351901C31FAB3000D4C92 /* JBBarChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351741C31FAB3000D4C92 /* JBBarChartView.m */; };
|
||||
9B1351911C31FAB3000D4C92 /* JBGradientBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351771C31FAB3000D4C92 /* JBGradientBarView.m */; };
|
||||
9B1351921C31FAB3000D4C92 /* JBChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B13517A1C31FAB3000D4C92 /* JBChartView.m */; };
|
||||
9B1351931C31FAB3000D4C92 /* JBLineChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B13517D1C31FAB3000D4C92 /* JBLineChartView.m */; };
|
||||
9B1351941C31FAB3000D4C92 /* JBGradientLineLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351801C31FAB3000D4C92 /* JBGradientLineLayer.m */; };
|
||||
9B1351951C31FAB3000D4C92 /* JBShapeLineLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351821C31FAB3000D4C92 /* JBShapeLineLayer.m */; };
|
||||
9B1351961C31FAB3000D4C92 /* JBLineChartLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351851C31FAB3000D4C92 /* JBLineChartLine.m */; };
|
||||
9B1351971C31FAB3000D4C92 /* JBLineChartPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1351871C31FAB3000D4C92 /* JBLineChartPoint.m */; };
|
||||
9B1351981C31FAB3000D4C92 /* JBLineChartDotsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B13518A1C31FAB3000D4C92 /* JBLineChartDotsView.m */; };
|
||||
9B1351991C31FAB3000D4C92 /* JBLineChartDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B13518C1C31FAB3000D4C92 /* JBLineChartDotView.m */; };
|
||||
9B13519A1C31FAB3000D4C92 /* JBLineChartLinesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B13518E1C31FAB3000D4C92 /* JBLineChartLinesView.m */; };
|
||||
9B2E530518218CF20079B9D2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B2E530418218CF20079B9D2 /* Foundation.framework */; };
|
||||
9B2E530718218CF20079B9D2 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B2E530618218CF20079B9D2 /* CoreGraphics.framework */; };
|
||||
9B2E530918218CF20079B9D2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B2E530818218CF20079B9D2 /* UIKit.framework */; };
|
||||
@@ -30,15 +43,12 @@
|
||||
9B698F17182D7DAE003C135F /* icon-bar-chart@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B698F13182D7DAE003C135F /* icon-bar-chart@2x.png */; };
|
||||
9B698F18182D7DAE003C135F /* icon-line-chart.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B698F14182D7DAE003C135F /* icon-line-chart.png */; };
|
||||
9B698F19182D7DAE003C135F /* icon-line-chart@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B698F15182D7DAE003C135F /* icon-line-chart@2x.png */; };
|
||||
9B6A68D31829AB9F006DB3BF /* JBChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A68D21829AB9F006DB3BF /* JBChartView.m */; };
|
||||
9B6A68DB1829ADE1006DB3BF /* JBBarChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A68DA1829ADE1006DB3BF /* JBBarChartView.m */; };
|
||||
9B6A68DE1829BE63006DB3BF /* JBBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A68DD1829BE63006DB3BF /* JBBarChartViewController.m */; };
|
||||
9B6A68E11829BED5006DB3BF /* JBLineChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A68E01829BED5006DB3BF /* JBLineChartViewController.m */; };
|
||||
9B7967EC198313E30003A2B0 /* JBAreaChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7967EB198313E30003A2B0 /* JBAreaChartViewController.m */; };
|
||||
9B7967EF198317540003A2B0 /* icon-area-chart.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B7967ED198317540003A2B0 /* icon-area-chart.png */; };
|
||||
9B7967F0198317540003A2B0 /* icon-area-chart@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B7967EE198317540003A2B0 /* icon-area-chart@2x.png */; };
|
||||
9BD57BBC18D13D1A00ACFA52 /* JBChartTooltipView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BD57BBB18D13D1A00ACFA52 /* JBChartTooltipView.m */; };
|
||||
9BE0B0AD182AD26400232023 /* JBLineChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE0B0AC182AD26400232023 /* JBLineChartView.m */; };
|
||||
9BE0B0C7182B161000232023 /* JBChartHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE0B0C6182B161000232023 /* JBChartHeaderView.m */; };
|
||||
9BE0B0CE182B162E00232023 /* JBBarChartFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE0B0CD182B162E00232023 /* JBBarChartFooterView.m */; };
|
||||
9BEBE9D2183167050046E4A8 /* JBChartInformationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEBE9D1183167050046E4A8 /* JBChartInformationView.m */; };
|
||||
@@ -46,10 +56,35 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
566C73A51C026858000EC785 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
|
||||
94BDFC3219F933B2007492F6 /* JBLineChartMissingPointsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartMissingPointsViewController.h; sourceTree = "<group>"; };
|
||||
94BDFC3319F933B2007492F6 /* JBLineChartMissingPointsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartMissingPointsViewController.m; sourceTree = "<group>"; };
|
||||
9B07251F1829822A0052109B /* JBChartListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartListViewController.h; sourceTree = "<group>"; };
|
||||
9B0725201829822A0052109B /* JBChartListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartListViewController.m; sourceTree = "<group>"; };
|
||||
9B1351701C31FAB3000D4C92 /* NSMutableArray+JBStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+JBStack.h"; sourceTree = "<group>"; };
|
||||
9B1351711C31FAB3000D4C92 /* NSMutableArray+JBStack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+JBStack.m"; sourceTree = "<group>"; };
|
||||
9B1351731C31FAB3000D4C92 /* JBBarChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBBarChartView.h; sourceTree = "<group>"; };
|
||||
9B1351741C31FAB3000D4C92 /* JBBarChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBBarChartView.m; sourceTree = "<group>"; };
|
||||
9B1351761C31FAB3000D4C92 /* JBGradientBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBGradientBarView.h; sourceTree = "<group>"; };
|
||||
9B1351771C31FAB3000D4C92 /* JBGradientBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBGradientBarView.m; sourceTree = "<group>"; };
|
||||
9B1351791C31FAB3000D4C92 /* JBChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartView.h; sourceTree = "<group>"; };
|
||||
9B13517A1C31FAB3000D4C92 /* JBChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartView.m; sourceTree = "<group>"; };
|
||||
9B13517C1C31FAB3000D4C92 /* JBLineChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartView.h; sourceTree = "<group>"; };
|
||||
9B13517D1C31FAB3000D4C92 /* JBLineChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartView.m; sourceTree = "<group>"; };
|
||||
9B13517F1C31FAB3000D4C92 /* JBGradientLineLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBGradientLineLayer.h; sourceTree = "<group>"; };
|
||||
9B1351801C31FAB3000D4C92 /* JBGradientLineLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBGradientLineLayer.m; sourceTree = "<group>"; };
|
||||
9B1351811C31FAB3000D4C92 /* JBShapeLineLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBShapeLineLayer.h; sourceTree = "<group>"; };
|
||||
9B1351821C31FAB3000D4C92 /* JBShapeLineLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBShapeLineLayer.m; sourceTree = "<group>"; };
|
||||
9B1351841C31FAB3000D4C92 /* JBLineChartLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartLine.h; sourceTree = "<group>"; };
|
||||
9B1351851C31FAB3000D4C92 /* JBLineChartLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartLine.m; sourceTree = "<group>"; };
|
||||
9B1351861C31FAB3000D4C92 /* JBLineChartPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartPoint.h; sourceTree = "<group>"; };
|
||||
9B1351871C31FAB3000D4C92 /* JBLineChartPoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartPoint.m; sourceTree = "<group>"; };
|
||||
9B1351891C31FAB3000D4C92 /* JBLineChartDotsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartDotsView.h; sourceTree = "<group>"; };
|
||||
9B13518A1C31FAB3000D4C92 /* JBLineChartDotsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartDotsView.m; sourceTree = "<group>"; };
|
||||
9B13518B1C31FAB3000D4C92 /* JBLineChartDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartDotView.h; sourceTree = "<group>"; };
|
||||
9B13518C1C31FAB3000D4C92 /* JBLineChartDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartDotView.m; sourceTree = "<group>"; };
|
||||
9B13518D1C31FAB3000D4C92 /* JBLineChartLinesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartLinesView.h; sourceTree = "<group>"; };
|
||||
9B13518E1C31FAB3000D4C92 /* JBLineChartLinesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartLinesView.m; sourceTree = "<group>"; };
|
||||
9B2E530118218CF20079B9D2 /* JBChartViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JBChartViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9B2E530418218CF20079B9D2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
9B2E530618218CF20079B9D2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
@@ -84,10 +119,6 @@
|
||||
9B698F13182D7DAE003C135F /* icon-bar-chart@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-bar-chart@2x.png"; sourceTree = "<group>"; };
|
||||
9B698F14182D7DAE003C135F /* icon-line-chart.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-line-chart.png"; sourceTree = "<group>"; };
|
||||
9B698F15182D7DAE003C135F /* icon-line-chart@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-line-chart@2x.png"; sourceTree = "<group>"; };
|
||||
9B6A68D11829AB9F006DB3BF /* JBChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBChartView.h; path = ../../Classes/JBChartView.h; sourceTree = "<group>"; };
|
||||
9B6A68D21829AB9F006DB3BF /* JBChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JBChartView.m; path = ../../Classes/JBChartView.m; sourceTree = "<group>"; };
|
||||
9B6A68D91829ADE1006DB3BF /* JBBarChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBBarChartView.h; path = ../../Classes/JBBarChartView.h; sourceTree = "<group>"; };
|
||||
9B6A68DA1829ADE1006DB3BF /* JBBarChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JBBarChartView.m; path = ../../Classes/JBBarChartView.m; sourceTree = "<group>"; };
|
||||
9B6A68DC1829BE63006DB3BF /* JBBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBBarChartViewController.h; sourceTree = "<group>"; };
|
||||
9B6A68DD1829BE63006DB3BF /* JBBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBBarChartViewController.m; sourceTree = "<group>"; };
|
||||
9B6A68DF1829BED5006DB3BF /* JBLineChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBLineChartViewController.h; sourceTree = "<group>"; };
|
||||
@@ -98,8 +129,6 @@
|
||||
9B7967EE198317540003A2B0 /* icon-area-chart@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-area-chart@2x.png"; sourceTree = "<group>"; };
|
||||
9BD57BBA18D13D1A00ACFA52 /* JBChartTooltipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartTooltipView.h; sourceTree = "<group>"; };
|
||||
9BD57BBB18D13D1A00ACFA52 /* JBChartTooltipView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartTooltipView.m; sourceTree = "<group>"; };
|
||||
9BE0B0AB182AD26400232023 /* JBLineChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBLineChartView.h; path = ../../Classes/JBLineChartView.h; sourceTree = "<group>"; };
|
||||
9BE0B0AC182AD26400232023 /* JBLineChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JBLineChartView.m; path = ../../Classes/JBLineChartView.m; sourceTree = "<group>"; };
|
||||
9BE0B0C5182B161000232023 /* JBChartHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartHeaderView.h; sourceTree = "<group>"; };
|
||||
9BE0B0C6182B161000232023 /* JBChartHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartHeaderView.m; sourceTree = "<group>"; };
|
||||
9BE0B0CC182B162E00232023 /* JBBarChartFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBBarChartFooterView.h; sourceTree = "<group>"; };
|
||||
@@ -127,6 +156,113 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
9B13516F1C31FAB3000D4C92 /* Additions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351701C31FAB3000D4C92 /* NSMutableArray+JBStack.h */,
|
||||
9B1351711C31FAB3000D4C92 /* NSMutableArray+JBStack.m */,
|
||||
);
|
||||
name = Additions;
|
||||
path = ../../Classes/Additions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B1351721C31FAB3000D4C92 /* Bar */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351731C31FAB3000D4C92 /* JBBarChartView.h */,
|
||||
9B1351741C31FAB3000D4C92 /* JBBarChartView.m */,
|
||||
9B1351751C31FAB3000D4C92 /* Views */,
|
||||
);
|
||||
name = Bar;
|
||||
path = ../../Classes/Bar;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B1351751C31FAB3000D4C92 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351761C31FAB3000D4C92 /* JBGradientBarView.h */,
|
||||
9B1351771C31FAB3000D4C92 /* JBGradientBarView.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B1351781C31FAB3000D4C92 /* Base */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351791C31FAB3000D4C92 /* JBChartView.h */,
|
||||
9B13517A1C31FAB3000D4C92 /* JBChartView.m */,
|
||||
);
|
||||
name = Base;
|
||||
path = ../../Classes/Base;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B13517B1C31FAB3000D4C92 /* Line */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B13517C1C31FAB3000D4C92 /* JBLineChartView.h */,
|
||||
9B13517D1C31FAB3000D4C92 /* JBLineChartView.m */,
|
||||
9B13517E1C31FAB3000D4C92 /* Layers */,
|
||||
9B1351831C31FAB3000D4C92 /* Models */,
|
||||
9B1351881C31FAB3000D4C92 /* Views */,
|
||||
);
|
||||
name = Line;
|
||||
path = ../../Classes/Line;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B13517E1C31FAB3000D4C92 /* Layers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B13517F1C31FAB3000D4C92 /* JBGradientLineLayer.h */,
|
||||
9B1351801C31FAB3000D4C92 /* JBGradientLineLayer.m */,
|
||||
9B1351811C31FAB3000D4C92 /* JBShapeLineLayer.h */,
|
||||
9B1351821C31FAB3000D4C92 /* JBShapeLineLayer.m */,
|
||||
);
|
||||
path = Layers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B1351831C31FAB3000D4C92 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351841C31FAB3000D4C92 /* JBLineChartLine.h */,
|
||||
9B1351851C31FAB3000D4C92 /* JBLineChartLine.m */,
|
||||
9B1351861C31FAB3000D4C92 /* JBLineChartPoint.h */,
|
||||
9B1351871C31FAB3000D4C92 /* JBLineChartPoint.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B1351881C31FAB3000D4C92 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B1351891C31FAB3000D4C92 /* JBLineChartDotsView.h */,
|
||||
9B13518A1C31FAB3000D4C92 /* JBLineChartDotsView.m */,
|
||||
9B13518B1C31FAB3000D4C92 /* JBLineChartDotView.h */,
|
||||
9B13518C1C31FAB3000D4C92 /* JBLineChartDotView.m */,
|
||||
9B13518D1C31FAB3000D4C92 /* JBLineChartLinesView.h */,
|
||||
9B13518E1C31FAB3000D4C92 /* JBLineChartLinesView.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B13519B1C31FABE000D4C92 /* JBChartView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B13516F1C31FAB3000D4C92 /* Additions */,
|
||||
9B1351721C31FAB3000D4C92 /* Bar */,
|
||||
9B1351781C31FAB3000D4C92 /* Base */,
|
||||
9B13517B1C31FAB3000D4C92 /* Line */,
|
||||
);
|
||||
name = JBChartView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B13519C1C31FAC3000D4C92 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B13519B1C31FABE000D4C92 /* JBChartView */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9B2E52F818218CF20079B9D2 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -161,6 +297,7 @@
|
||||
9BE0B0D0182B16DD00232023 /* Constants */,
|
||||
9B2E534018218D560079B9D2 /* Controllers */,
|
||||
9B2E533C18218D310079B9D2 /* Delegate */,
|
||||
9B13519C1C31FAC3000D4C92 /* Libraries */,
|
||||
9B603D43182C7002000A76D0 /* Resources */,
|
||||
9B2E530B18218CF20079B9D2 /* Supporting Files */,
|
||||
9B6A68D01829AB85006DB3BF /* Views */,
|
||||
@@ -233,6 +370,7 @@
|
||||
9B603D44182C7002000A76D0 /* Images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
566C73A51C026858000EC785 /* Default-568h@2x.png */,
|
||||
9B7967ED198317540003A2B0 /* icon-area-chart.png */,
|
||||
9B7967EE198317540003A2B0 /* icon-area-chart@2x.png */,
|
||||
9B698F07182D720E003C135F /* icon-arrow.png */,
|
||||
@@ -260,7 +398,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B698F0E182D7BCF003C135F /* Cells */,
|
||||
9BE0B0C0182B15DB00232023 /* Charts */,
|
||||
9BE0B0CB182B162E00232023 /* Footers */,
|
||||
9BE0B0C4182B161000232023 /* Headers */,
|
||||
9BEBE9CF183167050046E4A8 /* Misc */,
|
||||
@@ -268,19 +405,6 @@
|
||||
name = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9BE0B0C0182B15DB00232023 /* Charts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9B6A68D91829ADE1006DB3BF /* JBBarChartView.h */,
|
||||
9B6A68DA1829ADE1006DB3BF /* JBBarChartView.m */,
|
||||
9B6A68D11829AB9F006DB3BF /* JBChartView.h */,
|
||||
9B6A68D21829AB9F006DB3BF /* JBChartView.m */,
|
||||
9BE0B0AB182AD26400232023 /* JBLineChartView.h */,
|
||||
9BE0B0AC182AD26400232023 /* JBLineChartView.m */,
|
||||
);
|
||||
name = Charts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9BE0B0C4182B161000232023 /* Headers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -352,7 +476,7 @@
|
||||
9B2E52F918218CF20079B9D2 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0710;
|
||||
ORGANIZATIONNAME = Jawbone;
|
||||
};
|
||||
buildConfigurationList = 9B2E52FC18218CF20079B9D2 /* Build configuration list for PBXProject "JBChartViewDemo" */;
|
||||
@@ -387,6 +511,7 @@
|
||||
9B7967EF198317540003A2B0 /* icon-area-chart.png in Resources */,
|
||||
9B603D47182C7002000A76D0 /* icon-jawbone-logo.png in Resources */,
|
||||
9B603D48182C7002000A76D0 /* icon-jawbone-logo@2x.png in Resources */,
|
||||
566C73A61C026858000EC785 /* Default-568h@2x.png in Resources */,
|
||||
9B7967F0198317540003A2B0 /* icon-area-chart@2x.png in Resources */,
|
||||
9B698F19182D7DAE003C135F /* icon-line-chart@2x.png in Resources */,
|
||||
9B698F0A182D720E003C135F /* icon-arrow@2x.png in Resources */,
|
||||
@@ -401,24 +526,33 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9BE0B0CE182B162E00232023 /* JBBarChartFooterView.m in Sources */,
|
||||
9B6A68D31829AB9F006DB3BF /* JBChartView.m in Sources */,
|
||||
9B13519A1C31FAB3000D4C92 /* JBLineChartLinesView.m in Sources */,
|
||||
9B1351901C31FAB3000D4C92 /* JBBarChartView.m in Sources */,
|
||||
9BEBE9D2183167050046E4A8 /* JBChartInformationView.m in Sources */,
|
||||
9B603D3E182C6E79000A76D0 /* JBBaseNavigationController.m in Sources */,
|
||||
9B1351971C31FAB3000D4C92 /* JBLineChartPoint.m in Sources */,
|
||||
9B698F11182D7BCF003C135F /* JBChartTableCell.m in Sources */,
|
||||
9B6A68DB1829ADE1006DB3BF /* JBBarChartView.m in Sources */,
|
||||
9B2E531118218CF20079B9D2 /* main.m in Sources */,
|
||||
9B1351981C31FAB3000D4C92 /* JBLineChartDotsView.m in Sources */,
|
||||
9B698F01182D5F44003C135F /* JBLineChartFooterView.m in Sources */,
|
||||
9BE0B0C7182B161000232023 /* JBChartHeaderView.m in Sources */,
|
||||
9B603D4E182C7163000A76D0 /* JBBaseTableViewController.m in Sources */,
|
||||
9B6A68DE1829BE63006DB3BF /* JBBarChartViewController.m in Sources */,
|
||||
9B4437AD18D7686800682EF0 /* JBChartTooltipTipView.m in Sources */,
|
||||
94BDFC3419F933B2007492F6 /* JBLineChartMissingPointsViewController.m in Sources */,
|
||||
9BE0B0AD182AD26400232023 /* JBLineChartView.m in Sources */,
|
||||
9BEE694618D2789E005D9BA7 /* JBBaseChartViewController.m in Sources */,
|
||||
9B6A68E11829BED5006DB3BF /* JBLineChartViewController.m in Sources */,
|
||||
9B1351961C31FAB3000D4C92 /* JBLineChartLine.m in Sources */,
|
||||
9B1351911C31FAB3000D4C92 /* JBGradientBarView.m in Sources */,
|
||||
9B1351931C31FAB3000D4C92 /* JBLineChartView.m in Sources */,
|
||||
9B1351991C31FAB3000D4C92 /* JBLineChartDotView.m in Sources */,
|
||||
9B1351921C31FAB3000D4C92 /* JBChartView.m in Sources */,
|
||||
9B2E533F18218D310079B9D2 /* AppDelegate.m in Sources */,
|
||||
9B13518F1C31FAB3000D4C92 /* NSMutableArray+JBStack.m in Sources */,
|
||||
9B1351941C31FAB3000D4C92 /* JBGradientLineLayer.m in Sources */,
|
||||
9B7967EC198313E30003A2B0 /* JBAreaChartViewController.m in Sources */,
|
||||
9BD57BBC18D13D1A00ACFA52 /* JBChartTooltipView.m in Sources */,
|
||||
9B1351951C31FAB3000D4C92 /* JBShapeLineLayer.m in Sources */,
|
||||
9B603D4B182C7117000A76D0 /* JBBaseViewController.m in Sources */,
|
||||
9B0725211829822A0052109B /* JBChartListViewController.m in Sources */,
|
||||
);
|
||||
@@ -456,6 +590,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
@@ -519,6 +654,7 @@
|
||||
GCC_PREFIX_HEADER = "JBChartViewDemo/JBChartViewDemo-Prefix.pch";
|
||||
INFOPLIST_FILE = "JBChartViewDemo/JBChartViewDemo-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jawbone.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
@@ -533,6 +669,7 @@
|
||||
GCC_PREFIX_HEADER = "JBChartViewDemo/JBChartViewDemo-Prefix.pch";
|
||||
INFOPLIST_FILE = "JBChartViewDemo/JBChartViewDemo-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jawbone.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
|
||||
@@ -29,8 +29,12 @@
|
||||
#define kJBColorLineChartHeaderSeparatorColor UIColorFromHex(0x8eb6b7)
|
||||
#define kJBColorLineChartDefaultSolidLineColor [UIColor colorWithWhite:1.0 alpha:0.5]
|
||||
#define kJBColorLineChartDefaultSolidSelectedLineColor [UIColor colorWithWhite:1.0 alpha:1.0]
|
||||
#define kJBColorLineChartDefaultDashedLineColor [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]
|
||||
#define kJBColorLineChartDefaultDashedLineColor [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0]
|
||||
#define kJBColorLineChartDefaultDashedSelectedLineColor [UIColor colorWithWhite:1.0 alpha:1.0]
|
||||
#define kJBColorLineChartDefaultSolidFillColor [UIColor clearColor]
|
||||
#define kJBColorLineChartDefaultDashedFillColor [UIColor colorWithWhite:1.0 alpha:0.3]
|
||||
#define kJBColorLineChartDefaultGradientStartColor UIColorFromHex(0x0000FF)
|
||||
#define kJBColorLineChartDefaultGradientEndColor UIColorFromHex(0x00FF00)
|
||||
|
||||
#define mark - Area Chart
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#import "JBChartTooltipTipView.h"
|
||||
|
||||
// Numerics
|
||||
CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.25f;
|
||||
CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.1f;
|
||||
|
||||
@interface JBBaseChartViewController ()
|
||||
|
||||
@@ -55,10 +55,11 @@ CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.25f;
|
||||
[self.view bringSubviewToFront:self.tooltipTipView];
|
||||
|
||||
dispatch_block_t adjustTooltipPosition = ^{
|
||||
|
||||
CGPoint originalTouchPoint = [self.view convertPoint:touchPoint fromView:chartView];
|
||||
CGPoint convertedTouchPoint = originalTouchPoint; // modified
|
||||
JBChartView *chartView = [self chartView];
|
||||
if (chartView)
|
||||
if (chartView && !CGPointEqualToPoint(touchPoint, CGPointZero))
|
||||
{
|
||||
CGFloat minChartX = (chartView.frame.origin.x + ceil(self.tooltipView.frame.size.width * 0.5));
|
||||
if (convertedTouchPoint.x < minChartX)
|
||||
@@ -70,6 +71,7 @@ CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.25f;
|
||||
{
|
||||
convertedTouchPoint.x = maxChartX;
|
||||
}
|
||||
|
||||
self.tooltipView.frame = CGRectMake(convertedTouchPoint.x - ceil(self.tooltipView.frame.size.width * 0.5), CGRectGetMaxY(chartView.headerView.frame), self.tooltipView.frame.size.width, self.tooltipView.frame.size.height);
|
||||
|
||||
CGFloat minTipX = (chartView.frame.origin.x + self.tooltipTipView.frame.size.width);
|
||||
@@ -83,33 +85,34 @@ CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.25f;
|
||||
originalTouchPoint.x = maxTipX;
|
||||
}
|
||||
self.tooltipTipView.frame = CGRectMake(originalTouchPoint.x - ceil(self.tooltipTipView.frame.size.width * 0.5), CGRectGetMaxY(self.tooltipView.frame), self.tooltipTipView.frame.size.width, self.tooltipTipView.frame.size.height);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_block_t adjustTooltipVisibility = ^{
|
||||
self.tooltipView.alpha = _tooltipVisible ? 1.0 : 0.0;
|
||||
self.tooltipTipView.alpha = _tooltipVisible ? 1.0 : 0.0;
|
||||
};
|
||||
|
||||
if (tooltipVisible)
|
||||
{
|
||||
adjustTooltipPosition();
|
||||
}
|
||||
|
||||
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:kJBBaseChartViewControllerAnimationDuration animations:^{
|
||||
adjustTooltipVisibility();
|
||||
} completion:^(BOOL finished) {
|
||||
if (!tooltipVisible)
|
||||
{
|
||||
adjustTooltipPosition();
|
||||
}
|
||||
}];
|
||||
if (tooltipVisible)
|
||||
{
|
||||
adjustTooltipPosition();
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:kJBBaseChartViewControllerAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
adjustTooltipVisibility();
|
||||
} completion:^(BOOL finished) {
|
||||
if (!tooltipVisible)
|
||||
{
|
||||
adjustTooltipPosition();
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustTooltipVisibility();
|
||||
adjustTooltipPosition();
|
||||
adjustTooltipVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
||||
{
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ CGFloat const kJBAreaChartViewControllerChartHeaderHeight = 75.0f;
|
||||
CGFloat const kJBAreaChartViewControllerChartHeaderPadding = 20.0f;
|
||||
CGFloat const kJBAreaChartViewControllerChartFooterHeight = 20.0f;
|
||||
CGFloat const kJBAreaChartViewControllerChartLineWidth = 2.0f;
|
||||
CGFloat const kJBAreaChartViewControllerChartSunLineDimmedOpacity = 1.0f;
|
||||
CGFloat const kJBAreaChartViewControllerChartMoonLineDimmedOpacity = 0.0f;
|
||||
NSInteger const kJBAreaChartViewControllerMaxNumChartPoints = 12;
|
||||
|
||||
// Strings
|
||||
@@ -198,6 +200,18 @@ NSString * const kJBAreaChartViewControllerNavButtonViewKey = @"view";
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dimmedSelectionOpacityAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
if (lineIndex == JBLineChartLineMoon)
|
||||
{
|
||||
return kJBAreaChartViewControllerChartMoonLineDimmedOpacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
return kJBAreaChartViewControllerChartSunLineDimmedOpacity;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - JBLineChartViewDelegate
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
|
||||
|
||||
@@ -160,7 +160,7 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
|
||||
|
||||
- (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView
|
||||
{
|
||||
return kJBBarChartViewControllerNumBars;
|
||||
return [self.chartData count];
|
||||
}
|
||||
|
||||
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint
|
||||
@@ -205,15 +205,18 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
|
||||
|
||||
- (void)chartToggleButtonPressed:(id)sender
|
||||
{
|
||||
UIView *buttonImageView = [self.navigationItem.rightBarButtonItem valueForKey:kJBBarChartViewControllerNavButtonViewKey];
|
||||
buttonImageView.userInteractionEnabled = NO;
|
||||
|
||||
CGAffineTransform transform = self.barChartView.state == JBChartViewStateExpanded ? CGAffineTransformMakeRotation(M_PI) : CGAffineTransformMakeRotation(0);
|
||||
buttonImageView.transform = transform;
|
||||
|
||||
[self.barChartView setState:self.barChartView.state == JBChartViewStateExpanded ? JBChartViewStateCollapsed : JBChartViewStateExpanded animated:YES callback:^{
|
||||
buttonImageView.userInteractionEnabled = YES;
|
||||
}];
|
||||
int randomNumber = (-3) + rand() % (3-(-3));
|
||||
|
||||
NSMutableArray *mutableChartData = [NSMutableArray array];
|
||||
for (int i=0; i<(kJBBarChartViewControllerNumBars + randomNumber ); i++)
|
||||
{
|
||||
NSInteger delta = ((kJBBarChartViewControllerNumBars + randomNumber) - labs(((kJBBarChartViewControllerNumBars + randomNumber) - i) - i)) + 2;
|
||||
[mutableChartData addObject:[NSNumber numberWithFloat:MAX((delta * kJBBarChartViewControllerMinBarHeight), arc4random() % (delta * kJBBarChartViewControllerMaxBarHeight))]];
|
||||
|
||||
}
|
||||
self.chartData = [NSArray arrayWithArray:mutableChartData];
|
||||
|
||||
[self.barChartView reloadDataAnimated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
typedef NS_ENUM(NSInteger, JBLineChartLine){
|
||||
JBLineChartLineSolid,
|
||||
JBLineChartLineDashed,
|
||||
JBLineChartLineCount
|
||||
JBLineChartLineDashed,
|
||||
JBLineChartLineCount
|
||||
};
|
||||
|
||||
// Numerics
|
||||
@@ -58,221 +58,247 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self)
|
||||
{
|
||||
[self initFakeData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_lineChartView.delegate = nil;
|
||||
_lineChartView.dataSource = nil;
|
||||
_lineChartView.delegate = nil;
|
||||
_lineChartView.dataSource = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
- (void)initFakeData
|
||||
{
|
||||
NSMutableArray *mutableLineCharts = [NSMutableArray array];
|
||||
for (int lineIndex=0; lineIndex<JBLineChartLineCount; lineIndex++)
|
||||
{
|
||||
NSMutableArray *mutableChartData = [NSMutableArray array];
|
||||
for (int i=0; i<kJBLineChartViewControllerMaxNumChartPoints; i++)
|
||||
{
|
||||
[mutableChartData addObject:[NSNumber numberWithFloat:((double)arc4random() / ARC4RANDOM_MAX)]]; // random number between 0 and 1
|
||||
}
|
||||
[mutableLineCharts addObject:mutableChartData];
|
||||
}
|
||||
_chartData = [NSArray arrayWithArray:mutableLineCharts];
|
||||
_daysOfWeek = [[[NSDateFormatter alloc] init] shortWeekdaySymbols];
|
||||
NSMutableArray *mutableLineCharts = [NSMutableArray array];
|
||||
for (int lineIndex=0; lineIndex<JBLineChartLineCount; lineIndex++)
|
||||
{
|
||||
NSMutableArray *mutableChartData = [NSMutableArray array];
|
||||
for (int i=0; i<kJBLineChartViewControllerMaxNumChartPoints; i++)
|
||||
{
|
||||
[mutableChartData addObject:[NSNumber numberWithFloat:((double)arc4random() / ARC4RANDOM_MAX)]]; // random number between 0 and 1
|
||||
}
|
||||
[mutableLineCharts addObject:mutableChartData];
|
||||
}
|
||||
_chartData = [NSArray arrayWithArray:mutableLineCharts];
|
||||
_daysOfWeek = [[[NSDateFormatter alloc] init] shortWeekdaySymbols];
|
||||
}
|
||||
|
||||
- (NSArray *)largestLineData
|
||||
{
|
||||
NSArray *largestLineData = nil;
|
||||
for (NSArray *lineData in self.chartData)
|
||||
{
|
||||
if ([lineData count] > [largestLineData count])
|
||||
{
|
||||
largestLineData = lineData;
|
||||
}
|
||||
}
|
||||
return largestLineData;
|
||||
NSArray *largestLineData = nil;
|
||||
for (NSArray *lineData in self.chartData)
|
||||
{
|
||||
if ([lineData count] > [largestLineData count])
|
||||
{
|
||||
largestLineData = lineData;
|
||||
}
|
||||
}
|
||||
return largestLineData;
|
||||
}
|
||||
|
||||
#pragma mark - View Lifecycle
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
self.view.backgroundColor = kJBColorLineChartControllerBackground;
|
||||
self.navigationItem.rightBarButtonItem = [self chartToggleButtonWithTarget:self action:@selector(chartToggleButtonPressed:)];
|
||||
|
||||
self.lineChartView = [[JBLineChartView alloc] init];
|
||||
self.lineChartView.frame = CGRectMake(kJBLineChartViewControllerChartPadding, kJBLineChartViewControllerChartPadding, self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartHeight);
|
||||
self.lineChartView.delegate = self;
|
||||
self.lineChartView.dataSource = self;
|
||||
self.lineChartView.headerPadding = kJBLineChartViewControllerChartHeaderPadding;
|
||||
self.lineChartView.backgroundColor = kJBColorLineChartBackground;
|
||||
|
||||
JBChartHeaderView *headerView = [[JBChartHeaderView alloc] initWithFrame:CGRectMake(kJBLineChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartHeaderHeight * 0.5), self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartHeaderHeight)];
|
||||
headerView.titleLabel.text = [kJBStringLabelAverageDailyRainfall uppercaseString];
|
||||
headerView.titleLabel.textColor = kJBColorLineChartHeader;
|
||||
headerView.titleLabel.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.25];
|
||||
headerView.titleLabel.shadowOffset = CGSizeMake(0, 1);
|
||||
headerView.subtitleLabel.text = kJBStringLabel2013;
|
||||
headerView.subtitleLabel.textColor = kJBColorLineChartHeader;
|
||||
headerView.subtitleLabel.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.25];
|
||||
headerView.subtitleLabel.shadowOffset = CGSizeMake(0, 1);
|
||||
headerView.separatorColor = kJBColorLineChartHeaderSeparatorColor;
|
||||
self.lineChartView.headerView = headerView;
|
||||
|
||||
JBLineChartFooterView *footerView = [[JBLineChartFooterView alloc] initWithFrame:CGRectMake(kJBLineChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartFooterHeight * 0.5), self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartFooterHeight)];
|
||||
footerView.backgroundColor = [UIColor clearColor];
|
||||
footerView.leftLabel.text = [[self.daysOfWeek firstObject] uppercaseString];
|
||||
footerView.leftLabel.textColor = [UIColor whiteColor];
|
||||
footerView.rightLabel.text = [[self.daysOfWeek lastObject] uppercaseString];;
|
||||
footerView.rightLabel.textColor = [UIColor whiteColor];
|
||||
footerView.sectionCount = [[self largestLineData] count];
|
||||
self.lineChartView.footerView = footerView;
|
||||
|
||||
[self.view addSubview:self.lineChartView];
|
||||
|
||||
self.informationView = [[JBChartInformationView alloc] initWithFrame:CGRectMake(self.view.bounds.origin.x, CGRectGetMaxY(self.lineChartView.frame), self.view.bounds.size.width, self.view.bounds.size.height - CGRectGetMaxY(self.lineChartView.frame) - CGRectGetMaxY(self.navigationController.navigationBar.frame))];
|
||||
[self.informationView setValueAndUnitTextColor:[UIColor colorWithWhite:1.0 alpha:0.75]];
|
||||
[self.informationView setTitleTextColor:kJBColorLineChartHeader];
|
||||
[self.informationView setTextShadowColor:nil];
|
||||
[self.informationView setSeparatorColor:kJBColorLineChartHeaderSeparatorColor];
|
||||
[self.view addSubview:self.informationView];
|
||||
|
||||
[self.lineChartView reloadData];
|
||||
[super loadView];
|
||||
|
||||
self.view.backgroundColor = kJBColorLineChartControllerBackground;
|
||||
self.navigationItem.rightBarButtonItem = [self chartToggleButtonWithTarget:self action:@selector(chartToggleButtonPressed:)];
|
||||
|
||||
self.lineChartView = [[JBLineChartView alloc] init];
|
||||
self.lineChartView.frame = CGRectMake(kJBLineChartViewControllerChartPadding, kJBLineChartViewControllerChartPadding, self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartHeight);
|
||||
self.lineChartView.delegate = self;
|
||||
self.lineChartView.dataSource = self;
|
||||
self.lineChartView.headerPadding = kJBLineChartViewControllerChartHeaderPadding;
|
||||
self.lineChartView.backgroundColor = kJBColorLineChartBackground;
|
||||
|
||||
JBChartHeaderView *headerView = [[JBChartHeaderView alloc] initWithFrame:CGRectMake(kJBLineChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartHeaderHeight * 0.5), self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartHeaderHeight)];
|
||||
headerView.titleLabel.text = [kJBStringLabelAverageDailyRainfall uppercaseString];
|
||||
headerView.titleLabel.textColor = kJBColorLineChartHeader;
|
||||
headerView.titleLabel.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.25];
|
||||
headerView.titleLabel.shadowOffset = CGSizeMake(0, 1);
|
||||
headerView.subtitleLabel.text = kJBStringLabel2013;
|
||||
headerView.subtitleLabel.textColor = kJBColorLineChartHeader;
|
||||
headerView.subtitleLabel.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.25];
|
||||
headerView.subtitleLabel.shadowOffset = CGSizeMake(0, 1);
|
||||
headerView.separatorColor = kJBColorLineChartHeaderSeparatorColor;
|
||||
self.lineChartView.headerView = headerView;
|
||||
|
||||
JBLineChartFooterView *footerView = [[JBLineChartFooterView alloc] initWithFrame:CGRectMake(kJBLineChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartFooterHeight * 0.5), self.view.bounds.size.width - (kJBLineChartViewControllerChartPadding * 2), kJBLineChartViewControllerChartFooterHeight)];
|
||||
footerView.backgroundColor = [UIColor clearColor];
|
||||
footerView.leftLabel.text = [[self.daysOfWeek firstObject] uppercaseString];
|
||||
footerView.leftLabel.textColor = [UIColor whiteColor];
|
||||
footerView.rightLabel.text = [[self.daysOfWeek lastObject] uppercaseString];;
|
||||
footerView.rightLabel.textColor = [UIColor whiteColor];
|
||||
footerView.sectionCount = [[self largestLineData] count];
|
||||
self.lineChartView.footerView = footerView;
|
||||
|
||||
[self.view addSubview:self.lineChartView];
|
||||
|
||||
self.informationView = [[JBChartInformationView alloc] initWithFrame:CGRectMake(self.view.bounds.origin.x, CGRectGetMaxY(self.lineChartView.frame), self.view.bounds.size.width, self.view.bounds.size.height - CGRectGetMaxY(self.lineChartView.frame) - CGRectGetMaxY(self.navigationController.navigationBar.frame))];
|
||||
[self.informationView setValueAndUnitTextColor:[UIColor colorWithWhite:1.0 alpha:0.75]];
|
||||
[self.informationView setTitleTextColor:kJBColorLineChartHeader];
|
||||
[self.informationView setTextShadowColor:nil];
|
||||
[self.informationView setSeparatorColor:kJBColorLineChartHeaderSeparatorColor];
|
||||
[self.view addSubview:self.informationView];
|
||||
|
||||
[self.lineChartView reloadData];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
[self.lineChartView setState:JBChartViewStateExpanded];
|
||||
[super viewWillAppear:animated];
|
||||
[self.lineChartView setState:JBChartViewStateExpanded];
|
||||
}
|
||||
|
||||
#pragma mark - JBChartViewDataSource
|
||||
|
||||
- (BOOL)shouldExtendSelectionViewIntoHeaderPaddingForChartView:(JBChartView *)chartView
|
||||
{
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldExtendSelectionViewIntoFooterPaddingForChartView:(JBChartView *)chartView
|
||||
{
|
||||
return NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - JBLineChartViewDataSource
|
||||
|
||||
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
return [self.chartData count];
|
||||
return [self.chartData count];
|
||||
}
|
||||
|
||||
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return [[self.chartData objectAtIndex:lineIndex] count];
|
||||
return [[self.chartData objectAtIndex:lineIndex] count];
|
||||
}
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return lineIndex == JBLineChartViewLineStyleDashed;
|
||||
return lineIndex == JBLineChartViewLineStyleDashed;
|
||||
}
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView smoothLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return lineIndex == JBLineChartViewLineStyleSolid;
|
||||
return lineIndex == JBLineChartViewLineStyleSolid;
|
||||
}
|
||||
|
||||
#pragma mark - JBLineChartViewDelegate
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return [[[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex] floatValue];
|
||||
return [[[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex] floatValue];
|
||||
}
|
||||
|
||||
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint
|
||||
{
|
||||
NSNumber *valueNumber = [[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex];
|
||||
[self.informationView setValueText:[NSString stringWithFormat:@"%.2f", [valueNumber floatValue]] unitText:kJBStringLabelMm];
|
||||
[self.informationView setTitleText:lineIndex == JBLineChartLineSolid ? kJBStringLabelMetropolitanAverage : kJBStringLabelNationalAverage];
|
||||
[self.informationView setHidden:NO animated:YES];
|
||||
[self setTooltipVisible:YES animated:YES atTouchPoint:touchPoint];
|
||||
[self.tooltipView setText:[[self.daysOfWeek objectAtIndex:horizontalIndex] uppercaseString]];
|
||||
NSNumber *valueNumber = [[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex];
|
||||
[self.informationView setValueText:[NSString stringWithFormat:@"%.2f", [valueNumber floatValue]] unitText:kJBStringLabelMm];
|
||||
[self.informationView setTitleText:lineIndex == JBLineChartLineSolid ? kJBStringLabelMetropolitanAverage : kJBStringLabelNationalAverage];
|
||||
[self.informationView setHidden:NO animated:YES];
|
||||
[self setTooltipVisible:YES animated:YES atTouchPoint:touchPoint];
|
||||
[self.tooltipView setText:[[self.daysOfWeek objectAtIndex:horizontalIndex] uppercaseString]];
|
||||
}
|
||||
|
||||
- (void)didDeselectLineInLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
[self.informationView setHidden:YES animated:YES];
|
||||
[self setTooltipVisible:NO animated:YES];
|
||||
[self.informationView setHidden:YES animated:YES];
|
||||
[self setTooltipVisible:NO animated:YES];
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidLineColor: kJBColorLineChartDefaultDashedLineColor;
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidLineColor: nil;
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView fillColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidFillColor : nil;
|
||||
}
|
||||
|
||||
- (CAGradientLayer *)lineChartView:(JBLineChartView *)lineChartView gradientForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
if (lineIndex == JBLineChartLineSolid)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
CAGradientLayer *gradient = [CAGradientLayer new];
|
||||
gradient.startPoint = CGPointMake(0.0, 0.0);
|
||||
gradient.endPoint = CGPointMake(1.0, 0.0);
|
||||
gradient.colors = @[(id)kJBColorLineChartDefaultGradientStartColor.CGColor, (id)kJBColorLineChartDefaultGradientEndColor.CGColor];
|
||||
return gradient;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidLineColor: kJBColorLineChartDefaultDashedLineColor;
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidLineColor: kJBColorLineChartDefaultDashedLineColor;
|
||||
}
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView widthForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBLineChartViewControllerChartSolidLineWidth: kJBLineChartViewControllerChartDashedLineWidth;
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBLineChartViewControllerChartSolidLineWidth: kJBLineChartViewControllerChartDashedLineWidth;
|
||||
}
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dotRadiusForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? 0.0: kJBLineChartViewControllerChartSolidLineDotRadius;
|
||||
return (lineIndex == JBLineChartLineSolid) ? 0.0: kJBLineChartViewControllerChartSolidLineDotRadius;
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView verticalSelectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return [UIColor whiteColor];
|
||||
return [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidSelectedLineColor: kJBColorLineChartDefaultDashedSelectedLineColor;
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidSelectedLineColor: nil;
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidSelectedLineColor: kJBColorLineChartDefaultDashedSelectedLineColor;
|
||||
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidSelectedLineColor: kJBColorLineChartDefaultDashedSelectedLineColor;
|
||||
}
|
||||
|
||||
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? JBLineChartViewLineStyleSolid : JBLineChartViewLineStyleDashed;
|
||||
return (lineIndex == JBLineChartLineSolid) ? JBLineChartViewLineStyleSolid : JBLineChartViewLineStyleDashed;
|
||||
}
|
||||
|
||||
- (JBLineChartViewColorStyle)lineChartView:(JBLineChartView *)lineChartView colorStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return (lineIndex == JBLineChartLineSolid) ? JBLineChartViewColorStyleSolid : JBLineChartViewColorStyleGradient;
|
||||
}
|
||||
|
||||
#pragma mark - Buttons
|
||||
@@ -280,21 +306,21 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
|
||||
- (void)chartToggleButtonPressed:(id)sender
|
||||
{
|
||||
UIView *buttonImageView = [self.navigationItem.rightBarButtonItem valueForKey:kJBLineChartViewControllerNavButtonViewKey];
|
||||
buttonImageView.userInteractionEnabled = NO;
|
||||
|
||||
CGAffineTransform transform = self.lineChartView.state == JBChartViewStateExpanded ? CGAffineTransformMakeRotation(M_PI) : CGAffineTransformMakeRotation(0);
|
||||
buttonImageView.transform = transform;
|
||||
|
||||
[self.lineChartView setState:self.lineChartView.state == JBChartViewStateExpanded ? JBChartViewStateCollapsed : JBChartViewStateExpanded animated:YES callback:^{
|
||||
buttonImageView.userInteractionEnabled = YES;
|
||||
}];
|
||||
buttonImageView.userInteractionEnabled = NO;
|
||||
|
||||
CGAffineTransform transform = self.lineChartView.state == JBChartViewStateExpanded ? CGAffineTransformMakeRotation(M_PI) : CGAffineTransformMakeRotation(0);
|
||||
buttonImageView.transform = transform;
|
||||
|
||||
[self.lineChartView setState:self.lineChartView.state == JBChartViewStateExpanded ? JBChartViewStateCollapsed : JBChartViewStateExpanded animated:YES callback:^{
|
||||
buttonImageView.userInteractionEnabled = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (JBChartView *)chartView
|
||||
{
|
||||
return self.lineChartView;
|
||||
return self.lineChartView;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -5,16 +5,31 @@
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.jawbone.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
// Numerics
|
||||
CGFloat static const kJBChartTooltipViewCornerRadius = 5.0;
|
||||
CGFloat const kJBChartTooltipViewCornerRadius = 5.0;
|
||||
CGFloat const kJBChartTooltipViewDefaultWidth = 50.0f;
|
||||
CGFloat const kJBChartTooltipViewDefaultHeight = 25.0f;
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -43,7 +43,7 @@ Simply add the following line to your <code>Podfile</code>:
|
||||
|
||||
### The Old School Way
|
||||
|
||||
The simpliest way to use JBChartView with your application is to drag and drop the <i>/Classes</i> folder into you're Xcode 5 project. It's also recommended you rename the <i>/Classes</i> folder to something more descriptive (ie. "<i>Jawbone - JBChartView</i>").
|
||||
The simplest way to use JBChartView with your application is to drag and drop the <i>/Classes</i> folder into you're Xcode 5 project. It's also recommended you rename the <i>/Classes</i> folder to something more descriptive (ie. "<i>Jawbone - JBChartView</i>").
|
||||
|
||||
<center>
|
||||
<img src="https://raw.github.com/Jawbone/JBChartView/master/Screenshots/installation.png">
|
||||
@@ -100,9 +100,24 @@ Lastly, ensure you have set the *frame* of your barChartView & call *reloadData*
|
||||
|
||||
barChartView.frame = CGRectMake( ... );
|
||||
[barChartView reloadData];
|
||||
|
||||
Subsequent changes to the chart's frame will not invoke *reloadData*; it must be called directly afterwards for any changes to take effect.
|
||||
|
||||
**Note**: subsequent changes to the chart's frame will not invoke *reloadData*; it must be called directly afterwards for any changes to take effect.
|
||||
|
||||
|
||||
### Animated Reload
|
||||
|
||||
Both line and bar charts support *animated* reloads. The delta between the old data model and new data model is calculated and animated appropriately (ie. bars or lines will shrink, expand or morph in size). Due to techinical limitations in Apple's <a href="https://developer.apple.com/library/tvos/documentation/GraphicsImaging/Reference/CAShapeLayer_class/index.html#//apple_ref/occ/instp/CAShapeLayer/path">Quartz Core Framework</a>, line *fills* (both solid and gradient) can not be animated - they will simply 'snap' into place while the rest of the chart continues to animate.
|
||||
|
||||
- (void)reloadDataAnimated:(BOOL)animated;
|
||||
|
||||
State changes during a reload will be ignored. As well, subsequent calls to reloadData: or reloadDataAnimated: before any previous reloads are complete, will also be ignored. Lastly, all touch events will be ignored until a reload has completed. You can always check on the state of the animation via the exposed *read-only* property:
|
||||
|
||||
@property (nonatomic, readonly) BOOL reloading;
|
||||
|
||||
By default, the animation will complete in approximately 0.25 seconds. The animation duration is independent from the data model size.
|
||||
|
||||
**Note**: the above restrictions apply only to *animated* reloads, as non-animated reloads are synchronous.
|
||||
|
||||
#### JBLineChartView
|
||||
|
||||
Similiarily, to initialize a JBLineChartView, you only need a few lines of code (see below). Line charts can also be initialized via a <b>nib</b> or with a <b>frame</b>.
|
||||
@@ -140,7 +155,7 @@ Secondly, you need to inform the delegate of the y-position of each point (autom
|
||||
return ...; // y-position (y-axis) of point at horizontalIndex (x-axis)
|
||||
}
|
||||
|
||||
**Note**: You can return NAN instead of CGFloat to indicate missing values. The chart's line will begin at the first non-NAN value and end at the last non-NAN value. The line will interopolate any NAN values in between (ie. the line will not be interrupted).
|
||||
**Note**: you can return NAN instead of CGFloat to indicate missing values. The chart's line will begin at the first non-NAN value and end at the last non-NAN value. The line will interopolate any NAN values in between (ie. the line will not be interrupted).
|
||||
|
||||
return [[NSNumber numberWithFloat:NAN] floatValue];
|
||||
|
||||
@@ -153,145 +168,7 @@ Lastly, ensure you have set the *frame* of your lineChartView & call *reloadData
|
||||
|
||||
## Customization
|
||||
|
||||
Both the line and bar charts support a robust set of customization options.
|
||||
|
||||
The background of a bar or line chart can be set just like any other view:
|
||||
|
||||
self.barChartView.backgroundColor = ...; // UIColor
|
||||
self.lineChartView.backgroundColor = ...; // UIColor
|
||||
|
||||
Any <i>JBChartView</i> subclass supports the use of headers and footers (similiar to that of <i>UITableView</i>):
|
||||
|
||||
self.barChartView.footerView = ...; // UIView
|
||||
self.lineChartView.headerView = ...; // UIView
|
||||
|
||||
Lastly, any JBChartView subclass can be collapsed or expanded programmatically via the <i>state</i> property. If you chose to animate state changes, a callback helper can be used to notify you when the animation has completed:
|
||||
|
||||
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback;
|
||||
|
||||
#### JBBarChartView
|
||||
|
||||
A bar chart can be inverted such that it's orientation is top->down (including the selection view) by setting the following property:
|
||||
|
||||
@property (nonatomic, assign, getter=isInverted) BOOL inverted;
|
||||
|
||||
By default, a chart's bars will be black and flat. They can be customized by supplying a UIView subclass through the <i>optional</i> protocol:
|
||||
|
||||
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index
|
||||
{
|
||||
return ...; // color of line in chart
|
||||
}
|
||||
|
||||
If you don't require a custom UIView, simply supply a color for the bar instead:
|
||||
|
||||
- (UIColor *)barChartView:(JBBarChartView *)barChartView colorForBarViewAtIndex:(NSUInteger)index;
|
||||
|
||||
Furthermore, the color of the selection bar (on touch events) can be customized via the <i>optional</i> protocol:
|
||||
|
||||
- (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView
|
||||
{
|
||||
return ...; // color of selection view
|
||||
}
|
||||
|
||||
Lastly, a bar chart's selection events are delegated back via:
|
||||
|
||||
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
- (void)didDeselectBarChartView:(JBBarChartView *)barChartView
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
The <b>touchPoint</b> is especially important as it allows you to add custom elements to your chart during selection events. Refer to the demo project (<b>JBarChartViewController</b>) to see how a tooltip can be used to display additional information during selection events.
|
||||
|
||||
#### JBLineChartView
|
||||
|
||||
The color, width and style of each line in the chart can be customized via the <i>optional</i> protocol:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of line in chart
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView fillColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of area under line in chart
|
||||
}
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView widthForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // width of line in chart
|
||||
}
|
||||
|
||||
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // style of line in chart
|
||||
}
|
||||
|
||||
Furthermore, the color and width of the selection view along with the color of the selected line can be customized via the <i>optional</i> protocols:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView verticalSelectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of selection view
|
||||
}
|
||||
|
||||
- (CGFloat)verticalSelectionWidthForLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
return ...; // width of selection view
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of selected line
|
||||
}
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionFillColorForLineAtLineIndex:(NSUInteger)lineIndex
|
||||
{
|
||||
return ...; // color of area under selected line
|
||||
}
|
||||
|
||||
By default, each line will not show dots for each point. To enable this on a per-line basis:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
To the radius of each dot (default is 6x the line width, or 3x the diameter), implement:
|
||||
|
||||
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView dotRadiusForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
To customize the color of each dot during selection and non-selection events (default is white and black respectively), implement:
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForDotAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Alternatively, you can supply your own UIView instead of using the default impelmentation:
|
||||
|
||||
- (UIView *)lineChartView:(JBLineChartView *)lineChartView dotViewAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Custom dot views are automatically shown when selected unless the following is implemented:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView shouldHideDotViewOnSelectionAtHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
As well, by default, each line will have squared off end caps and connection points. To enable rounded connections and end caps:
|
||||
|
||||
- (BOOL)lineChartView:(JBLineChartView *)lineChartView smoothLineAtLineIndex:(NSUInteger)lineIndex;
|
||||
|
||||
Lastly, a line chart's selection events are delegated back via:
|
||||
|
||||
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
- (void)didDeselectLineInLineChartView:(JBLineChartView *)lineChartView
|
||||
{
|
||||
// Update view
|
||||
}
|
||||
|
||||
The <b>touchPoint</b> is especially important as it allows you to add custom elements to your chart during selection events. Refer to the demo project (<b>JBLineChartViewController</b>) to see how a tooltip can be used to display additional information during selection events.
|
||||
Both the line and bar charts support a robust set of customization options. Read more about them <a href="Customization.md">here</a>.
|
||||
|
||||
## Minimum & Maximum Values
|
||||
|
||||
@@ -307,6 +184,9 @@ If value(s) are supplied, they must be >= 0, otherwise an assertion will be thro
|
||||
|
||||
The min/max values are clamped to the ceiling and floor of the actual min/max values of the chart's data source; for example, if a maximumValue of 20 is supplied & the chart's actual max is 100, then 100 will be used. For min/max modifications to take effect, reloadData must be called.
|
||||
|
||||
## Performance
|
||||
|
||||
The nature of charting is to display all available information, unlike a UITableView, which can cache rows that are offscreen. JBChartView's performance will suffer if the number of data points exceed the resolution of the device. The same issue exists with MKMapView, when hundreds of pins are supplied within a certain geographic area. It's why Apple recommends clustering to avoid performance issues. As such, for large datasets, we recommend that your dataSource to supply a subset of points; clustering those that are close to one another.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Reference in New Issue
Block a user