Compare commits

...

79 Commits

Author SHA1 Message Date
Terry Worona 01171b9487 Updated read me and version 2014-03-19 13:32:27 -07:00
Terry Worona d3a1ce1c25 Fixed issue #22 2014-03-19 13:30:26 -07:00
Terry Worona 3b6863786f Updated read me 2014-03-19 13:27:00 -07:00
Terry Worona 1d39945e62 added frame and reload docs to readme 2014-03-18 17:56:17 -07:00
Terry Worona f0f1d5174e More read me work 2014-03-18 17:38:14 -07:00
Terry Worona 11e0f21e03 More read me work 2014-03-18 17:35:57 -07:00
Terry Worona 59183657e8 Center image 2014-03-18 17:35:26 -07:00
Terry Worona 47a75d4291 updated image 2014-03-18 17:34:41 -07:00
Terry Worona 32a449f460 Updated read me and pod spec 2014-03-18 17:32:42 -07:00
terryworona d9aea500bf Merge pull request #21 from Jawbone/multi_line
Multiple Line Support & Tooltips
2014-03-18 17:31:32 -07:00
Terry Worona 8c947322ae Added new line to constants file 2014-03-18 17:28:45 -07:00
Terry Worona 5b397a4f49 fixed assert comment 2014-03-18 17:28:23 -07:00
Terry Worona 44c79e8a7c Minor fix to bar chart datasource 2014-03-18 17:03:14 -07:00
Terry Worona 40db9062ce Line view assert checks 2014-03-18 17:01:07 -07:00
Terry Worona dafb0f81d4 More asserts for bar charts 2014-03-18 16:54:43 -07:00
Terry Worona f8df682b62 more asserts 2014-03-18 16:48:34 -07:00
Terry Worona 293dc29183 Added asserts 2014-03-18 16:48:12 -07:00
Terry Worona ef1b8b3414 Fixed extreme value bugs 2014-03-18 16:24:50 -07:00
Terry Worona 58bbb99c94 Updated read me 2014-03-18 16:04:18 -07:00
Terry Worona b829884008 Cleaned up strings and random chart data 2014-03-18 15:12:06 -07:00
Terry Worona e82756172b Cleaning up string constants 2014-03-18 14:08:30 -07:00
Terry Worona 58651a3325 fixed line chart out of bounds crash 2014-03-18 11:47:58 -07:00
Terry Worona 328b5775aa Changed line chart style order 2014-03-17 14:27:43 -07:00
Terry Worona 64f22aa758 more readme 2014-03-17 14:22:58 -07:00
Terry Worona bc9e9c31b5 Cleaning up read me 2014-03-17 14:21:13 -07:00
Terry Worona c4d1d2ade3 Updated read me and pod spec 2014-03-17 14:15:37 -07:00
Terry Worona 97874ce1ef Updated project settings 2014-03-17 14:15:28 -07:00
Terry Worona dcc826e8a6 Added nib init support 2014-03-17 11:28:22 -07:00
Terry Worona 31bb7a0a2b more tooltip views 2014-03-17 11:08:16 -07:00
Terry Worona d83742315f more tooltip cleanup 2014-03-17 11:06:41 -07:00
Terry Worona 7e4cbc0533 More tooltip work 2014-03-17 10:57:34 -07:00
Terry Worona 87d752c5e3 Improving chart tooltip position 2014-03-17 10:39:03 -07:00
Terry Worona 5f2de0e862 More tooltip work and tooltip tip view 2014-03-17 10:32:48 -07:00
Terry Worona 0d4f17dd25 Updated line separator size 2014-03-13 17:57:05 -07:00
Terry Worona 9ba965ecf0 added tooltip to bar chart view 2014-03-13 16:53:49 -07:00
Terry Worona 31b0ba8e28 moved tooltip functionality to base chart controller 2014-03-13 16:46:57 -07:00
Terry Worona 9edd9d562a Merge branch 'master' of https://github.com/Jawbone/JBChartView into multi_line 2014-03-13 16:21:13 -07:00
Terry Worona d30abc6a1a Fixed issue #19 2014-03-13 16:12:22 -07:00
Terry Worona 78d1bfb275 Information view cleanup 2014-03-12 19:16:10 -07:00
Terry Worona b6de13f1ac more tooltip and touch work 2014-03-12 18:42:43 -07:00
Terry Worona 1dbd52fd75 tooltip work 2014-03-12 18:27:32 -07:00
Terry Worona 0917d4b6b8 more opacity work 2014-03-12 16:47:31 -07:00
Terry Worona d10b42dc5a added line dims on selection 2014-03-12 16:45:24 -07:00
Terry Worona 78f34ecaa5 more constant cleanup 2014-03-12 16:27:48 -07:00
Terry Worona cc2d356223 cleaning up line controller 2014-03-12 16:25:09 -07:00
Terry Worona 2c8a578f92 added interface comments 2014-03-12 16:15:03 -07:00
Terry Worona 2e5b1c4d01 line chart line style work 2014-03-12 16:03:05 -07:00
Terry Worona a58ac858ab Integer cleanup and adding line customization 2014-03-12 15:17:30 -07:00
Terry Worona ebfb4c421e Added comments 2014-03-11 15:02:32 -07:00
Terry Worona b08324e6dd Fixed chart extremes 2014-03-11 14:58:39 -07:00
Terry Worona 72dda814c6 more line cleanup 2014-03-11 14:01:49 -07:00
Terry Worona e76daca899 updated datasource functions 2014-03-11 12:04:38 -07:00
Terry Worona bafcc02862 updating chart comments 2014-03-11 11:51:50 -07:00
Terry Worona 9d80750467 Internal line view cleanup 2014-03-11 11:28:06 -07:00
Terry Worona 8c93fafa37 Multiple line selections finally working. 2014-03-10 19:14:35 -07:00
Terry Worona e518246f26 line selection working 2014-03-07 15:01:16 -08:00
Terry Worona a1256c3297 single line selections 2014-03-07 14:18:14 -08:00
Terry Worona 84ca6d41ae Line selection 2014-03-05 19:31:19 -08:00
Terry Worona d94ef105cf consolidated touch code 2014-03-05 16:41:36 -08:00
Terry Worona 361985f236 More selection view cleanup 2014-03-05 16:29:27 -08:00
Terry Worona 6987b4636a more property cleanup 2014-03-05 16:04:01 -08:00
Terry Worona 18f56fa818 more property renaming for selection views 2014-03-05 15:59:05 -08:00
Terry Worona 5724b8424f Property renaming 2014-03-05 14:57:15 -08:00
Terry Worona f62b3be08e Line chart line selection logic 2014-03-05 14:52:20 -08:00
Terry Worona a4c21168ab hooking up line index logic 2014-03-04 11:56:23 -08:00
Terry Worona f1ec5add4b Updated shows selection bool naming 2014-03-04 11:53:29 -08:00
Terry Worona 62ed0c6496 Started multi line support 2014-03-04 11:36:24 -08:00
Terry Worona 3d5e5c3e1d Updated podspect 2014-03-02 17:24:33 -08:00
Terry Worona 7e33f7121a Updated read me 2014-03-02 17:23:53 -08:00
Terry Worona 09fefa292a Added assert check for line width and added hookups to demo project 2014-03-02 17:16:51 -08:00
Terry Worona 47c6d744ba added periods to comments 2014-03-02 17:14:49 -08:00
Terry Worona fc32f8282e Moved pod spec back into /Classes 2014-03-02 17:10:31 -08:00
terryworona 7ee84c24f0 Merge pull request #15 from kmcbride/line-customization
Added customization for line width
2014-03-02 17:06:44 -08:00
Terry Worona 12a15f1051 Added default init for bar and line charts. 2014-03-02 16:49:03 -08:00
Kylan McBride 6a8f927510 Moved podspec location 2014-02-14 22:36:25 -08:00
Kylan McBride 3bf16ecae9 Added line width customization 2014-02-14 22:34:23 -08:00
Kylan McBride 5a7b211f16 Fixed Typo and excess whitespace 2014-02-14 22:32:44 -08:00
terryworona 189c907bb9 Merge pull request #14 from Undo1/patch-1
fix spelling mistake
2014-02-14 09:38:35 -08:00
Undo1 6f16ae64ab fix spelling mistake 2014-02-13 20:06:11 -07:00
30 changed files with 1413 additions and 520 deletions
+28 -26
View File
@@ -18,11 +18,11 @@
@property (nonatomic, weak) id<JBBarChartViewDataSource> dataSource;
/**
* If showsSelection is YES, a vertical highlight will overlayed on a bar during touch events.
*
* Default: YES
* Vertical highlight overlayed on bar during touch events.
*
* Default: YES.
*/
@property (nonatomic, assign) BOOL showsSelection;
@property (nonatomic, assign) BOOL showsVerticalSelection;
@end
@@ -34,32 +34,33 @@
* Height for a bar 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.
*
* @param barChartView The origin chart
* @param index The 0-based index of a given bar (left to right, x-axis)
* @param barChartView The bar chart object requesting this information.
* @param index The 0-based index of a given bar (left to right, x-axis).
*
* @return The y-axis height of the supplied bar index (x-axis)
*/
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSInteger)index;
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSUInteger)index;
@optional
/**
* Occurs when a touch gesture event occurs on a given bar. The chart must be expanded, showsSelection must be YES,
* Occurs when a touch gesture event occurs on a given bar (chart must be expanded).
* and the selection must occur within the bounds of the chart.
*
* @param barChartView The origin chart
* @param index The 0-based index of a given bar (left to right, x-axis)
* @param barChartView A bar chart object informing the delegate about the new selection.
* @param index The 0-based index of a given bar (left to right, x-axis).
* @param touchPoint The touch point in relation to the chart's bounds (excludes footer and header).
*/
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSInteger)index;
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint;
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index;
/**
* Occurs when selection ends by either ending a touch event or selecting an area that is outside the view's bounds.
* For selection start events, see: didSelectBarAtIndex...
*
* @param barChartView The origin chart
* @param index The 0-based index of a given bar. Index will be -1 if the touch ends outside of the view's bounds.
* @param barChartView A bar chart object informing the delegate about the unselection.
*/
- (void)barChartView:(JBBarChartView *)barChartView didUnselectBarAtIndex:(NSInteger)index;
- (void)didUnselectBarChartView:(JBBarChartView *)barChartView;
@end
@@ -70,11 +71,11 @@
/**
* The number of bars in a given bar chart is the number of vertical views shown along the x-axis.
*
* @param barChartView The origin chart
* @param barChartView The bar chart object requesting this information.
*
* @return Number of bars in the given chart, displayed horizontally along the chart's x-axis.
*/
- (NSInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView;
- (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView;
@optional
@@ -83,34 +84,35 @@
*
* Default: 'best-guess' algorithm based on the the total number of bars and width of the chart.
*
* @param barChartView The origin chart
* @param barChartView The bar chart object requesting this information.
*
* @return Horizontal width (in pixels) between each bar.
*/
- (NSInteger)barPaddingForBarChartView:(JBBarChartView *)barChartView;
- (NSUInteger)barPaddingForBarChartView:(JBBarChartView *)barChartView;
/**
* A UIView subclass representing the bar at a particular index.
*
* Default: solid black UIView
* Default: solid black UIView.
*
* @param barChartView The origin chart
* @param index The 0-based index of a given bar (left to right, x-axis)
* @param barChartView The bar chart object requesting this information.
* @param index The 0-based index of a given bar (left to right, x-axis).
*
* @return A UIView subclass. The view will automatically be resized by the chart during creation (ie. no need to set the frame).
*/
- (UIView *)barViewForBarChartView:(JBBarChartView *)barChartView atIndex:(NSInteger)index;
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index;
/**
* The selection color to be overlayed on a bar during touch events.
* The color is automically faded to transparent (vertically).
* The color is automically faded to transparent (vertically). The property showsVerticalSelection
* must be YES for the color to apply.
*
* Default: white color (faded to transparent)
* Default: white color (faded to transparent).
*
* @param barChartView The origin chart
* @param barChartView The bar chart object requesting this information.
*
* @return The color to be used on each bar selection.
*/
- (UIColor *)selectionBarColorForBarChartView:(JBBarChartView *)barChartView;
- (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView;
@end
+156 -119
View File
@@ -24,8 +24,11 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
@property (nonatomic, strong) NSArray *barViews;
@property (nonatomic, assign) CGFloat barPadding;
@property (nonatomic, assign) CGFloat cachedMaxHeight;
@property (nonatomic, strong) JBChartSelectionView *selectionView;
@property (nonatomic, assign) BOOL selectionViewVisible;
@property (nonatomic, strong) JBChartVerticalSelectionView *verticalSelectionView;
@property (nonatomic, assign) BOOL verticalSelectionViewVisible;
// Initialization
- (void)construct;
// View quick accessors
- (CGFloat)availableHeight;
@@ -38,10 +41,11 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
// Touch helpers
- (NSInteger)barViewIndexForPoint:(CGPoint)point;
- (UIView *)barViewForForPoint:(CGPoint)point;
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches;
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches;
// Setters
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible animated:(BOOL)animated;
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated;
@end
@@ -57,18 +61,43 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
}
}
- (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.clipsToBounds = NO;
_showsSelection = YES;
_cachedMaxHeight = kJBBarChartViewUndefinedMaxHeight;
[self construct];
}
return self;
}
- (id)init
{
self = [super init];
if (self)
{
[self construct];
}
return self;
}
- (void)construct
{
self.clipsToBounds = YES;
_showsVerticalSelection = YES;
_cachedMaxHeight = kJBBarChartViewUndefinedMaxHeight;
}
#pragma mark - Memory Management
- (void)dealloc
@@ -90,15 +119,17 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
dispatch_block_t createDataDictionaries = ^{
// Grab the count
NSAssert([self.dataSource respondsToSelector:@selector(numberOfBarsInBarChartView:)], @"JBBarChartView // datasource must implement - (NSInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView");
NSInteger dataCount = [self.dataSource numberOfBarsInBarChartView:self];
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:heightForBarViewAtAtIndex:)], @"JBBarChartView // delegate must implement - (NSInteger)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSInteger)index");
NSAssert([self.delegate respondsToSelector:@selector(barChartView:heightForBarViewAtAtIndex:)], @"JBBarChartView // delegate must implement - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSUInteger)index");
NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
for (NSInteger index=0; index<dataCount; index++)
for (NSUInteger index=0; index<dataCount; index++)
{
[dataDictionary setObject:[NSNumber numberWithFloat:[self.delegate barChartView:self heightForBarViewAtAtIndex:index]] forKey:[NSNumber numberWithInt:(int)index]];
CGFloat height = [self.delegate barChartView:self heightForBarViewAtAtIndex:index];
NSAssert(height >= 0, @"JBBarChartView // datasource function - (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSUInteger)index must return a CGFloat >= 0");
[dataDictionary setObject:[NSNumber numberWithFloat:height] forKey:[NSNumber numberWithInt:(int)index]];
}
self.chartDataDictionary = [NSDictionary dictionaryWithDictionary:dataDictionary];
};
@@ -113,7 +144,7 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
}
else
{
NSInteger totalBars = [[self.chartDataDictionary allKeys] count];
NSUInteger totalBars = [[self.chartDataDictionary allKeys] count];
self.barPadding = (1/(float)totalBars) * kJBBarChartViewBarBasePaddingMutliplier;
}
};
@@ -130,14 +161,15 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
}
CGFloat xOffset = 0;
NSInteger index = 0;
NSUInteger index = 0;
NSMutableArray *mutableBarViews = [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(barViewForBarChartView:atIndex:)])
if ([self.dataSource respondsToSelector:@selector(barChartView:barViewAtIndex:)])
{
barView = [self.dataSource barViewForBarChartView:self atIndex:index];
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
{
@@ -171,27 +203,28 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
dispatch_block_t createSelectionView = ^{
// Remove old selection bar
if (self.selectionView)
if (self.verticalSelectionView)
{
[self.selectionView removeFromSuperview];
self.selectionView = nil;
[self.verticalSelectionView removeFromSuperview];
self.verticalSelectionView = nil;
}
self.selectionView = [[JBChartSelectionView alloc] initWithFrame:CGRectMake(0, 0, [self barWidth], self.bounds.size.height - self.footerView.frame.size.height)];
self.selectionView.alpha = 0.0;
if ([self.dataSource respondsToSelector:@selector(selectionBarColorForBarChartView:)])
self.verticalSelectionView = [[JBChartVerticalSelectionView alloc] initWithFrame:CGRectMake(0, 0, [self barWidth], self.bounds.size.height - self.footerView.frame.size.height)];
self.verticalSelectionView.alpha = 0.0;
self.verticalSelectionView.hidden = !self.showsVerticalSelection;
if ([self.dataSource respondsToSelector:@selector(barSelectionColorForBarChartView:)])
{
self.selectionView.bgColor = [self.dataSource selectionBarColorForBarChartView:self];
self.verticalSelectionView.bgColor = [self.dataSource barSelectionColorForBarChartView:self];
}
// Add new selection bar
if (self.footerView)
{
[self insertSubview:self.selectionView belowSubview:self.footerView];
[self insertSubview:self.verticalSelectionView belowSubview:self.footerView];
}
else
{
[self addSubview:self.selectionView];
[self addSubview:self.verticalSelectionView];
}
};
@@ -244,7 +277,7 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
- (CGFloat)barWidth
{
NSInteger barCount = [[self.chartDataDictionary allKeys] count];
NSUInteger barCount = [[self.chartDataDictionary allKeys] count];
if (barCount > 0)
{
CGFloat totalPadding = (barCount - 1) * self.barPadding;
@@ -267,51 +300,61 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
dispatch_block_t callbackCopy = [callback copy];
if (animated)
if ([self.barViews count] > 0)
{
CGFloat popOffset = [self popOffset];
NSUInteger index = 0;
for (UIView *barView in self.barViews)
if (animated)
{
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:(kJBBarChartViewStateAnimationDuration * 0.5) * index options:UIViewAnimationOptionBeginFromCurrentState animations:^{
barView.frame = CGRectMake(barView.frame.origin.x, popOffset - barView.frame.size.height, barView.frame.size.width, barView.frame.size.height);
} completion:^(BOOL finished) {
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
if (state == JBChartViewStateExpanded)
{
barView.frame = CGRectMake(barView.frame.origin.x, popOffset - barView.frame.size.height + kJBBarChartViewPopOffset, barView.frame.size.width, barView.frame.size.height);
}
else if (state == JBChartViewStateCollapsed)
{
barView.frame = CGRectMake(barView.frame.origin.x, self.bounds.size.height, barView.frame.size.width, barView.frame.size.height);
}
} completion:^(BOOL lastBarFinished) {
if (index == [self.barViews count] - 1)
{
if (callbackCopy)
CGFloat popOffset = [self popOffset];
NSUInteger index = 0;
for (UIView *barView in self.barViews)
{
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:(kJBBarChartViewStateAnimationDuration * 0.5) * index options:UIViewAnimationOptionBeginFromCurrentState animations:^{
barView.frame = CGRectMake(barView.frame.origin.x, popOffset - barView.frame.size.height, barView.frame.size.width, barView.frame.size.height);
} completion:^(BOOL finished) {
[UIView animateWithDuration:kJBBarChartViewStateAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
if (state == JBChartViewStateExpanded)
{
callbackCopy();
barView.frame = CGRectMake(barView.frame.origin.x, popOffset - barView.frame.size.height + kJBBarChartViewPopOffset, barView.frame.size.width, barView.frame.size.height);
}
}
else if (state == JBChartViewStateCollapsed)
{
barView.frame = CGRectMake(barView.frame.origin.x, self.bounds.size.height, barView.frame.size.width, barView.frame.size.height);
}
} completion:^(BOOL lastBarFinished) {
if (index == [self.barViews count] - 1)
{
if (callbackCopy)
{
callbackCopy();
}
}
}];
}];
}];
index++;
index++;
}
}
else
{
for (UIView *barView in self.barViews)
{
if (state == JBChartViewStateExpanded)
{
barView.frame = CGRectMake(barView.frame.origin.x, (self.bounds.size.height + kJBBarChartViewPopOffset) - (barView.frame.size.height + self.footerView.frame.size.height), barView.frame.size.width, barView.frame.size.height);
}
else if (state == JBChartViewStateCollapsed)
{
barView.frame = CGRectMake(barView.frame.origin.x, self.bounds.size.height, barView.frame.size.width, barView.frame.size.height);
}
}
if (callbackCopy)
{
callbackCopy();
}
}
}
else
{
for (UIView *barView in self.barViews)
{
if (state == JBChartViewStateExpanded)
{
barView.frame = CGRectMake(barView.frame.origin.x, (self.bounds.size.height + kJBBarChartViewPopOffset) - (barView.frame.size.height + self.footerView.frame.size.height), barView.frame.size.width, barView.frame.size.height);
}
else if (state == JBChartViewStateCollapsed)
{
barView.frame = CGRectMake(barView.frame.origin.x, self.bounds.size.height, barView.frame.size.width, barView.frame.size.height);
}
}
if (callbackCopy)
{
callbackCopy();
@@ -352,103 +395,97 @@ static UIColor *kJBBarChartViewDefaultBarColor = nil;
NSInteger selectedIndex = [self barViewIndexForPoint:point];
if (selectedIndex >= 0)
{
barView = [self.barViews objectAtIndex:[self barViewIndexForPoint:point]];
return [self.barViews objectAtIndex:[self barViewIndexForPoint:point]];
}
return barView;
}
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
if (self.state == JBChartViewStateCollapsed || [[self.chartDataDictionary allKeys] count] <= 0)
{
return;
}
[self setSelectionViewVisible:NO animated:YES];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
NSInteger index = [self barViewIndexForPoint:touchPoint];
if ([self.delegate respondsToSelector:@selector(barChartView:didUnselectBarAtIndex:)])
UIView *barView = [self barViewForForPoint:touchPoint];
if (barView == nil)
{
[self.delegate barChartView:self didUnselectBarAtIndex:index];
[self setVerticalSelectionViewVisible:NO animated:YES];
return;
}
CGRect barViewFrame = barView.frame;
CGRect selectionViewFrame = self.verticalSelectionView.frame;
selectionViewFrame.origin.x = barViewFrame.origin.x;
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(didUnselectBarChartView:)])
{
[self.delegate didUnselectBarChartView:self];
}
}
#pragma mark - Setters
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible animated:(BOOL)animated
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated
{
_selectionViewVisible = selectionViewVisible;
_verticalSelectionViewVisible = verticalSelectionViewVisible;
if (animated)
{
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.selectionView.alpha = self.selectionViewVisible ? 1.0 : 0.0;
self.verticalSelectionView.alpha = self.verticalSelectionViewVisible ? 1.0 : 0.0;
} completion:nil];
}
else
{
self.selectionView.alpha = _selectionViewVisible ? 1.0 : 0.0;
self.verticalSelectionView.alpha = _verticalSelectionViewVisible ? 1.0 : 0.0;
}
}
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible
{
[self setSelectionViewVisible:selectionViewVisible animated:NO];
[self setVerticalSelectionViewVisible:verticalSelectionViewVisible animated:NO];
}
- (void)setShowsVerticalSelection:(BOOL)showsVerticalSelection
{
_showsVerticalSelection = showsVerticalSelection;
self.verticalSelectionView.hidden = _showsVerticalSelection ? NO : YES;
}
#pragma mark - Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
{
return;
}
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
UIView *barView = [self barViewForForPoint:touchPoint];
if (barView == nil)
{
[self setSelectionViewVisible:NO animated:YES];
return;
}
CGRect barViewFrame = barView.frame;
CGRect selectionViewFrame = self.selectionView.frame;
selectionViewFrame.origin.x = barViewFrame.origin.x;
self.selectionView.frame = selectionViewFrame;
[self setSelectionViewVisible:YES animated:YES];
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:)])
{
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint]];
}
[self touchesBeganOrMovedWithTouches:touches];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
{
return;
}
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
UIView *barView = [self barViewForForPoint:touchPoint];
if (barView == nil)
{
[self setSelectionViewVisible:NO animated:YES];
return;
}
CGRect barViewFrame = barView.frame;
CGRect selectionViewFrame = self.selectionView.frame;
selectionViewFrame.origin.x = barViewFrame.origin.x;
self.selectionView.frame = selectionViewFrame;
[self setSelectionViewVisible:YES animated:YES];
if ([self.delegate respondsToSelector:@selector(barChartView:didSelectBarAtIndex:)])
{
[self.delegate barChartView:self didSelectBarAtIndex:[self barViewIndexForPoint:touchPoint]];
}
[self touchesBeganOrMovedWithTouches:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+12 -12
View File
@@ -34,7 +34,7 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
@property (nonatomic, strong) UIView *headerView;
/**
* The vertical padding between the header and highest chart point (bar, line, etc)
* The vertical padding between the header and highest chart point (bar, line, etc).
*/
@property (nonatomic, assign) CGFloat headerPadding;
@@ -51,19 +51,19 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
- (void)reloadData;
/**
* State setter
* State setter.
*
* @param state Either collapse or expanded.
* @param animated Whether or not the state should be animated or not.
* @param callback Called once the animation is completed. If animated == NO, then callback is immediate.
* @param state Either collapse or expanded.
* @param animated Whether or not the state should be animated or not.
* @param callback Called once the animation is completed. If animated == NO, then callback is immediate.
*/
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback;
/**
* State setter
* State setter.
*
* @param state Either collapse or expanded.
* @param animated Whether or not the state should be animated or not.
* @param state Either collapse or expanded.
* @param animated Whether or not the state should be animated or not.
*/
- (void)setState:(JBChartViewState)state animated:(BOOL)animated;
@@ -71,14 +71,14 @@ typedef NS_ENUM(NSInteger, JBChartViewState){
/**
* A simple UIView subclass that fades a base color from current alpha to 0.0 (vertically).
* Used as a selection ivew in JBChartView subclasses.
* Used as a vertical selection view in JBChartView subclasses.
*/
@interface JBChartSelectionView : UIView
@interface JBChartVerticalSelectionView : UIView
/**
* Base selection view color. This color will be faded to transparent vertically
* Base selection view color. This color will be faded to transparent vertically.
*
* Default: white color
* Default: white color.
*
*/
@property (nonatomic, strong) UIColor *bgColor;
+5 -5
View File
@@ -11,7 +11,7 @@
CGFloat const kJBChartViewDefaultAnimationDuration = 0.25f;
// Color (JBChartSelectionView)
static UIColor *kJBChartSelectionViewDefaultBgColor = nil;
static UIColor *kJBChartVerticalSelectionViewDefaultBgColor = nil;
@interface JBChartView ()
@@ -108,15 +108,15 @@ static UIColor *kJBChartSelectionViewDefaultBgColor = nil;
@end
@implementation JBChartSelectionView
@implementation JBChartVerticalSelectionView
#pragma mark - Alloc/Init
+ (void)initialize
{
if (self == [JBChartSelectionView class])
if (self == [JBChartVerticalSelectionView class])
{
kJBChartSelectionViewDefaultBgColor = [UIColor whiteColor];
kJBChartVerticalSelectionViewDefaultBgColor = [UIColor whiteColor];
}
}
@@ -148,7 +148,7 @@ static UIColor *kJBChartSelectionViewDefaultBgColor = nil;
}
else
{
colors = @[(__bridge id)kJBChartSelectionViewDefaultBgColor.CGColor, (__bridge id)[kJBChartSelectionViewDefaultBgColor colorWithAlphaComponent:0.0].CGColor];
colors = @[(__bridge id)kJBChartVerticalSelectionViewDefaultBgColor.CGColor, (__bridge id)[kJBChartVerticalSelectionViewDefaultBgColor colorWithAlphaComponent:0.0].CGColor];
}
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "JBChartView"
s.version = "1.1.5"
s.version = "2.0.1"
s.summary = "Jawbone's iOS-based charting library for both line and bar graphs."
s.homepage = "https://github.com/Jawbone/JBChartView"
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
s.author = { "Terry Worona" => "tworona@jawbone.com" }
s.source = {
:git => "https://github.com/Jawbone/JBChartView.git",
:tag => "v1.1.5"
:tag => "v2.0.1"
}
s.platform = :ios, '7.0'
+109 -32
View File
@@ -8,6 +8,20 @@
#import "JBChartView.h"
/**
* Current support for two line styles: solid (default) and dashed.
*/
typedef NS_ENUM(NSInteger, JBLineChartViewLineStyle){
/**
* Solid round capped line.
*/
JBLineChartViewLineStyleSolid,
/**
* Dashed square capped line with a phase of 3:2 (3 points dashed, 2 points spaced).
*/
JBLineChartViewLineStyleDashed
};
@protocol JBLineChartViewDelegate;
@protocol JBLineChartViewDataSource;
@@ -17,11 +31,20 @@
@property (nonatomic, weak) id<JBLineChartViewDataSource> dataSource;
/**
* If showsSelection is YES, a vertical highlight will overlayed on a the line graph during touch events.
* Vertical highlight overlayed on a line graph during touch events.
*
* Default: YES
* Default: YES.
*/
@property (nonatomic, assign) BOOL showsSelection;
@property (nonatomic, assign) BOOL showsVerticalSelection;
/**
* 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
* didSelectChartAtHorizontalIndex:atLineIndex: and didUnSlectChartAtHorizontalIndex:atLineIndex:
*
* Default: YES.
*/
@property (nonatomic, assign) BOOL showsLineSelection;
@end
@@ -30,35 +53,39 @@
@required
/**
* Vertical position for line point at a given index (left to right). There is no ceiling on the the height;
* 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.
*
* @param lineChartView The origin chart
* @param index The 0-based index of a given line height (left to right, x-axis)
* @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).
* @param lineIndex An index number identifying the closest line in the chart to the current touch point.
*
* @return The y-axis value of the supplied line index (x-axis)
*/
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView heightForIndex:(NSInteger)index;
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
@optional
/**
* Occurs when a touch gesture event occurs anywhere on the chart. The chart must be expanded, showsSelection must be YES,
* and the selection must occur within the bounds of the chart.
* 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 unselected and will be highlighted using the (optional) selectionColorForLineAtLineIndex: protocol.
* Futhermore, all other lines that aren't selected will be dimmed to 50% opacity throughout the duration of the touch/move.
*
* @param lineChartView The origin chart
* @param index The 0-based index of a selection point (left to right, x-axis)
* @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
* @param horizontalIndex The 0-based horizontal index of a selection point (left to right, x-axis).point.
* @param touchPoint The touch point in relation to the chart's bounds (excludes footer and header).
*/
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectChartAtIndex:(NSInteger)index;
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint;
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex;
/**
* Occurs when selection ends by either ending a touch event or selecting an area that is outside the view's bounds.
* For selection start events, see: didSelectChartAtIndex...
* Occurs when selection ends by ending a touch event. For selection start events, see: didSelectChartAtIndex:
*
* @param lineChartView The origin chart
* @param index The 0-based index of a selection point. Index will be -1 if the touch ends outside of the view's bounds.
* @param lineChartView A line chart object informing the delegate about the unselection.
*/
- (void)lineChartView:(JBLineChartView *)lineChartView didUnselectChartAtIndex:(NSInteger)index;
- (void)didUnselectLineInLineChartView:(JBLineChartView *)lineChartView;
@end
@@ -67,37 +94,87 @@
@required
/**
* The number of points in a given line chart equates to the number of values along the x-axis.
* Returns the number of lines for the line chart.
*
* @param lineChartView The origin chart
* @param lineChartView The line chart object requesting this information.
*
* @return Number of points in the given chart.
* @return The number of lines in the line chart.
*/
- (NSInteger)numberOfPointsInLineChartView:(JBLineChartView *)lineChartView;
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView;
/**
* Returns the number of vertical values for a particular line at lineIndex within the chart.
*
* @param lineChartView The line chart object requesting this information.
* @param lineIndex An index number identifying a line in the chart.
*
* @return The number of vertical values for a given line in the line chart.
*/
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex;
@optional
/**
* The color of the line within the chart.
*
* Default: black color
* Returns the color of particular line at lineIndex within the chart.
*
* @param lineChartView The origin chart
* Default: black color.
*
* @return The color to be used to draw the line on the chart (alphas < 1 are supported)
* @param lineChartView The line chart object requesting this information.
* @param lineIndex An index number identifying a line in the chart.
*
* @return The color to be used to shade a line in the chart.
*/
- (UIColor *)lineColorForLineChartView:(JBLineChartView *)lineChartView;
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex;
/**
* The selection color to be overlayed on the chart during touch events.
* The color is automically faded to transparent (vertically).
* Returns the width of particular line at lineIndex within the chart.
*
* Default: white color (faded to transparent)
* Default: 5 points.
*
* @param lineChartView The origin chart
* @param lineChartView The line chart object requesting this information.
* @param lineIndex An index number identifying a line in the chart.
*
* @return The width to be used to draw a line in the chart.
*/
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView widthForLineAtLineIndex:(NSUInteger)lineIndex;
/**
* Returns the (vertical) selection color to be overlayed on the chart during touch events.
* The color is automically faded to transparent (vertically). The property showsVerticalSelection
* must be YES for the color to apply.
*
* Default: white color (faded to transparent).
*
* @param lineChartView The line chart object requesting this information.
*
* @return The color to be used on chart selections.
*/
- (UIColor *)selectionColorForLineChartView:(JBLineChartView *)lineChartView;
- (UIColor *)verticalSelectionColorForLineChartView:(JBLineChartView *)lineChartView;
/**
* 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.
*
* @param lineChartView The line chart object requesting this information.
* @param lineIndex An index number identifying a line in the chart.
*
* @return The color to be used to highlight a line during chart selections.
*/
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex;
/**
* Returns the line style of a particular line at lineIndex within the chart.
* See JBLineChartViewLineStyle for line style descriptions.
*
* Default: JBLineChartViewLineStyleSolid
*
* @param lineChartView The line chart object requesting this information.
* @param lineIndex An index number identifying a line in the chart.
*
* @return The line style to be used to draw a line in the chart.
*/
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex;
@end
+495 -185
View File
@@ -12,25 +12,49 @@
#import <QuartzCore/QuartzCore.h>
// Enums
typedef NS_ENUM(NSInteger, JBLineChartLineViewState){
typedef NS_ENUM(NSUInteger, JBLineChartLineViewState){
JBLineChartLineViewStateExpanded,
JBLineChartLineViewStateCollapsed
};
typedef NS_ENUM(NSUInteger, JBLineChartHorizontalIndexClamp){
JBLineChartHorizontalIndexClampLeft,
JBLineChartHorizontalIndexClampRight,
JBLineChartHorizontalIndexClampNone
};
// Numerics (JBLineChartLineView)
CGFloat static const kJBLineChartLineViewEdgePadding = 10.0;
CGFloat static const kJBLineChartLineViewStrokeWidth = 5.0;
CGFloat static const kJBLineChartLineViewMiterLimit = -5.0;
CGFloat static const kJBLineChartLineViewStateAnimationDuration = 0.25f;
CGFloat static const kJBLineChartLineViewDefaultLinePhase = 1.0f;
CGFloat static const kJBLineChartLineViewDefaultDimmedOpacity = 0.5f;
NSInteger static const kJBLineChartLineViewUnselectedLineIndex = -1;
// Numerics (JBLineSelectionView)
CGFloat static const kJBLineSelectionViewWidth = 20.0f;
// Numerics (JBLineChartView)
CGFloat static const kJBLineChartViewUndefinedMaxHeight = -1.0f;
NSInteger static const kJBLineChartUnselectedLineIndex = -1;
// Collections (JBLineChartLineView)
static NSArray *kJBLineChartLineViewDefaultDashPattern = nil;
// Colors (JBLineChartView)
static UIColor *kJBLineChartViewDefaultLineColor = nil;
static UIColor *kJBLineChartViewDefaultLineSelectionColor = nil;
// Strings (JBLineChartView)
NSString * const kJBLineChartViewAnimationPathKey = @"path";
@interface JBLineLayer : CAShapeLayer
@property (nonatomic, assign) NSUInteger tag;
@property (nonatomic, assign) JBLineChartViewLineStyle lineStyle;
@end
@interface JBLineChartPoint : NSObject
@@ -44,8 +68,8 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
@property (nonatomic, assign) id<JBLineChartLineViewDelegate> delegate;
@property (nonatomic, assign) JBLineChartLineViewState state;
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@property (nonatomic, assign) BOOL aniamted;
@property (nonatomic, assign) NSInteger selectedLineIndex; // -1 to unselect
@property (nonatomic, assign) BOOL animated;
// Data
- (void)reloadData;
@@ -57,12 +81,18 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
// Callback helpers
- (void)fireCallback:(void (^)())callback;
// View helpers
- (JBLineLayer *)lineLayerForLineIndex:(NSUInteger)lineIndex;
@end
@protocol JBLineChartLineViewDelegate <NSObject>
- (NSArray *)chartDataForLineChartLineView:(JBLineChartLineView*)lineChartLineView;
- (UIColor *)lineColorForLineChartLineView:(JBLineChartLineView*)lineChartLineView;
- (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView colorForLineAtLineIndex:(NSUInteger)lineIndex;
- (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView selectedColorForLineAtLineIndex:(NSUInteger)lineIndex;
- (CGFloat)lineChartLineView:(JBLineChartLineView *)lineChartLineView widthForLineAtLineIndex:(NSUInteger)lineIndex;
- (JBLineChartViewLineStyle)lineChartLineView:(JBLineChartLineView *)lineChartLineView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex;
@end
@@ -70,23 +100,31 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
@property (nonatomic, strong) NSArray *chartData;
@property (nonatomic, strong) JBLineChartLineView *lineView;
@property (nonatomic, strong) JBChartSelectionView *selectionView;
@property (nonatomic, strong) JBChartVerticalSelectionView *verticalSelectionView;
@property (nonatomic, assign) CGFloat cachedMaxHeight;
@property (nonatomic, assign) BOOL selectionViewVisible;
@property (nonatomic, assign) BOOL verticalSelectionViewVisible;
// Initialization
- (void)construct;
// View quick accessors
- (CGFloat)normalizedHeightForRawHeight:(CGFloat)rawHeight;
- (CGFloat)availableHeight;
- (CGFloat)maxHeight;
- (CGFloat)minHeight;
- (NSInteger)dataCount;
- (NSUInteger)dataCount;
// Touch helpers
- (NSInteger)indexForPoint:(CGPoint)point;
- (CGPoint)clampPoint:(CGPoint)point toBounds:(CGRect)bounds padding:(CGFloat)padding;
- (NSInteger)horizontalIndexForPoint:(CGPoint)point indexClamp:(JBLineChartHorizontalIndexClamp)indexClamp lineData:(NSArray *)lineData;
- (NSInteger)horizontalIndexForPoint:(CGPoint)point indexClamp:(JBLineChartHorizontalIndexClamp)indexClamp; // uses largest line data
- (NSInteger)horizontalIndexForPoint:(CGPoint)point;
- (NSInteger)lineIndexForPoint:(CGPoint)point;
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches;
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches;
// Setters
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible animated:(BOOL)animated;
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated;
@end
@@ -99,106 +137,152 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
if (self == [JBLineChartView class])
{
kJBLineChartViewDefaultLineColor = [UIColor blackColor];
kJBLineChartViewDefaultLineSelectionColor = [UIColor whiteColor];
}
}
- (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.clipsToBounds = NO;
_showsSelection = YES;
_cachedMaxHeight = kJBLineChartViewUndefinedMaxHeight;
[self construct];
}
return self;
}
- (id)init
{
self = [super init];
if (self)
{
[self construct];
}
return self;
}
- (void)construct
{
self.clipsToBounds = NO;
_showsVerticalSelection = YES;
_showsLineSelection = YES;
_cachedMaxHeight = kJBLineChartViewUndefinedMaxHeight;
}
#pragma mark - Data
- (void)reloadData
{
// reset cached max height
self.cachedMaxHeight = kJBLineChartViewUndefinedMaxHeight;
/*
* Subview rectangle calculations
*/
CGRect mainViewRect = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, [self availableHeight]);
/*
* The data collection holds all position and marker information:
* constructed via datasource and delegate functions
*/
dispatch_block_t createChartData = ^{
CGFloat pointSpace = (self.bounds.size.width - (kJBLineChartLineViewEdgePadding * 2)) / ([self dataCount] - 1); // Space in between points
CGFloat xOffset = kJBLineChartLineViewEdgePadding;
CGFloat yOffset = 0;
// Build up the data collection
NSAssert([self.delegate respondsToSelector:@selector(lineChartView:heightForIndex:)], @"JBLineChartView // delegate must implement - (NSInteger)lineChartView:(JBLineChartView *)lineChartView heightForIndex:(NSInteger)index");
NSMutableArray *mutableChartData = [NSMutableArray array];
for (NSInteger index=0; index<[self dataCount]; index++)
NSAssert([self.dataSource respondsToSelector:@selector(numberOfLinesInLineChartView:)], @"JBLineChartView // dataSource must implement - (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView");
for (NSUInteger lineIndex=0; lineIndex<[self.dataSource numberOfLinesInLineChartView:self]; lineIndex++)
{
JBLineChartPoint *chartPoint = [[JBLineChartPoint alloc] init];
CGFloat rawHeight = [self.delegate lineChartView:self heightForIndex:index];
CGFloat normalizedHeight = [self normalizedHeightForRawHeight:rawHeight];
yOffset = mainViewRect.size.height - normalizedHeight;
chartPoint.position = CGPointMake(xOffset, yOffset);
[mutableChartData addObject:chartPoint];
xOffset += pointSpace;
NSAssert([self.dataSource respondsToSelector:@selector(lineChartView:numberOfVerticalValuesAtLineIndex:)], @"JBLineChartView // dataSource must implement - (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex");
NSUInteger dataCount = [self.dataSource lineChartView:self numberOfVerticalValuesAtLineIndex:lineIndex];
NSMutableArray *chartPointData = [NSMutableArray array];
for (NSUInteger horizontalIndex=0; horizontalIndex<dataCount; horizontalIndex++)
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartView:verticalValueForHorizontalIndex:atLineIndex:)], @"JBLineChartView // delegate must implement - (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
CGFloat rawHeight = [self.delegate lineChartView:self verticalValueForHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
NSAssert(rawHeight >= 0, @"JBLineChartView // delegate function - (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex must return a CGFloat >= 0");
CGFloat normalizedHeight = [self normalizedHeightForRawHeight:rawHeight];
yOffset = mainViewRect.size.height - normalizedHeight;
JBLineChartPoint *chartPoint = [[JBLineChartPoint alloc] init];
chartPoint.position = CGPointMake(xOffset, yOffset);
[chartPointData addObject:chartPoint];
xOffset += pointSpace;
}
[mutableChartData addObject:chartPointData];
xOffset = kJBLineChartLineViewEdgePadding;
}
self.chartData = [NSArray arrayWithArray:mutableChartData];
};
/*
* Creates a new line graph view using the previously calculated data model
*/
dispatch_block_t createLineGraphView = ^{
// Remove old line and overlay views
if (self.lineView)
{
[self.lineView removeFromSuperview];
self.lineView = nil;
}
// Create new line and overlay subviews
self.lineView = [[JBLineChartLineView alloc] initWithFrame:CGRectOffset(mainViewRect, 0, self.headerView.frame.size.height + self.headerPadding)];
self.lineView.delegate = self;
[self addSubview:self.lineView];
};
/*
* Creates a vertical selection view for touch events
*/
dispatch_block_t createSelectionView = ^{
if (self.selectionView)
if (self.verticalSelectionView)
{
[self.selectionView removeFromSuperview];
self.selectionView = nil;
[self.verticalSelectionView removeFromSuperview];
self.verticalSelectionView = nil;
}
self.selectionView = [[JBChartSelectionView alloc] initWithFrame:CGRectMake(0, 0, kJBLineSelectionViewWidth, self.bounds.size.height - self.footerView.frame.size.height)];
self.selectionView.alpha = 0.0;
if ([self.dataSource respondsToSelector:@selector(selectionColorForLineChartView:)])
self.verticalSelectionView = [[JBChartVerticalSelectionView alloc] initWithFrame:CGRectMake(0, 0, kJBLineSelectionViewWidth, self.bounds.size.height - self.footerView.frame.size.height)];
self.verticalSelectionView.alpha = 0.0;
self.verticalSelectionView.hidden = !self.showsVerticalSelection;
if ([self.dataSource respondsToSelector:@selector(verticalSelectionColorForLineChartView:)])
{
self.selectionView.bgColor = [self.dataSource selectionColorForLineChartView:self];
self.verticalSelectionView.bgColor = [self.dataSource verticalSelectionColorForLineChartView:self];
}
// Add new selection bar
if (self.footerView)
{
[self insertSubview:self.verticalSelectionView belowSubview:self.footerView];
}
else
{
[self addSubview:self.verticalSelectionView];
}
[self insertSubview:self.selectionView belowSubview:self.footerView];
};
createChartData();
createLineGraphView();
createSelectionView();
// Reload views
[self.lineView reloadData];
// 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);
@@ -210,12 +294,12 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
{
CGFloat minHeight = [self minHeight];
CGFloat maxHeight = [self maxHeight];
if ((maxHeight - minHeight) <= 0)
{
return 0;
}
return ((rawHeight - minHeight) / (maxHeight - minHeight)) * [self availableHeight];
}
@@ -228,13 +312,21 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
{
if (self.cachedMaxHeight == kJBLineChartViewUndefinedMaxHeight)
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartView:heightForIndex:)], @"JBLineChartView // delegate must implement - (NSInteger)lineChartView:(JBLineChartView *)lineChartView heightForIndex:(NSInteger)index");
CGFloat maxHeight = 0;
for (NSInteger index=0; index<[self dataCount]; index++)
NSAssert([self.dataSource respondsToSelector:@selector(numberOfLinesInLineChartView:)], @"JBLineChartView // dataSource must implement - (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView");
for (NSUInteger lineIndex=0; lineIndex<[self.dataSource numberOfLinesInLineChartView:self]; lineIndex++)
{
if (([self.delegate lineChartView:self heightForIndex:index]) > maxHeight)
NSAssert([self.dataSource respondsToSelector:@selector(lineChartView:numberOfVerticalValuesAtLineIndex:)], @"JBLineChartView // dataSource must implement - (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex");
NSUInteger dataCount = [self.dataSource lineChartView:self numberOfVerticalValuesAtLineIndex:lineIndex];
for (NSUInteger horizontalIndex=0; horizontalIndex<dataCount; horizontalIndex++)
{
maxHeight = [self.delegate lineChartView:self heightForIndex:index];
NSAssert([self.delegate respondsToSelector:@selector(lineChartView:verticalValueForHorizontalIndex:atLineIndex:)], @"JBLineChartView // delegate must implement - (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex");
CGFloat height = [self.delegate lineChartView:self verticalValueForHorizontalIndex:horizontalIndex atLineIndex:lineIndex];
NSAssert(height >= 0, @"JBLineChartView // delegate function - (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex must return a CGFloat >= 0");
if (height > maxHeight)
{
maxHeight = height;
}
}
}
self.cachedMaxHeight = maxHeight;
@@ -247,10 +339,20 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
return 0;
}
- (NSInteger)dataCount
- (NSUInteger)dataCount
{
NSAssert([self.dataSource respondsToSelector:@selector(numberOfPointsInLineChartView:)], @"JBLineChartView // dataSource must implement - (NSInteger)numberOfPointsInLineChartView:(JBLineChartView *)lineChartView");
return [self.dataSource numberOfPointsInLineChartView:self];
NSUInteger dataCount = 0;
NSAssert([self.dataSource respondsToSelector:@selector(numberOfLinesInLineChartView:)], @"JBLineChartView // dataSource must implement - (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView");
for (NSUInteger lineIndex=0; lineIndex<[self.dataSource numberOfLinesInLineChartView:self]; lineIndex++)
{
NSAssert([self.dataSource respondsToSelector:@selector(lineChartView:numberOfVerticalValuesAtLineIndex:)], @"JBLineChartView // dataSource must implement - (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex");
NSUInteger lineDataCount = [self.dataSource lineChartView:self numberOfVerticalValuesAtLineIndex:lineIndex];
if (lineDataCount > dataCount)
{
dataCount = lineDataCount;
}
}
return dataCount;
}
#pragma mark - JBLineChartLineViewDelegate
@@ -260,131 +362,248 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
return self.chartData;
}
- (UIColor *)lineColorForLineChartLineView:(JBLineChartLineView*)lineChartLineView
- (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView colorForLineAtLineIndex:(NSUInteger)lineIndex
{
if ([self.dataSource respondsToSelector:@selector(lineColorForLineChartView:)])
if ([self.dataSource respondsToSelector:@selector(lineChartView:colorForLineAtLineIndex:)])
{
return [self.dataSource lineColorForLineChartView:self];
return [self.dataSource lineChartView:self colorForLineAtLineIndex:lineIndex];
}
return kJBLineChartViewDefaultLineColor;
}
- (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView selectedColorForLineAtLineIndex:(NSUInteger)lineIndex
{
if ([self.dataSource respondsToSelector:@selector(lineChartView:selectionColorForLineAtLineIndex:)])
{
return [self.dataSource lineChartView:self selectionColorForLineAtLineIndex:lineIndex];
}
return kJBLineChartViewDefaultLineSelectionColor;
}
- (CGFloat)lineChartLineView:(JBLineChartLineView *)lineChartLineView widthForLineAtLineIndex:(NSUInteger)lineIndex
{
if ([self.dataSource respondsToSelector:@selector(lineChartView:widthForLineAtLineIndex:)])
{
return [self.dataSource lineChartView:self widthForLineAtLineIndex:lineIndex];
}
return kJBLineChartLineViewStrokeWidth;
}
- (JBLineChartViewLineStyle)lineChartLineView:(JBLineChartLineView *)lineChartLineView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex
{
if ([self.dataSource respondsToSelector:@selector(lineChartView:lineStyleForLineAtLineIndex:)])
{
return [self.dataSource lineChartView:self lineStyleForLineAtLineIndex:lineIndex];
}
return JBLineChartViewLineStyleSolid;
}
#pragma mark - Setters
- (void)setState:(JBChartViewState)state animated:(BOOL)animated callback:(void (^)())callback
{
[super setState:state animated:animated callback:callback];
if (state == JBChartViewStateCollapsed)
if ([self.chartData count] > 0)
{
[self.lineView setState:JBLineChartLineViewStateCollapsed animated:animated callback:callback];
if (state == JBChartViewStateCollapsed)
{
[self.lineView setState:JBLineChartLineViewStateCollapsed animated:animated callback:callback];
}
else if (state == JBChartViewStateExpanded)
{
[self.lineView setState:JBLineChartLineViewStateExpanded animated:animated callback:callback];
}
}
else if (state == JBChartViewStateExpanded)
else
{
[self.lineView setState:JBLineChartLineViewStateExpanded animated:animated callback:callback];
if (callback)
{
callback();
}
}
}
#pragma mark - Touch Helpers
- (NSInteger)indexForPoint:(CGPoint)point
- (CGPoint)clampPoint:(CGPoint)point toBounds:(CGRect)bounds padding:(CGFloat)padding
{
return CGPointMake(MIN(MAX(bounds.origin.x + padding, point.x), bounds.size.width - padding),
MIN(MAX(bounds.origin.y + padding, point.y), bounds.size.height - padding));
}
- (NSInteger)horizontalIndexForPoint:(CGPoint)point indexClamp:(JBLineChartHorizontalIndexClamp)indexClamp lineData:(NSArray *)lineData
{
NSUInteger index = 0;
CGFloat currentDistance = INT_MAX;
NSUInteger selectedIndex = -1;
for (JBLineChartPoint *lineChartPoint in self.chartData)
NSInteger selectedIndex = kJBLineChartUnselectedLineIndex;
for (JBLineChartPoint *lineChartPoint in lineData)
{
if ((abs(point.x - lineChartPoint.position.x)) < currentDistance)
BOOL clamped = (indexClamp == JBLineChartHorizontalIndexClampNone) ? YES : (indexClamp == JBLineChartHorizontalIndexClampLeft) ? (point.x - lineChartPoint.position.x >= 0) : (point.x - lineChartPoint.position.x <= 0);
if ((abs(point.x - lineChartPoint.position.x)) < currentDistance && clamped == YES)
{
currentDistance = (abs(point.x - lineChartPoint.position.x));
selectedIndex = index;
}
index++;
}
return selectedIndex != kJBLineChartUnselectedLineIndex ? selectedIndex : [lineData count] - 1;
}
- (NSInteger)horizontalIndexForPoint:(CGPoint)point indexClamp:(JBLineChartHorizontalIndexClamp)indexClamp
{
NSArray *largestLineData = nil;
for (NSArray *lineData in self.chartData)
{
if ([lineData count] > [largestLineData count])
{
largestLineData = lineData;
}
}
return [self horizontalIndexForPoint:point indexClamp:indexClamp lineData:largestLineData];
}
- (NSInteger)horizontalIndexForPoint:(CGPoint)point
{
return [self horizontalIndexForPoint:point indexClamp:JBLineChartHorizontalIndexClampNone];
}
- (NSInteger)lineIndexForPoint:(CGPoint)point
{
// Find the horizontal indexes
NSInteger leftHorizontalIndex = [self horizontalIndexForPoint:point indexClamp:JBLineChartHorizontalIndexClampLeft];
NSInteger rightHorizontalIndex = [self horizontalIndexForPoint:point indexClamp:JBLineChartHorizontalIndexClampRight];
NSUInteger shortestDistance = INT_MAX;
NSInteger selectedIndex = kJBLineChartUnselectedLineIndex;
NSAssert([self.dataSource respondsToSelector:@selector(numberOfLinesInLineChartView:)], @"JBLineChartView // dataSource must implement - (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView");
// Iterate all lines
for (NSUInteger lineIndex=0; lineIndex<[self.dataSource numberOfLinesInLineChartView:self]; lineIndex++)
{
NSAssert([self.dataSource respondsToSelector:@selector(lineChartView:numberOfVerticalValuesAtLineIndex:)], @"JBLineChartView // dataSource must implement - (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex");
if ([self.dataSource lineChartView:self numberOfVerticalValuesAtLineIndex:lineIndex] > rightHorizontalIndex)
{
NSArray *lineData = [self.chartData objectAtIndex:lineIndex];
// Left point
JBLineChartPoint *leftLineChartPoint = [lineData objectAtIndex:leftHorizontalIndex];
CGPoint leftPoint = CGPointMake(leftLineChartPoint.position.x, fmin(fmax(kJBLineChartLineViewEdgePadding, self.lineView.bounds.size.height - leftLineChartPoint.position.y), self.lineView.bounds.size.height - kJBLineChartLineViewEdgePadding));
// Right point
JBLineChartPoint *rightLineChartPoint = [lineData objectAtIndex:rightHorizontalIndex];
CGPoint rightPoint = CGPointMake(rightLineChartPoint.position.x, fmin(fmax(kJBLineChartLineViewEdgePadding, self.lineView.bounds.size.height - rightLineChartPoint.position.y), self.lineView.bounds.size.height - kJBLineChartLineViewEdgePadding));
// Touch point
CGPoint normalizedTouchPoint = CGPointMake(point.x, self.lineView.bounds.size.height - point.y);
// Slope
CGFloat lineSlope = (CGFloat)(rightPoint.y - leftPoint.y) / (CGFloat)(rightPoint.x - leftPoint.x);
// Insersection point
CGPoint interesectionPoint = CGPointMake(normalizedTouchPoint.x, (lineSlope * (normalizedTouchPoint.x - leftPoint.x)) + leftPoint.y);
CGFloat currentDistance = abs(interesectionPoint.y - normalizedTouchPoint.y);
if (currentDistance < shortestDistance)
{
shortestDistance = currentDistance;
selectedIndex = lineIndex;
}
}
}
return selectedIndex;
}
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches
- (void)touchesBeganOrMovedWithTouches:(NSSet *)touches
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
if (self.state == JBChartViewStateCollapsed || [self.chartData count] <= 0)
{
return;
}
[self setSelectionViewVisible:NO animated:YES];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
NSInteger index = [self indexForPoint:touchPoint];
if ([self.delegate respondsToSelector:@selector(lineChartView:didUnselectChartAtIndex:)])
CGPoint touchPoint = [self clampPoint:[touch locationInView:self.lineView] toBounds:self.lineView.bounds padding:kJBLineChartLineViewEdgePadding];
if ([self.delegate respondsToSelector:@selector(lineChartView:didSelectLineAtIndex:horizontalIndex:touchPoint:)])
{
[self.delegate lineChartView:self didUnselectChartAtIndex:index];
NSUInteger lineIndex = self.lineView.selectedLineIndex != kJBLineChartLineViewUnselectedLineIndex ? self.lineView.selectedLineIndex : [self lineIndexForPoint:touchPoint];
NSUInteger horizontalIndex = [self horizontalIndexForPoint:touchPoint indexClamp:JBLineChartHorizontalIndexClampNone lineData:[self.chartData objectAtIndex:lineIndex]];
[self.delegate lineChartView:self didSelectLineAtIndex:lineIndex horizontalIndex:horizontalIndex touchPoint:[touch locationInView:self]];
}
if ([self.delegate respondsToSelector:@selector(lineChartView:didSelectLineAtIndex:horizontalIndex:)])
{
NSUInteger lineIndex = self.lineView.selectedLineIndex != kJBLineChartLineViewUnselectedLineIndex ? self.lineView.selectedLineIndex : [self lineIndexForPoint:touchPoint];
[self.delegate lineChartView:self didSelectLineAtIndex:lineIndex horizontalIndex:[self horizontalIndexForPoint:touchPoint indexClamp:JBLineChartHorizontalIndexClampNone lineData:[self.chartData objectAtIndex:lineIndex]]];
}
CGFloat xOffset = fmin(self.bounds.size.width - self.verticalSelectionView.frame.size.width, fmax(0, touchPoint.x - (ceil(self.verticalSelectionView.frame.size.width * 0.5))));
self.verticalSelectionView.frame = CGRectMake(xOffset, self.verticalSelectionView.frame.origin.y, self.verticalSelectionView.frame.size.width, self.verticalSelectionView.frame.size.height);
[self setVerticalSelectionViewVisible:YES animated:YES];
}
- (void)touchesEndedOrCancelledWithTouches:(NSSet *)touches
{
if (self.state == JBChartViewStateCollapsed || [self.chartData count] <= 0)
{
return;
}
[self setVerticalSelectionViewVisible:NO animated:YES];
if ([self.delegate respondsToSelector:@selector(didUnselectLineInLineChartView:)])
{
[self.delegate didUnselectLineInLineChartView:self];
}
[self.lineView setSelectedLineIndex:kJBLineChartLineViewUnselectedLineIndex];
}
#pragma mark - Setters
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible animated:(BOOL)animated
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible animated:(BOOL)animated
{
_selectionViewVisible = selectionViewVisible;
_verticalSelectionViewVisible = verticalSelectionViewVisible;
[self bringSubviewToFront:self.verticalSelectionView];
if (animated)
{
[UIView animateWithDuration:kJBChartViewDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.selectionView.alpha = self.selectionViewVisible ? 1.0 : 0.0;
self.verticalSelectionView.alpha = self.verticalSelectionViewVisible ? 1.0 : 0.0;
} completion:nil];
}
else
{
self.selectionView.alpha = _selectionViewVisible ? 1.0 : 0.0;
self.verticalSelectionView.alpha = _verticalSelectionViewVisible ? 1.0 : 0.0;
}
}
- (void)setSelectionViewVisible:(BOOL)selectionViewVisible
- (void)setVerticalSelectionViewVisible:(BOOL)verticalSelectionViewVisible
{
[self setSelectionViewVisible:selectionViewVisible animated:NO];
[self setVerticalSelectionViewVisible:verticalSelectionViewVisible animated:NO];
}
- (void)setShowsVerticalSelection:(BOOL)showsVerticalSelection
{
_showsVerticalSelection = showsVerticalSelection;
self.verticalSelectionView.hidden = _showsVerticalSelection ? NO : YES;
}
#pragma mark - Gestures
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
{
return;
}
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if ([self.delegate respondsToSelector:@selector(lineChartView:didSelectChartAtIndex:)])
{
[self.delegate lineChartView:self didSelectChartAtIndex:[self indexForPoint:touchPoint]];
}
CGFloat xOffset = fmin(self.bounds.size.width - self.selectionView.frame.size.width, fmax(0, touchPoint.x - (ceil(self.selectionView.frame.size.width * 0.5))));
self.selectionView.frame = CGRectMake(xOffset, self.selectionView.frame.origin.y, self.selectionView.frame.size.width, self.selectionView.frame.size.height);
[self setSelectionViewVisible:YES animated:YES];
CGPoint touchPoint = [self clampPoint:[touch locationInView:self.lineView] toBounds:self.lineView.bounds padding:kJBLineChartLineViewEdgePadding];
[self.lineView setSelectedLineIndex:[self lineIndexForPoint:touchPoint]];
[self touchesBeganOrMovedWithTouches:touches];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.showsSelection || self.state == JBChartViewStateCollapsed)
{
return;
}
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if ([self.delegate respondsToSelector:@selector(lineChartView:didSelectChartAtIndex:)])
{
[self.delegate lineChartView:self didSelectChartAtIndex:[self indexForPoint:touchPoint]];
}
CGFloat xOffset = fmin(self.bounds.size.width - self.selectionView.frame.size.width, fmax(0, touchPoint.x - (ceil(self.selectionView.frame.size.width * 0.5))));
self.selectionView.frame = CGRectMake(xOffset, self.selectionView.frame.origin.y, self.selectionView.frame.size.width, self.selectionView.frame.size.height);
[self setSelectionViewVisible:YES animated:YES];
[self touchesBeganOrMovedWithTouches:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
@@ -426,86 +645,91 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *flatPath = [UIBezierPath bezierPath];
flatPath.miterLimit = kJBLineChartLineViewMiterLimit;
UIBezierPath *dynamicPath = [UIBezierPath bezierPath];
dynamicPath.miterLimit = kJBLineChartLineViewMiterLimit;
NSAssert([self.delegate respondsToSelector:@selector(chartDataForLineChartLineView:)], @"JBLineChartLineView // delegate must implement - (NSArray *)chartDataForLineChartLineView:(JBLineChartLineView *)lineChartLineView");
NSInteger index = 0;
for (JBLineChartPoint *lineChartPoint in [[self.delegate chartDataForLineChartLineView:self] sortedArrayUsingSelector:@selector(compare:)])
NSArray *chartData = [self.delegate chartDataForLineChartLineView:self];
NSUInteger lineIndex = 0;
for (NSArray *lineData in chartData)
{
if (index == 0)
UIBezierPath *flatPath = [UIBezierPath bezierPath];
flatPath.miterLimit = kJBLineChartLineViewMiterLimit;
UIBezierPath *dynamicPath = [UIBezierPath bezierPath];
dynamicPath.miterLimit = kJBLineChartLineViewMiterLimit;
NSUInteger index = 0;
for (JBLineChartPoint *lineChartPoint in [lineData sortedArrayUsingSelector:@selector(compare:)])
{
[dynamicPath moveToPoint:CGPointMake(lineChartPoint.position.x, fmin(self.bounds.size.height - kJBLineChartLineViewEdgePadding, fmax(kJBLineChartLineViewEdgePadding, lineChartPoint.position.y)))];
[flatPath moveToPoint:CGPointMake(lineChartPoint.position.x, ceil(self.bounds.size.height * 0.5))];
if (index == 0)
{
[dynamicPath moveToPoint:CGPointMake(lineChartPoint.position.x, fmin(self.bounds.size.height - kJBLineChartLineViewEdgePadding, fmax(kJBLineChartLineViewEdgePadding, lineChartPoint.position.y)))];
[flatPath moveToPoint:CGPointMake(lineChartPoint.position.x, ceil(self.bounds.size.height * 0.5))];
}
else
{
[dynamicPath addLineToPoint:CGPointMake(lineChartPoint.position.x, fmin(self.bounds.size.height - kJBLineChartLineViewEdgePadding, fmax(kJBLineChartLineViewEdgePadding, lineChartPoint.position.y)))];
[flatPath addLineToPoint:CGPointMake(lineChartPoint.position.x, ceil(self.bounds.size.height * 0.5))];
}
index++;
}
JBLineLayer *shapeLayer = [self lineLayerForLineIndex:lineIndex];
if (shapeLayer == nil)
{
shapeLayer = [JBLineLayer layer];
}
shapeLayer.tag = lineIndex;
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:lineStyleForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (JBLineChartViewLineStyle)lineChartLineView:(JBLineChartLineView *)lineChartLineView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex");
shapeLayer.lineStyle = [self.delegate lineChartLineView:self lineStyleForLineAtLineIndex:lineIndex];
if (self.animated)
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:colorForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView colorForLineAtLineIndex:(NSUInteger)lineIndex");
shapeLayer.strokeColor = [self.delegate lineChartLineView:self colorForLineAtLineIndex:lineIndex].CGColor;
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:widthForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (CGFloat)lineChartLineView:(JBLineChartLineView *)lineChartLineView widthForLineAtLineIndex:(NSUInteger)lineIndex");
shapeLayer.lineWidth = [self.delegate lineChartLineView:self widthForLineAtLineIndex:lineIndex];
shapeLayer.path = (self.state == JBLineChartLineViewStateCollapsed) ? dynamicPath.CGPath : flatPath.CGPath;
shapeLayer.frame = self.bounds;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:kJBLineChartViewAnimationPathKey];
[anim setRemovedOnCompletion:NO];
anim.toValue = self.state == JBLineChartLineViewStateCollapsed ? (id)flatPath.CGPath : (id)dynamicPath.CGPath;
anim.duration = kJBLineChartLineViewStateAnimationDuration;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.autoreverses = NO;
anim.repeatCount = 0;
[shapeLayer addAnimation:anim forKey:kJBLineChartViewAnimationPathKey];
[self.layer addSublayer:shapeLayer];
}
else
{
[dynamicPath addLineToPoint:CGPointMake(lineChartPoint.position.x, fmin(self.bounds.size.height - kJBLineChartLineViewEdgePadding, fmax(kJBLineChartLineViewEdgePadding, lineChartPoint.position.y)))];
[flatPath addLineToPoint:CGPointMake(lineChartPoint.position.x, ceil(self.bounds.size.height * 0.5))];
CGContextSaveGState(context);
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:colorForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView colorForLineAtLineIndex:(NSUInteger)lineIndex");
CGContextSetStrokeColorWithColor(context, [self.delegate lineChartLineView:self colorForLineAtLineIndex:lineIndex].CGColor);
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:widthForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (CGFloat)lineChartLineView:(JBLineChartLineView *)lineChartLineView widthForLineAtLineIndex:(NSUInteger)lineIndex");
CGContextSetLineWidth(context, [self.delegate lineChartLineView:self widthForLineAtLineIndex:lineIndex]);
CGContextBeginPath(context);
CGContextAddPath(context, self.state == JBLineChartLineViewStateCollapsed ? flatPath.CGPath : dynamicPath.CGPath);
CGContextDrawPath(context, kCGPathStroke);
}
CGContextRestoreGState(context);
}
index++;
lineIndex++;
}
NSAssert([self.delegate respondsToSelector:@selector(lineColorForLineChartLineView:)], @"JBLineChartLineView // delegate must implement - (UIColor *)lineColorForLineChartLineView:(JBLineChartLineView*)lineChartLineView");
if (self.shapeLayer == nil)
{
self.shapeLayer = [CAShapeLayer layer];
}
if (self.aniamted)
{
self.shapeLayer.zPosition = 0.0f;
self.shapeLayer.strokeColor = [self.delegate lineColorForLineChartLineView:self].CGColor;
self.shapeLayer.lineWidth = kJBLineChartLineViewStrokeWidth;
self.shapeLayer.lineCap = kCALineCapRound;
self.shapeLayer.lineJoin = kCALineJoinRound;
self.shapeLayer.frame = self.bounds;
self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
if (self.state == JBLineChartLineViewStateCollapsed)
{
self.shapeLayer.path = dynamicPath.CGPath;
}
else
{
self.shapeLayer.path = flatPath.CGPath;
}
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"path"];
[anim setRemovedOnCompletion:NO];
anim.toValue = self.state == JBLineChartLineViewStateCollapsed ? (id)flatPath.CGPath : (id)dynamicPath.CGPath;
anim.duration = kJBLineChartLineViewStateAnimationDuration;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.autoreverses = NO;
anim.repeatCount = 0;
[self.shapeLayer addAnimation:anim forKey:@"path"];
[self.layer addSublayer:self.shapeLayer];
}
else
{
CGContextSaveGState(context);
{
CGContextSetLineWidth(context, kJBLineChartLineViewStrokeWidth);
CGContextSetStrokeColorWithColor(context, [self.delegate lineColorForLineChartLineView:self].CGColor);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextBeginPath(context);
CGContextAddPath(context, self.state == JBLineChartLineViewStateCollapsed ? flatPath.CGPath : dynamicPath.CGPath);
CGContextDrawPath(context, kCGPathStroke);
}
CGContextRestoreGState(context);
}
self.aniamted = NO;
self.animated = NO;
}
#pragma mark - Data
@@ -524,13 +748,13 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
{
return;
}
dispatch_block_t callbackCopy = [callback copy];
_state = state;
self.aniamted = animated;
self.animated = animated;
[self setNeedsDisplay];
if (animated)
{
[self performSelector:@selector(fireCallback:) withObject:callback afterDelay:kJBLineChartLineViewStateAnimationDuration];
@@ -549,18 +773,57 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
[self setState:state animated:animated callback:nil];
}
- (void)setSelectedLineIndex:(NSInteger)selectedLineIndex
{
_selectedLineIndex = selectedLineIndex;
for (CALayer *layer in [self.layer sublayers])
{
if ([layer isKindOfClass:[JBLineLayer class]])
{
if (((JBLineLayer *)layer).tag == _selectedLineIndex)
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:selectedColorForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView selectedColorForLineAtLineIndex:(NSUInteger)lineIndex");
((JBLineLayer *)layer).strokeColor = [self.delegate lineChartLineView:self selectedColorForLineAtLineIndex:((JBLineLayer *)layer).tag].CGColor;
((JBLineLayer *)layer).opacity = 1.0f;
}
else
{
NSAssert([self.delegate respondsToSelector:@selector(lineChartLineView:colorForLineAtLineIndex:)], @"JBLineChartLineView // delegate must implement - (UIColor *)lineChartLineView:(JBLineChartLineView *)lineChartLineView colorForLineAtLineIndex:(NSUInteger)lineIndex");
((JBLineLayer *)layer).strokeColor = [self.delegate lineChartLineView:self colorForLineAtLineIndex:((JBLineLayer *)layer).tag].CGColor;
((JBLineLayer *)layer).opacity = (_selectedLineIndex == kJBLineChartLineViewUnselectedLineIndex) ? 1.0f : kJBLineChartLineViewDefaultDimmedOpacity;
}
}
}
}
#pragma mark - Callback Helpers
- (void)fireCallback:(void (^)())callback
{
dispatch_block_t callbackCopy = [callback copy];
if (callbackCopy != nil)
{
callbackCopy();
}
}
- (JBLineLayer *)lineLayerForLineIndex:(NSUInteger)lineIndex
{
for (CALayer *layer in [self.layer sublayers])
{
if ([layer isKindOfClass:[JBLineLayer class]])
{
if (((JBLineLayer *)layer).tag == lineIndex)
{
return (JBLineLayer *)layer;
}
}
}
return nil;
}
@end
@implementation JBLineChartPoint
@@ -585,3 +848,50 @@ static UIColor *kJBLineChartViewDefaultLineColor = nil;
}
@end
@implementation JBLineLayer
#pragma mark - Alloc/Init
+ (void)initialize
{
if (self == [JBLineLayer class])
{
kJBLineChartLineViewDefaultDashPattern = @[@(3), @(2)];
}
}
- (id)init
{
self = [super init];
if (self)
{
self.zPosition = 0.0f;
self.fillColor = [UIColor clearColor].CGColor;
}
return self;
}
#pragma mark - Setters
- (void)setLineStyle:(JBLineChartViewLineStyle)lineStyle
{
_lineStyle = lineStyle;
if (_lineStyle == JBLineChartViewLineStyleDashed)
{
self.lineCap = kCALineCapButt;
self.lineJoin = kCALineJoinMiter;
self.lineDashPhase = kJBLineChartLineViewDefaultLinePhase;
self.lineDashPattern = kJBLineChartLineViewDefaultDashPattern;
}
else if (_lineStyle == JBLineChartViewLineStyleSolid)
{
self.lineCap = kCALineCapRound;
self.lineJoin = kCALineJoinRound;
self.lineDashPhase = 0.0;
self.lineDashPattern = nil;
}
}
@end
@@ -15,6 +15,7 @@
9B2E531118218CF20079B9D2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B2E531018218CF20079B9D2 /* main.m */; };
9B2E532018218CF30079B9D2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9B2E531F18218CF30079B9D2 /* Images.xcassets */; };
9B2E533F18218D310079B9D2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B2E533E18218D310079B9D2 /* AppDelegate.m */; };
9B4437AD18D7686800682EF0 /* JBChartTooltipTipView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B4437AC18D7686800682EF0 /* JBChartTooltipTipView.m */; };
9B603D3E182C6E79000A76D0 /* JBBaseNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B603D3D182C6E79000A76D0 /* JBBaseNavigationController.m */; };
9B603D47182C7002000A76D0 /* icon-jawbone-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B603D45182C7002000A76D0 /* icon-jawbone-logo.png */; };
9B603D48182C7002000A76D0 /* icon-jawbone-logo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B603D46182C7002000A76D0 /* icon-jawbone-logo@2x.png */; };
@@ -32,10 +33,12 @@
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 */; };
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 */; };
9BEE694618D2789E005D9BA7 /* JBBaseChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEE694518D2789E005D9BA7 /* JBBaseChartViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -55,6 +58,8 @@
9B2E533E18218D310079B9D2 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9B3FBA54182C3B6900CF1C5F /* JBColorConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBColorConstants.h; sourceTree = "<group>"; };
9B3FBA57182C3BFB00CF1C5F /* JBFontConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBFontConstants.h; sourceTree = "<group>"; };
9B4437AB18D7686800682EF0 /* JBChartTooltipTipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartTooltipTipView.h; sourceTree = "<group>"; };
9B4437AC18D7686800682EF0 /* JBChartTooltipTipView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartTooltipTipView.m; sourceTree = "<group>"; };
9B603D3C182C6E79000A76D0 /* JBBaseNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBBaseNavigationController.h; sourceTree = "<group>"; };
9B603D3D182C6E79000A76D0 /* JBBaseNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBBaseNavigationController.m; sourceTree = "<group>"; };
9B603D45182C7002000A76D0 /* icon-jawbone-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-jawbone-logo.png"; sourceTree = "<group>"; };
@@ -81,6 +86,8 @@
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>"; };
9B6A68E01829BED5006DB3BF /* JBLineChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBLineChartViewController.m; 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>"; };
@@ -92,6 +99,8 @@
9BE0B0D3182B16F700232023 /* JBStringConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBStringConstants.h; sourceTree = "<group>"; };
9BEBE9D0183167050046E4A8 /* JBChartInformationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBChartInformationView.h; sourceTree = "<group>"; };
9BEBE9D1183167050046E4A8 /* JBChartInformationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBChartInformationView.m; sourceTree = "<group>"; };
9BEE694418D2789E005D9BA7 /* JBBaseChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBBaseChartViewController.h; sourceTree = "<group>"; };
9BEE694518D2789E005D9BA7 /* JBBaseChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBBaseChartViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -187,6 +196,8 @@
9B603D3B182C6E79000A76D0 /* Base */ = {
isa = PBXGroup;
children = (
9BEE694418D2789E005D9BA7 /* JBBaseChartViewController.h */,
9BEE694518D2789E005D9BA7 /* JBBaseChartViewController.m */,
9B603D3C182C6E79000A76D0 /* JBBaseNavigationController.h */,
9B603D3D182C6E79000A76D0 /* JBBaseNavigationController.m */,
9B603D4C182C7163000A76D0 /* JBBaseTableViewController.h */,
@@ -291,6 +302,10 @@
children = (
9BEBE9D0183167050046E4A8 /* JBChartInformationView.h */,
9BEBE9D1183167050046E4A8 /* JBChartInformationView.m */,
9B4437AB18D7686800682EF0 /* JBChartTooltipTipView.h */,
9B4437AC18D7686800682EF0 /* JBChartTooltipTipView.m */,
9BD57BBA18D13D1A00ACFA52 /* JBChartTooltipView.h */,
9BD57BBB18D13D1A00ACFA52 /* JBChartTooltipView.m */,
);
path = Misc;
sourceTree = "<group>";
@@ -321,7 +336,7 @@
9B2E52F918218CF20079B9D2 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = Jawbone;
};
buildConfigurationList = 9B2E52FC18218CF20079B9D2 /* Build configuration list for PBXProject "JBChartViewDemo" */;
@@ -378,9 +393,12 @@
9BE0B0C7182B161000232023 /* JBChartHeaderView.m in Sources */,
9B603D4E182C7163000A76D0 /* JBBaseTableViewController.m in Sources */,
9B6A68DE1829BE63006DB3BF /* JBBarChartViewController.m in Sources */,
9B4437AD18D7686800682EF0 /* JBChartTooltipTipView.m in Sources */,
9BE0B0AD182AD26400232023 /* JBLineChartView.m in Sources */,
9BEE694618D2789E005D9BA7 /* JBBaseChartViewController.m in Sources */,
9B6A68E11829BED5006DB3BF /* JBLineChartViewController.m in Sources */,
9B2E533F18218D310079B9D2 /* AppDelegate.m in Sources */,
9BD57BBC18D13D1A00ACFA52 /* JBChartTooltipView.m in Sources */,
9B603D4B182C7117000A76D0 /* JBBaseViewController.m in Sources */,
9B0725211829822A0052109B /* JBChartListViewController.m in Sources */,
);
@@ -404,7 +422,6 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -444,7 +461,6 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -21,10 +21,18 @@
#define kJBColorBarChartBarGreen UIColorFromHex(0x34b234)
#define kJBColorBarChartHeaderSeparatorColor UIColorFromHex(0x686868)
#pragma mark - Line Char
#pragma mark - Line Chart
#define kJBColorLineChartControllerBackground UIColorFromHex(0xb7e3e4)
#define kJBColorLineChartBackground UIColorFromHex(0xb7e3e4)
#define kJBColorLineChartHeader UIColorFromHex(0x1c474e)
#define kJBColorLineChartHeaderSeparatorColor UIColorFromHex(0x8eb6b7)
#define kJBColorLineChartLineColor [UIColor colorWithWhite:1.0 alpha:0.5]
#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 kJBColorLineChartDefaultDashedSelectedLineColor [UIColor colorWithWhite:1.0 alpha:1.0]
#pragma mark - Tooltips
#define kJBColorTooltipColor [UIColor colorWithWhite:1.0 alpha:0.9]
#define kJBColorTooltipTextColor UIColorFromHex(0x313131)
@@ -21,3 +21,7 @@
#define kJBFontInformationTitle [UIFont fontWithName:@"HelveticaNeue" size:20]
#define kJBFontInformationValue [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:100]
#define kJBFontInformationUnit [UIFont fontWithName:@"HelveticaNeue" size:60]
#pragma mark - Tooltip
#define kJBFontTooltipText [UIFont fontWithName:@"HelveticaNeue-Bold" size:14]
@@ -10,11 +10,20 @@
#pragma mark - Labels
#define kJBStringLabel1987 localize(@"label.1985", @"1987")
#pragma mark - Labels (Bar Chart)
#define kJBStringLabel2012 localize(@"label.2012", @"2012")
#define kJBStringLabelAverageMonthlyTemperature localize(@"label.average.monthly.temperature", @"Average Monthly Temperature")
#define kJBStringLabelWorldwide2012 localize(@"label.worldwide.2013", @"Worldwide - 2012")
#define kJBStringLabelWorldwideAverage localize(@"label.worldwide.average", @"Worldwide Average")
#define kJBStringLabelDegreesFahrenheit localize(@"label.degrees.fahrenheit", @"%d%@F")
#define kJBStringLabelDegreeSymbol localize(@"label.degree.symbol", @"\u00B0")
#pragma mark - Labels (Line Chart)
#define kJBStringLabel2013 localize(@"label.2013", @"2013")
#define kJBStringLabeJanuary localize(@"label.january", @"January")
#define kJBStringLabelDecember localize(@"label.august", @"December")
#define kJBStringLabelAverageMonthlyRainfall localize(@"label.annual.monthly.rainfall", @"Average Monthly Rainfall")
#define kJBStringLabelAverageAnnualRainfall localize(@"label.average.annual.rainfall", @"Average Annual Rainfall")
#define kJBStringLabelSanFrancisco localize(@"label.san.francisco", @"San Francisco")
#define kJBStringLabelSanFrancisco2013 localize(@"label.san.francisco.2013", @"San Francisco - 2013")
#define kJBStringLabelAverageDailyRainfall localize(@"label.average.daily.rainfall", @"Average Daily Rainfall")
#define kJBStringLabelMm localize(@"label.mm", @"mm")
#define kJBStringLabelMetropolitanAverage localize(@"label.metropolitan.average", @"Metropolitan Average")
#define kJBStringLabelNationalAverage localize(@"label.national.average", @"National Average")
@@ -6,11 +6,6 @@
// Copyright (c) 2013 Jawbone. All rights reserved.
//
#pragma mark - Numerics
#define kJBNumericDefaultPadding 10.0f
#define kJBNumericDefaultAnimationDuration 0.25f
#pragma mark - Images
#define kJBImageIconJawboneLogo @"icon-jawbone-logo.png"
@@ -0,0 +1,27 @@
//
// JBBaseChartViewController.h
// JBChartViewDemo
//
// Created by Terry Worona on 3/13/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import "JBBaseViewController.h"
// Views
#import "JBChartTooltipView.h"
#import "JBChartView.h"
@interface JBBaseChartViewController : JBBaseViewController
@property (nonatomic, strong, readonly) JBChartTooltipView *tooltipView;
@property (nonatomic, assign) BOOL tooltipVisible;
// Settres
- (void)setTooltipVisible:(BOOL)tooltipVisible animated:(BOOL)animated atTouchPoint:(CGPoint)touchPoint;
- (void)setTooltipVisible:(BOOL)tooltipVisible animated:(BOOL)animated;
// Getters
- (JBChartView *)chartView; // subclasses to return chart instance for tooltip functionality
@end
@@ -0,0 +1,130 @@
//
// JBBaseChartViewController.m
// JBChartViewDemo
//
// Created by Terry Worona on 3/13/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import "JBBaseChartViewController.h"
// Views
#import "JBChartTooltipTipView.h"
// Numerics
CGFloat const kJBBaseChartViewControllerAnimationDuration = 0.25f;
@interface JBBaseChartViewController ()
@property (nonatomic, strong) JBChartTooltipView *tooltipView;
@property (nonatomic, strong) JBChartTooltipTipView *tooltipTipView;
@end
@implementation JBBaseChartViewController
#pragma mark - Setters
- (void)setTooltipVisible:(BOOL)tooltipVisible animated:(BOOL)animated atTouchPoint:(CGPoint)touchPoint
{
_tooltipVisible = tooltipVisible;
JBChartView *chartView = [self chartView];
if (!chartView)
{
return;
}
if (!self.tooltipView)
{
self.tooltipView = [[JBChartTooltipView alloc] init];
self.tooltipView.alpha = 0.0;
[self.view addSubview:self.tooltipView];
}
if (!self.tooltipTipView)
{
self.tooltipTipView = [[JBChartTooltipTipView alloc] init];
self.tooltipTipView.alpha = 0.0;
[self.view addSubview:self.tooltipTipView];
}
dispatch_block_t adjustTooltipPosition = ^{
CGPoint originalTouchPoint = [self.view convertPoint:touchPoint fromView:chartView];
CGPoint convertedTouchPoint = originalTouchPoint; // modified
JBChartView *chartView = [self chartView];
if (chartView)
{
CGFloat minChartX = (chartView.frame.origin.x + ceil(self.tooltipView.frame.size.width * 0.5));
if (convertedTouchPoint.x < minChartX)
{
convertedTouchPoint.x = minChartX;
}
CGFloat maxChartX = (chartView.frame.origin.x + chartView.frame.size.width - ceil(self.tooltipView.frame.size.width * 0.5));
if (convertedTouchPoint.x > maxChartX)
{
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);
if (originalTouchPoint.x < minTipX)
{
originalTouchPoint.x = minTipX;
}
CGFloat maxTipX = (chartView.frame.origin.x + chartView.frame.size.width - self.tooltipTipView.frame.size.width);
if (originalTouchPoint.x > maxTipX)
{
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();
}
}];
}
else
{
adjustTooltipVisibility();
}
}
- (void)setTooltipVisible:(BOOL)tooltipVisible animated:(BOOL)animated
{
[self setTooltipVisible:tooltipVisible animated:animated atTouchPoint:CGPointZero];
}
- (void)setTooltipVisible:(BOOL)tooltipVisible
{
[self setTooltipVisible:tooltipVisible animated:NO];
}
#pragma mark - Getters
- (JBChartView *)chartView
{
// Subclasses should return chart instance for tooltip functionality
return nil;
}
@end
@@ -20,6 +20,7 @@
self.navigationBar.translucent = NO;
[[UINavigationBar appearance] setBarTintColor:kJBColorNavigationTint];
[[UINavigationBar appearance] setTintColor:kJBColorNavigationBarTint];
self.interactivePopGestureRecognizer.enabled = NO;
}
return self;
}
@@ -6,8 +6,8 @@
// Copyright (c) 2013 Jawbone. All rights reserved.
//
#import "JBBaseViewController.h"
#import "JBBaseChartViewController.h"
@interface JBBarChartViewController : JBBaseViewController
@interface JBBarChartViewController : JBBaseChartViewController
@end
@@ -16,14 +16,15 @@
// Numerics
CGFloat const kJBBarChartViewControllerChartHeight = 250.0f;
CGFloat const kJBBarChartViewControllerChartPadding = 10.0f;
CGFloat const kJBBarChartViewControllerChartHeaderHeight = 80.0f;
CGFloat const kJBBarChartViewControllerChartHeaderPadding = 10.0f;
CGFloat const kJBBarChartViewControllerChartFooterHeight = 25.0f;
CGFloat const kJBBarChartViewControllerChartFooterPadding = 5.0f;
CGFloat const kJBBarChartViewControllerBarPadding = 1;
NSInteger const kJBBarChartViewControllerNumBars = 12;
NSInteger const kJBBarChartViewControllerMaxBarHeight = 100; // max random value
NSInteger const kJBBarChartViewControllerMinBarHeight = 20;
NSInteger const kJBBarChartViewControllerMaxBarHeight = 10;
NSInteger const kJBBarChartViewControllerMinBarHeight = 5;
// Strings
NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
@@ -52,8 +53,27 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
self = [super init];
if (self)
{
[self initFakeData]; // fake rain data
_monthlySymbols = [[[NSDateFormatter alloc] init] monthSymbols];
[self initFakeData];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
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;
}
@@ -65,9 +85,12 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
NSMutableArray *mutableChartData = [NSMutableArray array];
for (int i=0; i<kJBBarChartViewControllerNumBars; i++)
{
[mutableChartData addObject:[NSNumber numberWithFloat:MAX(kJBBarChartViewControllerMinBarHeight, arc4random() % kJBBarChartViewControllerMaxBarHeight)]]; // fake height
NSInteger delta = (kJBBarChartViewControllerNumBars - abs((kJBBarChartViewControllerNumBars - i) - i)) + 2;
[mutableChartData addObject:[NSNumber numberWithFloat:MAX((delta * kJBBarChartViewControllerMinBarHeight), arc4random() % (delta * kJBBarChartViewControllerMaxBarHeight))]];
}
_chartData = [NSArray arrayWithArray:mutableChartData];
_monthlySymbols = [[[NSDateFormatter alloc] init] shortMonthSymbols];
}
#pragma mark - View Lifecycle
@@ -79,23 +102,24 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
self.view.backgroundColor = kJBColorBarChartControllerBackground;
self.navigationItem.rightBarButtonItem = [self chartToggleButtonWithTarget:self action:@selector(chartToggleButtonPressed:)];
self.barChartView = [[JBBarChartView alloc] initWithFrame:CGRectMake(kJBNumericDefaultPadding, kJBNumericDefaultPadding, self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBBarChartViewControllerChartHeight)];
self.barChartView = [[JBBarChartView alloc] init];
self.barChartView.frame = CGRectMake(kJBBarChartViewControllerChartPadding, kJBBarChartViewControllerChartPadding, self.view.bounds.size.width - (kJBBarChartViewControllerChartPadding * 2), kJBBarChartViewControllerChartHeight);
self.barChartView.delegate = self;
self.barChartView.dataSource = self;
self.barChartView.headerPadding = kJBBarChartViewControllerChartHeaderPadding;
self.barChartView.backgroundColor = kJBColorBarChartBackground;
JBChartHeaderView *headerView = [[JBChartHeaderView alloc] initWithFrame:CGRectMake(kJBNumericDefaultPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBBarChartViewControllerChartHeaderHeight * 0.5), self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBBarChartViewControllerChartHeaderHeight)];
headerView.titleLabel.text = [kJBStringLabelAverageMonthlyRainfall uppercaseString];
headerView.subtitleLabel.text = [NSString stringWithFormat:@"%@ - %@", kJBStringLabelSanFrancisco, kJBStringLabel2013];
JBChartHeaderView *headerView = [[JBChartHeaderView alloc] initWithFrame:CGRectMake(kJBBarChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBBarChartViewControllerChartHeaderHeight * 0.5), self.view.bounds.size.width - (kJBBarChartViewControllerChartPadding * 2), kJBBarChartViewControllerChartHeaderHeight)];
headerView.titleLabel.text = [kJBStringLabelAverageMonthlyTemperature uppercaseString];
headerView.subtitleLabel.text = kJBStringLabel2012;
headerView.separatorColor = kJBColorBarChartHeaderSeparatorColor;
self.barChartView.headerView = headerView;
JBBarChartFooterView *footerView = [[JBBarChartFooterView alloc] initWithFrame:CGRectMake(kJBNumericDefaultPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBBarChartViewControllerChartFooterHeight * 0.5), self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBBarChartViewControllerChartFooterHeight)];
JBBarChartFooterView *footerView = [[JBBarChartFooterView alloc] initWithFrame:CGRectMake(kJBBarChartViewControllerChartPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBBarChartViewControllerChartFooterHeight * 0.5), self.view.bounds.size.width - (kJBBarChartViewControllerChartPadding * 2), kJBBarChartViewControllerChartFooterHeight)];
footerView.padding = kJBBarChartViewControllerChartFooterPadding;
footerView.leftLabel.text = [kJBStringLabeJanuary uppercaseString];
footerView.leftLabel.text = [[self.monthlySymbols firstObject] uppercaseString];
footerView.leftLabel.textColor = [UIColor whiteColor];
footerView.rightLabel.text = [kJBStringLabelDecember uppercaseString];
footerView.rightLabel.text = [[self.monthlySymbols lastObject] uppercaseString];
footerView.rightLabel.textColor = [UIColor whiteColor];
self.barChartView.footerView = footerView;
@@ -120,46 +144,49 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
#pragma mark - JBBarChartViewDelegate
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSInteger)index
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtAtIndex:(NSUInteger)index
{
return [[self.chartData objectAtIndex:index] floatValue];
}
#pragma mark - JBBarChartViewDataSource
- (NSInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView
- (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView
{
return kJBBarChartViewControllerNumBars;
}
- (NSInteger)barPaddingForBarChartView:(JBBarChartView *)barChartView
- (NSUInteger)barPaddingForBarChartView:(JBBarChartView *)barChartView
{
return kJBBarChartViewControllerBarPadding;
}
- (UIView *)barViewForBarChartView:(JBBarChartView *)barChartView atIndex:(NSInteger)index
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index
{
UIView *barView = [[UIView alloc] init];
barView.backgroundColor = (index % 2 == 0) ? kJBColorBarChartBarBlue : kJBColorBarChartBarGreen;
return barView;
}
- (UIColor *)selectionBarColorForBarChartView:(JBBarChartView *)barChartView
- (UIColor *)barSelectionColorForBarChartView:(JBBarChartView *)barChartView
{
return [UIColor whiteColor];
}
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSInteger)index
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint
{
NSNumber *valueNumber = [self.chartData objectAtIndex:index];
[self.informationView setValueText:[NSString stringWithFormat:@"%d", [valueNumber intValue]] unitText:kJBStringLabelMm];
[self.informationView setTitleText:[self.monthlySymbols objectAtIndex:index]];
[self.informationView setValueText:[NSString stringWithFormat:kJBStringLabelDegreesFahrenheit, [valueNumber intValue], kJBStringLabelDegreeSymbol] unitText:nil];
[self.informationView setTitleText:kJBStringLabelWorldwideAverage];
[self.informationView setHidden:NO animated:YES];
[self setTooltipVisible:YES animated:YES atTouchPoint:touchPoint];
[self.tooltipView setText:[[self.monthlySymbols objectAtIndex:index] uppercaseString]];
}
- (void)barChartView:(JBBarChartView *)barChartView didUnselectBarAtIndex:(NSInteger)index
- (void)didUnselectBarChartView:(JBBarChartView *)barChartView
{
[self.informationView setHidden:YES animated:YES];
[self setTooltipVisible:NO animated:YES];
}
#pragma mark - Buttons
@@ -177,4 +204,11 @@ NSString * const kJBBarChartViewControllerNavButtonViewKey = @"view";
}];
}
#pragma mark - Overrides
- (JBChartView *)chartView
{
return self.barChartView;
}
@end
@@ -51,8 +51,8 @@ NSInteger const kJBChartListViewControllerCellHeight = 100;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JBChartTableCell *cell = [tableView dequeueReusableCellWithIdentifier:kJBChartListViewControllerCellIdentifier forIndexPath:indexPath];
cell.textLabel.text = indexPath.row == JBChartListViewControllerRowLineChart ? kJBStringLabelAverageAnnualRainfall : kJBStringLabelAverageMonthlyRainfall;
cell.detailTextLabel.text = kJBStringLabelSanFrancisco;
cell.textLabel.text = indexPath.row == JBChartListViewControllerRowLineChart ? kJBStringLabelAverageDailyRainfall : kJBStringLabelAverageMonthlyTemperature;
cell.detailTextLabel.text = indexPath.row == JBChartListViewControllerRowLineChart ? kJBStringLabelSanFrancisco2013 : kJBStringLabelWorldwide2012;
cell.type = indexPath.row == JBChartListViewControllerRowLineChart ? JBChartTableCellTypeLineChart : JBChartTableCellTypeBarChart;
return cell;
}
@@ -6,8 +6,8 @@
// Copyright (c) 2013 Jawbone. All rights reserved.
//
#import "JBBaseViewController.h"
#import "JBBaseChartViewController.h"
@interface JBLineChartViewController : JBBaseViewController
@interface JBLineChartViewController : JBBaseChartViewController
@end
@@ -16,12 +16,21 @@
#define ARC4RANDOM_MAX 0x100000000
typedef NS_ENUM(NSInteger, JBLineChartLine){
JBLineChartLineSolid,
JBLineChartLineDashed,
JBLineChartLineCount
};
// Numerics
CGFloat const kJBLineChartViewControllerChartHeight = 250.0f;
CGFloat const kJBLineChartViewControllerChartPadding = 10.0f;
CGFloat const kJBLineChartViewControllerChartHeaderHeight = 75.0f;
CGFloat const kJBLineChartViewControllerChartHeaderPadding = 20.0f;
CGFloat const kJBLineChartViewControllerChartFooterHeight = 20.0f;
NSInteger const kJBLineChartViewControllerNumChartPoints = 27;
CGFloat const kJBLineChartViewControllerChartSolidLineWidth = 6.0f;
CGFloat const kJBLineChartViewControllerChartDashedLineWidth = 2.0f;
NSInteger const kJBLineChartViewControllerMaxNumChartPoints = 7;
// Strings
NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
@@ -31,12 +40,14 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
@property (nonatomic, strong) JBLineChartView *lineChartView;
@property (nonatomic, strong) JBChartInformationView *informationView;
@property (nonatomic, strong) NSArray *chartData;
@property (nonatomic, strong) NSArray *daysOfWeek;
// Buttons
- (void)chartToggleButtonPressed:(id)sender;
// Helpers
- (void)initFakeData;
- (NSArray *)largestLineData; // largest collection of fake line data
@end
@@ -49,7 +60,27 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
self = [super init];
if (self)
{
[self initFakeData]; // fake rain data
[self initFakeData];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
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;
}
@@ -58,12 +89,31 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
- (void)initFakeData
{
NSMutableArray *mutableChartData = [NSMutableArray array];
for (int i=0; i<kJBLineChartViewControllerNumChartPoints; i++)
NSMutableArray *mutableLineCharts = [NSMutableArray array];
for (int lineIndex=0; lineIndex<JBLineChartLineCount; lineIndex++)
{
[mutableChartData addObject:[NSNumber numberWithFloat:((double)arc4random() / ARC4RANDOM_MAX)]]; // random number between 0 and 1
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: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;
}
#pragma mark - View Lifecycle
@@ -75,36 +125,37 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
self.view.backgroundColor = kJBColorLineChartControllerBackground;
self.navigationItem.rightBarButtonItem = [self chartToggleButtonWithTarget:self action:@selector(chartToggleButtonPressed:)];
self.lineChartView = [[JBLineChartView alloc] initWithFrame:CGRectMake(kJBNumericDefaultPadding, kJBNumericDefaultPadding, self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBLineChartViewControllerChartHeight)];
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(kJBNumericDefaultPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartHeaderHeight * 0.5), self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBLineChartViewControllerChartHeaderHeight)];
headerView.titleLabel.text = [kJBStringLabelAverageAnnualRainfall uppercaseString];
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 = [kJBStringLabelSanFrancisco uppercaseString];
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(kJBNumericDefaultPadding, ceil(self.view.bounds.size.height * 0.5) - ceil(kJBLineChartViewControllerChartFooterHeight * 0.5), self.view.bounds.size.width - (kJBNumericDefaultPadding * 2), kJBLineChartViewControllerChartFooterHeight)];
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 = kJBStringLabel1987;
footerView.leftLabel.text = [[self.daysOfWeek firstObject] uppercaseString];
footerView.leftLabel.textColor = [UIColor whiteColor];
footerView.rightLabel.text = kJBStringLabel2013;
footerView.rightLabel.text = [[self.daysOfWeek lastObject] uppercaseString];;
footerView.rightLabel.textColor = [UIColor whiteColor];
footerView.sectionCount = kJBLineChartViewControllerNumChartPoints;
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)) layout:JBChartInformationViewLayoutVertical];
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];
@@ -128,41 +179,64 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
#pragma mark - JBLineChartViewDelegate
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView heightForIndex:(NSInteger)index
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
{
return [[self.chartData objectAtIndex:index] floatValue];
return [[[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex] floatValue];
}
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectChartAtIndex:(NSInteger)index
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint
{
NSNumber *valueNumber = [self.chartData objectAtIndex:index];
NSNumber *valueNumber = [[self.chartData objectAtIndex:lineIndex] objectAtIndex:horizontalIndex];
[self.informationView setValueText:[NSString stringWithFormat:@"%.2f", [valueNumber floatValue]] unitText:kJBStringLabelMm];
[self.informationView setTitleText:[NSString stringWithFormat:@"%d", [kJBStringLabel1987 intValue] + (int)index]];
[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)lineChartView:(JBLineChartView *)lineChartView didUnselectChartAtIndex:(NSInteger)index
- (void)didUnselectLineInLineChartView:(JBLineChartView *)lineChartView
{
[self.informationView setHidden:YES animated:YES];
[self setTooltipVisible:NO animated:YES];
}
#pragma mark - JBLineChartViewDataSource
- (NSInteger)numberOfPointsInLineChartView:(JBLineChartView *)lineChartView
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView
{
return [self.chartData count];
}
- (UIColor *)lineColorForLineChartView:(JBLineChartView *)lineChartView
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex
{
return kJBColorLineChartLineColor;
return [[self.chartData objectAtIndex:lineIndex] count];
}
- (UIColor *)selectionColorForLineChartView:(JBLineChartView *)lineChartView
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex
{
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidLineColor: kJBColorLineChartDefaultDashedLineColor;
}
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView widthForLineAtLineIndex:(NSUInteger)lineIndex
{
return (lineIndex == JBLineChartLineSolid) ? kJBLineChartViewControllerChartSolidLineWidth: kJBLineChartViewControllerChartDashedLineWidth;
}
- (UIColor *)verticalSelectionColorForLineChartView:(JBLineChartView *)lineChartView
{
return [UIColor whiteColor];
}
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex
{
return (lineIndex == JBLineChartLineSolid) ? kJBColorLineChartDefaultSolidSelectedLineColor: kJBColorLineChartDefaultDashedSelectedLineColor;
}
- (JBLineChartViewLineStyle)lineChartView:(JBLineChartView *)lineChartView lineStyleForLineAtLineIndex:(NSUInteger)lineIndex
{
return (lineIndex == JBLineChartLineSolid) ? JBLineChartViewLineStyleSolid : JBLineChartViewLineStyleDashed;
}
#pragma mark - Buttons
- (void)chartToggleButtonPressed:(id)sender
@@ -178,4 +252,11 @@ NSString * const kJBLineChartViewControllerNavButtonViewKey = @"view";
}];
}
#pragma mark - Overrides
- (JBChartView *)chartView
{
return self.lineChartView;
}
@end
@@ -8,19 +8,12 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, JBChartInformationViewLayout){
JBChartInformationViewLayoutHorizontal, // default
JBChartInformationViewLayoutVertical
};
@interface JBChartInformationView : UIView
/*
* View must be initialized with a layout type (default = horizontal)
*/
- (id)initWithFrame:(CGRect)frame layout:(JBChartInformationViewLayout)layout;
@property (nonatomic, assign, readonly) JBChartInformationViewLayout layout; // read-only (must be set in init..)
- (id)initWithFrame:(CGRect)frame;
// Content
- (void)setTitleText:(NSString *)titleText;
@@ -10,9 +10,10 @@
// Numerics
CGFloat const kJBChartValueViewPadding = 10.0f;
CGFloat const kJBChartValueViewSeparatorSize = 1.0f;
CGFloat const kJBChartValueViewSeparatorSize = 0.5f;
CGFloat const kJBChartValueViewTitleHeight = 50.0f;
CGFloat const kJBChartValueViewTitleWidth = 75.0f;
CGFloat const kJBChartValueViewDefaultAnimationDuration = 0.25f;
// Colors (JBChartInformationView)
static UIColor *kJBChartViewSeparatorColor = nil;
@@ -58,13 +59,12 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
}
}
- (id)initWithFrame:(CGRect)frame layout:(JBChartInformationViewLayout)layout
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.clipsToBounds = YES;
_layout = layout;
_titleLabel = [[UILabel alloc] init];
_titleLabel.font = kJBFontInformationTitle;
@@ -74,7 +74,7 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
_titleLabel.textColor = kJBChartViewTitleColor;
_titleLabel.shadowColor = kJBChartViewShadowColor;
_titleLabel.shadowOffset = CGSizeMake(0, 1);
_titleLabel.textAlignment = _layout == JBChartInformationViewLayoutHorizontal ? NSTextAlignmentLeft : NSTextAlignmentCenter;
_titleLabel.textAlignment = NSTextAlignmentLeft;
[self addSubview:_titleLabel];
_separatorView = [[UIView alloc] init];
@@ -89,20 +89,15 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
return self;
}
- (id)initWithFrame:(CGRect)frame
{
return [self initWithFrame:frame layout:JBChartInformationViewLayoutHorizontal];
}
#pragma mark - Position
- (CGRect)valueViewRect
{
CGRect valueRect = CGRectZero;
valueRect.origin.x = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewPadding : (kJBChartValueViewPadding * 3) + kJBChartValueViewTitleWidth;
valueRect.origin.y = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewPadding + kJBChartValueViewTitleHeight : kJBChartValueViewPadding;
valueRect.size.width = (self.layout == JBChartInformationViewLayoutHorizontal) ? self.bounds.size.width - (kJBChartValueViewPadding * 2) : self.bounds.size.width - valueRect.origin.x - kJBChartValueViewPadding;
valueRect.size.height = (self.layout == JBChartInformationViewLayoutHorizontal) ? self.bounds.size.height - valueRect.origin.y - kJBChartValueViewPadding : self.bounds.size.height - (kJBChartValueViewPadding * 2);
valueRect.origin.x = kJBChartValueViewPadding;
valueRect.origin.y = kJBChartValueViewPadding + kJBChartValueViewTitleHeight;
valueRect.size.width = self.bounds.size.width - (kJBChartValueViewPadding * 2);
valueRect.size.height = self.bounds.size.height - valueRect.origin.y - kJBChartValueViewPadding;
return valueRect;
}
@@ -111,28 +106,21 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
CGRect titleRect = CGRectZero;
titleRect.origin.x = kJBChartValueViewPadding;
titleRect.origin.y = hidden ? -kJBChartValueViewTitleHeight : kJBChartValueViewPadding;
titleRect.size.width = (self.layout == JBChartInformationViewLayoutHorizontal) ? self.bounds.size.width - (kJBChartValueViewPadding * 2) : kJBChartValueViewTitleWidth;
titleRect.size.height = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewTitleHeight : self.bounds.size.height - (kJBChartValueViewPadding * 2);
titleRect.size.width = self.bounds.size.width - (kJBChartValueViewPadding * 2);
titleRect.size.height = kJBChartValueViewTitleHeight;
return titleRect;
}
- (CGRect)separatorViewRectForHidden:(BOOL)hidden
{
CGRect separatorRect = CGRectZero;
separatorRect.origin.x = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewPadding : (kJBChartValueViewPadding * 2) + kJBChartValueViewTitleWidth;
separatorRect.origin.y = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewTitleHeight : kJBChartValueViewPadding;
separatorRect.size.width = (self.layout == JBChartInformationViewLayoutHorizontal) ? self.bounds.size.width - (kJBChartValueViewPadding * 2) : kJBChartValueViewSeparatorSize;
separatorRect.size.height = (self.layout == JBChartInformationViewLayoutHorizontal) ? kJBChartValueViewSeparatorSize : self.bounds.size.height - (kJBChartValueViewPadding * 2);
separatorRect.origin.x = kJBChartValueViewPadding;
separatorRect.origin.y = kJBChartValueViewTitleHeight;
separatorRect.size.width = self.bounds.size.width - (kJBChartValueViewPadding * 2);
separatorRect.size.height = kJBChartValueViewSeparatorSize;
if (hidden)
{
if (self.layout == JBChartInformationViewLayoutHorizontal)
{
separatorRect.origin.x -= self.bounds.size.width;
}
else
{
separatorRect.origin.y = self.bounds.size.height;
}
separatorRect.origin.x -= self.bounds.size.width;
}
return separatorRect;
}
@@ -185,7 +173,7 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
{
if (hidden)
{
[UIView animateWithDuration:kJBNumericDefaultAnimationDuration * 0.5 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[UIView animateWithDuration:kJBChartValueViewDefaultAnimationDuration * 0.5 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.titleLabel.alpha = 0.0;
self.separatorView.alpha = 0.0;
self.valueView.valueLabel.alpha = 0.0;
@@ -197,7 +185,7 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
}
else
{
[UIView animateWithDuration:kJBNumericDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[UIView animateWithDuration:kJBChartValueViewDefaultAnimationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.titleLabel.frame = [self titleViewRectForHidden:NO];
self.titleLabel.alpha = 1.0;
self.valueView.valueLabel.alpha = 1.0;
@@ -273,14 +261,13 @@ static UIColor *kJBChartInformationViewShadowColor = nil;
- (void)layoutSubviews
{
CGFloat xOffset = kJBChartValueViewPadding;
CGFloat width = ceil((self.bounds.size.width - (kJBChartValueViewPadding * 2)) * 0.5);
CGSize valueLabelSize = [self.valueLabel.text sizeWithAttributes:@{NSFontAttributeName:self.valueLabel.font}];
self.valueLabel.frame = CGRectMake(xOffset, ceil(self.bounds.size.height * 0.5) - ceil(valueLabelSize.height * 0.5), width, valueLabelSize.height);
CGSize unitLabelSize = [self.unitLabel.text sizeWithAttributes:@{NSFontAttributeName:self.unitLabel.font}];
self.unitLabel.frame = CGRectMake(CGRectGetMaxX(self.valueLabel.frame), ceil(self.bounds.size.height * 0.5) - ceil(unitLabelSize.height * 0.5) + kJBChartValueViewPadding + 3, width, unitLabelSize.height);
CGFloat xOffset = ceil((self.bounds.size.width - (valueLabelSize.width + unitLabelSize.width)) * 0.5);
self.valueLabel.frame = CGRectMake(xOffset, ceil(self.bounds.size.height * 0.5) - ceil(valueLabelSize.height * 0.5), valueLabelSize.width, valueLabelSize.height);
self.unitLabel.frame = CGRectMake(CGRectGetMaxX(self.valueLabel.frame), ceil(self.bounds.size.height * 0.5) - ceil(unitLabelSize.height * 0.5) + kJBChartValueViewPadding + 3, unitLabelSize.width, unitLabelSize.height);
}
@end
@@ -0,0 +1,13 @@
//
// JBChartTooltipTipView.h
// JBChartViewDemo
//
// Created by Terry Worona on 3/17/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface JBChartTooltipTipView : UIView
@end
@@ -0,0 +1,52 @@
//
// JBChartTooltipTipView.m
// JBChartViewDemo
//
// Created by Terry Worona on 3/17/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import "JBChartTooltipTipView.h"
// Numerics
CGFloat const kJBChartTooltipTipViewDefaultWidth = 8.0f;
CGFloat const kJBChartTooltipTipViewDefaultHeight = 5.0f;
@implementation JBChartTooltipTipView
#pragma mark - Alloc/Init
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, kJBChartTooltipTipViewDefaultWidth, kJBChartTooltipTipViewDefaultHeight)];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor clearColor] set];
CGContextFillRect(context, rect);
CGContextSaveGState(context);
{
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGContextClosePath(context);
CGContextSetFillColorWithColor(context, kJBColorTooltipColor.CGColor);
CGContextFillPath(context);
}
CGContextRestoreGState(context);
}
@end
@@ -0,0 +1,15 @@
//
// JBChartTooltipView.h
// JBChartViewDemo
//
// Created by Terry Worona on 3/12/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface JBChartTooltipView : UIView
- (void)setText:(NSString *)text;
@end
@@ -0,0 +1,71 @@
//
// JBChartTooltipView.m
// JBChartViewDemo
//
// Created by Terry Worona on 3/12/14.
// Copyright (c) 2014 Jawbone. All rights reserved.
//
#import "JBChartTooltipView.h"
// Drawing
#import <QuartzCore/QuartzCore.h>
// Numerics
CGFloat static const kJBChartTooltipViewCornerRadius = 5.0;
CGFloat const kJBChartTooltipViewDefaultWidth = 50.0f;
CGFloat const kJBChartTooltipViewDefaultHeight = 25.0f;
@interface JBChartTooltipView ()
@property (nonatomic, strong) UILabel *textLabel;
@end
@implementation JBChartTooltipView
#pragma mark - Alloc/Init
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, kJBChartTooltipViewDefaultWidth, kJBChartTooltipViewDefaultHeight)];
if (self)
{
self.backgroundColor = kJBColorTooltipColor;
self.layer.cornerRadius = kJBChartTooltipViewCornerRadius;
_textLabel = [[UILabel alloc] init];
_textLabel.font = kJBFontTooltipText;
_textLabel.backgroundColor = [UIColor clearColor];
_textLabel.textColor = kJBColorTooltipTextColor;
_textLabel.adjustsFontSizeToFitWidth = YES;
_textLabel.numberOfLines = 1;
_textLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_textLabel];
}
return self;
}
#pragma mark - Setters
- (void)setText:(NSString *)text
{
self.textLabel.text = text;
[self setNeedsLayout];
}
- (void)setTooltipColor:(UIColor *)tooltipColor
{
self.backgroundColor = tooltipColor;
[self setNeedsDisplay];
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
_textLabel.frame = self.bounds;
}
@end
+59 -28
View File
@@ -1,10 +1,10 @@
# JBChartView
<center>
<br/>
<p align="center">
<img src="https://raw.github.com/Jawbone/JBChartView/master/Screenshots/main.png">
</center>
</p>
<b>Introducing JBChartView - </b> Jawbone's iOS-based charting library for both line and bar graphs. It is easy to set-up, and highly customizable.
Introducing <b>JBChartView - </b> Jawbone's iOS-based charting library for both line and bar graphs. It is easy to set-up, and highly customizable.
## Features
@@ -36,7 +36,7 @@ Simply add the following line to your <code>Podfile</code>:
Your Podfile should look something like:
platform :ios, '7.0'
pod 'JBChartView', '~> 1.1.5'
pod 'JBChartView', '~> 2.0.1'
### The Old School Way
@@ -48,11 +48,11 @@ The simpliest way to use JBChartView with your application is to drag and drop t
## Usage
Both JBChartView implementations have a similiar data source and delgate pattern to <i>UITableView</i>. If you're familiar with using a <i>UITableView</i> or <i>UITableViewController</i>, using a JBChartView subclass should be a breeze!
All JBChartView implementations have a similiar data source and delgate pattern to <i>UITableView</i>. If you're familiar with using a <i>UITableView</i> or <i>UITableViewController</i>, using a JBChartView subclass should be a breeze!
#### JBBarChartView
To initialize a <i>JBBarChartView</i>, you only need a few lines of code:
To initialize a <i>JBBarChartView</i>, you only need a few lines of code (see below). Bar charts can also be initialized via a <b>nib</b> or with a <b>frame</b>.
JBBarChartView *barChartView = [[JBBarChartView alloc] init];
barChartView.delegate = self;
@@ -72,29 +72,45 @@ Secondly, you need to inform the delegate the height of each bar (automatically
{
return ...; // height of bar at index
}
Lastly, ensure you have set the *frame* of your barChartView & call *reloadData* at least once:
barChartView.frame = CGRectMake( ... );
[barChartView reloadData];
#### JBLineChartView
Similiarily, to initialize a JBLineChartView, you only need a few lines of code:
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>.
JBLineChartView *lineChartView = [[JBLineChartView alloc] init];
lineChartView.delegate = self;
lineChartView.dataSource = self;
[self addSubview:lineChartView];
At a minimum, you need to inform the data source how many points are in the line chart:
At a minimum, you need to inform the data source how many lines and vertical data points (for each line) are in the chart:
- (NSInteger)numberOfPointsInLineChartView:(JBLineChartView *)lineChartView
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView
{
return ...; // number of points in chart
return ...; // number of lines in chart
}
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex
{
return ...; // number of values for a line
}
Secondly, you need to inform the delegate the y-position of each point (automatically normalized across the entire chart):
Secondly, you need to inform the delegate of the y-position of each point (automatically normalized across the entire chart) for each line in the chart:
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView heightForIndex:(NSInteger)index
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
{
return ...; // y-position of poinnt at index (x-axis)
return ...; // y-position (y-axis) of point at horizontalIndex (x-axis)
}
Lastly, ensure you have set the *frame* of your lineChartView & call *reloadData* at least once:
lineChartView.frame = CGRectMake( ... );
[lineChartView reloadData];
## Customization
@@ -118,61 +134,76 @@ Lastly, any JBChartView subclass can be collapsed or expanded programmatically v
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 *)barViewForBarChartView:(JBBarChartView *)barChartView atIndex:(NSInteger)index
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index
{
return ...; // color of line in chart
}
Furthermore, the color of the selection bar (on touch events) can be customized via the <i>optional</i> protocol:
- (UIColor *)selectionBarColorForBarChartView:(JBBarChartView *)barChartView
- (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:(NSInteger)index
- (void)barChartView:(JBBarChartView *)barChartView didSelectBarAtIndex:(NSUInteger)index touchPoint:(CGPoint)touchPoint
{
// Update view
}
- (void)barChartView:(JBBarChartView *)barChartView didUnselectBarAtIndex:(NSInteger)index
- (void)didUnselectBarChartView:(JBBarChartView *)barChartView
{
// Update view
}
A JBBarChartView visuaul overview can be found <a href="https://raw.github.com/Jawbone/JBChartView/master/Screenshots/JBBarChartView.png" target="_blank">here</a>.
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 of the chart's line can be customized via the <i>optional</i> protocol:
The color, width and style of each line in the chart can be customized via the <i>optional</i> protocol:
- (UIColor *)lineColorForLineChartView:(JBLineChartView *)lineChartView
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView colorForLineAtLineIndex:(NSUInteger)lineIndex
{
return ...; // color of line in chart
}
Furthermore, the color of the selection bar (on touch events) can be customized via the <i>optional</i> protocol:
- (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 of the selection bar and line can be customized via the <i>optional</i> protocols:
- (UIColor *)selectionColorForLineChartView:(JBLineChartView *)lineChartView
- (UIColor *)verticalSelectionColorForLineChartView:(JBLineChartView *)lineChartView
{
return ...; // color of selection view
}
- (UIColor *)lineChartView:(JBLineChartView *)lineChartView selectionColorForLineAtLineIndex:(NSUInteger)lineIndex
{
return ...; // color of selected line
}
Lastly, a line chart's selection events are delegated back via:
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectChartAtIndex:(NSInteger)index
- (void)lineChartView:(JBLineChartView *)lineChartView didSelectLineAtIndex:(NSUInteger)lineIndex horizontalIndex:(NSUInteger)horizontalIndex touchPoint:(CGPoint)touchPoint
{
// Update view
}
- (void)lineChartView:(JBLineChartView *)lineChartView didUnselectChartAtIndex:(NSInteger)index
- (void)didUnselectLineInLineChartView:(JBLineChartView *)lineChartView
{
// Update view
}
A JBLineChartView visuaul overview can be found <a href="https://raw.github.com/Jawbone/JBChartView/master/Screenshots/JBLineChartView.png" target="_blank">here</a>.
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.
## License
Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 157 KiB