Advertisement
Guest User

Untitled

a guest
Feb 6th, 2016
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #import "SHLineGraphView.h"
  2. #import "PopoverView.h"
  3. #import "SHPlot.h"
  4. #import <math.h>
  5. #import <objc/runtime.h>
  6.  
  7. #define BOTTOM_MARGIN_TO_LEAVE 30.0
  8. #define TOP_MARGIN_TO_LEAVE 30.0
  9. #define INTERVAL_COUNT 9
  10. #define PLOT_WIDTH (self.bounds.size.width - _leftMarginToLeave)
  11.  
  12. #define kAssociatedPlotObject @"kAssociatedPlotObject"
  13.  
  14.  
  15. @implementation SHLineGraphView
  16. {
  17.     float _leftMarginToLeave;
  18. }
  19. - (instancetype)init {
  20.     if((self = [super init])) {
  21.         [self loadDefaultTheme];
  22.     }
  23.     return self;
  24. }
  25.  
  26. - (void)awakeFromNib
  27. {
  28.     [self loadDefaultTheme];
  29. }
  30.  
  31. - (id)initWithFrame:(CGRect)frame
  32. {
  33.     self = [super initWithFrame:frame];
  34.     if (self) {
  35.         [self loadDefaultTheme];
  36.     }
  37.     return self;
  38. }
  39.  
  40. - (void)loadDefaultTheme {
  41.     _themeAttributes = @{
  42.                          kXAxisLabelColorKey : [UIColor colorWithRed:0.48 green:0.48 blue:0.49 alpha:0.4],
  43.                          kXAxisLabelFontKey : [UIFont fontWithName:@"TrebuchetMS" size:10],
  44.                          kYAxisLabelColorKey : [UIColor colorWithRed:0.48 green:0.48 blue:0.49 alpha:0.4],
  45.                          kYAxisLabelFontKey : [UIFont fontWithName:@"TrebuchetMS" size:10],
  46.                          kYAxisLabelSideMarginsKey : @10,
  47.                          kPlotBackgroundLineColorKey : [UIColor colorWithRed:0.48 green:0.48 blue:0.49 alpha:0.4],
  48.                          kDotSizeKey : @10.0
  49.                          };
  50. }
  51.  
  52. - (void)addPlot:(SHPlot *)newPlot;
  53. {
  54.     if(nil == newPlot) {
  55.         return;
  56.     }
  57.    
  58.     if(_plots == nil){
  59.         _plots = [NSMutableArray array];
  60.     }
  61.     [_plots addObject:newPlot];
  62. }
  63.  
  64. - (void)setupTheView
  65. {
  66.     for(SHPlot *plot in _plots) {
  67.         [self drawPlotWithPlot:plot];
  68.     }
  69. }
  70.  
  71. #pragma mark - Actual Plot Drawing Methods
  72.  
  73. - (void)drawPlotWithPlot:(SHPlot *)plot {
  74.     //draw y-axis labels. this has to be done first, so that we can determine the left margin to leave according to the
  75.     //y-axis lables.
  76.     [self drawYLabels:plot];
  77.    
  78.     //draw x-labels
  79.     [self drawXLabels:plot];
  80.    
  81.     //draw the grey lines
  82.     [self drawLines:plot];
  83.    
  84.     /*
  85.      actual plot drawing
  86.      */
  87.     [self drawPlot:plot];
  88. }
  89.  
  90. - (int)getIndexForValue:(NSNumber *)value forPlot:(SHPlot *)plot {
  91.     for(int i=0; i< _xAxisValues.count; i++) {
  92.         NSDictionary *d = [_xAxisValues objectAtIndex:i];
  93.         __block BOOL foundValue = NO;
  94.         [d enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
  95.             NSNumber *k = (NSNumber *)key;
  96.             if([k doubleValue] == [value doubleValue]) {
  97.                 foundValue = YES;
  98.                 *stop = foundValue;
  99.             }
  100.         }];
  101.         if(foundValue){
  102.             return i;
  103.         }
  104.     }
  105.     return -1;
  106. }
  107.  
  108. - (void)drawPlot:(SHPlot *)plot {
  109.    
  110.     NSDictionary *theme = plot.plotThemeAttributes;
  111.    
  112.     //
  113.     CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
  114.     backgroundLayer.frame = self.bounds;
  115.     backgroundLayer.fillColor = ((UIColor *)theme[kPlotFillColorKey]).CGColor;
  116.     backgroundLayer.backgroundColor = [UIColor clearColor].CGColor;
  117.     [backgroundLayer setStrokeColor:[UIColor clearColor].CGColor];
  118.     [backgroundLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
  119.    
  120.     CGMutablePathRef backgroundPath = CGPathCreateMutable();
  121.    
  122.     //
  123.     CAShapeLayer *circleLayer = [CAShapeLayer layer];
  124.     circleLayer.frame = self.bounds;
  125.     circleLayer.fillColor = ((UIColor *)theme[kPlotPointFillColorKey]).CGColor;
  126.     circleLayer.backgroundColor = [UIColor clearColor].CGColor;
  127.     [circleLayer setStrokeColor:((UIColor *)theme[kPlotPointFillColorKey]).CGColor];
  128.     [circleLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
  129.    
  130.     CGMutablePathRef circlePath = CGPathCreateMutable();
  131.    
  132.     //
  133.     CAShapeLayer *graphLayer = [CAShapeLayer layer];
  134.     graphLayer.frame = self.bounds;
  135.     graphLayer.fillColor = [UIColor clearColor].CGColor;
  136.     graphLayer.backgroundColor = [UIColor clearColor].CGColor;
  137.     [graphLayer setStrokeColor:((UIColor *)theme[kPlotStrokeColorKey]).CGColor];
  138.     [graphLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
  139.    
  140.     CGMutablePathRef graphPath = CGPathCreateMutable();
  141.    
  142.     double yRange = [_yAxisRange doubleValue]; // this value will be in dollars
  143.     double yIntervalValue = yRange / INTERVAL_COUNT;
  144.    
  145.     //logic to fill the graph path, ciricle path, background path.
  146.     [plot.plottingValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  147.         NSDictionary *dic = (NSDictionary *)obj;
  148.        
  149.         __block NSNumber *_key = nil;
  150.         __block NSNumber *_value = nil;
  151.        
  152.         [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
  153.             _key = (NSNumber *)key;
  154.             _value = (NSNumber *)obj;
  155.         }];
  156.        
  157.         int xIndex = [self getIndexForValue:_key forPlot:plot];
  158.        
  159.         //x value
  160.         double height = self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE;
  161.         double y = height - ((height / ([_yAxisRange doubleValue] + yIntervalValue)) * [_value doubleValue]);
  162.         (plot.xPoints[xIndex]).x = ceil((plot.xPoints[xIndex]).x);
  163.         (plot.xPoints[xIndex]).y = ceil(y);
  164.     }];
  165.    
  166.     //move to initial point for path and background.
  167.     CGPathMoveToPoint(graphPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
  168.     CGPathMoveToPoint(backgroundPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
  169.    
  170.     int count = _xAxisValues.count;
  171.     for(int i=0; i< count; i++){
  172.         CGPoint point = plot.xPoints[i];
  173.         CGPathAddLineToPoint(graphPath, NULL, point.x, point.y);
  174.         CGPathAddLineToPoint(backgroundPath, NULL, point.x, point.y);
  175.         CGFloat dotsSize = [_themeAttributes[kDotSizeKey] floatValue];
  176.         CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(point.x - dotsSize/2.0f, point.y - dotsSize/2.0f, dotsSize, dotsSize));
  177.     }
  178.    
  179.     //move to initial point for path and background.
  180.     CGPathAddLineToPoint(graphPath, NULL, _leftMarginToLeave + PLOT_WIDTH, plot.xPoints[count -1].y);
  181.     CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, plot.xPoints[count - 1].y);
  182.    
  183.     //additional points for background.
  184.     CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
  185.     CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
  186.     CGPathCloseSubpath(backgroundPath);
  187.    
  188.     backgroundLayer.path = backgroundPath;
  189.     graphLayer.path = graphPath;
  190.     circleLayer.path = circlePath;
  191.    
  192.     //animation
  193.     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  194.     animation.duration = 1;
  195.     animation.fromValue = @(0.0);
  196.     animation.toValue = @(1.0);
  197.     [graphLayer addAnimation:animation forKey:@"strokeEnd"];
  198.    
  199.     backgroundLayer.zPosition = 0;
  200.     graphLayer.zPosition = 1;
  201.     circleLayer.zPosition = 2;
  202.    
  203.     [self.layer addSublayer:graphLayer];
  204.     [self.layer addSublayer:circleLayer];
  205.     [self.layer addSublayer:backgroundLayer];
  206.    
  207.     NSUInteger count2 = _xAxisValues.count;
  208.     for(int i=0; i< count2; i++){
  209.         CGPoint point = plot.xPoints[i];
  210.         UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
  211.        
  212.         btn.backgroundColor = [UIColor clearColor];
  213.         btn.tag = i;
  214.         btn.frame = CGRectMake(point.x - 20, point.y - 20, 40, 40);
  215.         [btn addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
  216.         objc_setAssociatedObject(btn, kAssociatedPlotObject, plot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  217.        
  218.         [self addSubview:btn];
  219.     }
  220. }
  221.  
  222. - (void)drawXLabels:(SHPlot *)plot {
  223.     int xIntervalCount = _xAxisValues.count;
  224.     double xIntervalInPx = PLOT_WIDTH / _xAxisValues.count;
  225.    
  226.     //initialize actual x points values where the circle will be
  227.     plot.xPoints = calloc(sizeof(CGPoint), xIntervalCount);
  228.    
  229.     for(int i=0; i < xIntervalCount; i++){
  230.         CGPoint currentLabelPoint = CGPointMake((xIntervalInPx * i) + _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
  231.         CGRect xLabelFrame = CGRectMake(currentLabelPoint.x , currentLabelPoint.y, xIntervalInPx, BOTTOM_MARGIN_TO_LEAVE);
  232.        
  233.         plot.xPoints[i] = CGPointMake((int) xLabelFrame.origin.x + (xLabelFrame.size.width /2) , (int) 0);
  234.        
  235.         UILabel *xAxisLabel = [[UILabel alloc] initWithFrame:xLabelFrame];
  236.         xAxisLabel.backgroundColor = [UIColor clearColor];
  237.         xAxisLabel.font = (UIFont *)_themeAttributes[kXAxisLabelFontKey];
  238.        
  239.         xAxisLabel.textColor = (UIColor *)_themeAttributes[kXAxisLabelColorKey];
  240.         xAxisLabel.textAlignment = NSTextAlignmentCenter;
  241.        
  242.         NSDictionary *dic = [_xAxisValues objectAtIndex:i];
  243.         __block NSString *xLabel = nil;
  244.         [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
  245.             xLabel = (NSString *)obj;
  246.         }];
  247.        
  248.         xAxisLabel.text = [NSString stringWithFormat:@"%@", xLabel];
  249.         [self addSubview:xAxisLabel];
  250.     }
  251. }
  252.  
  253. - (void)drawYLabels:(SHPlot *)plot {
  254.     double yRange = [_yAxisRange doubleValue]; // this value will be in dollars
  255.     double yIntervalValue = yRange / INTERVAL_COUNT;
  256.     double intervalInPx = (self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE ) / (INTERVAL_COUNT +1);
  257.    
  258.     NSMutableArray *labelArray = [NSMutableArray array];
  259.     float maxWidth = 0;
  260.    
  261.     for(int i= INTERVAL_COUNT + 1; i >= 0; i--){
  262.         CGPoint currentLinePoint = CGPointMake(_leftMarginToLeave, i * intervalInPx);
  263.         CGRect lableFrame = CGRectMake(0, currentLinePoint.y - (intervalInPx / 2), 100, intervalInPx);
  264.        
  265.         if(i != 0) {
  266.             UILabel *yAxisLabel = [[UILabel alloc] initWithFrame:lableFrame];
  267.             yAxisLabel.backgroundColor = [UIColor clearColor];
  268.             yAxisLabel.font = (UIFont *)_themeAttributes[kYAxisLabelFontKey];
  269.             yAxisLabel.textColor = (UIColor *)_themeAttributes[kYAxisLabelColorKey];
  270.             yAxisLabel.textAlignment = NSTextAlignmentCenter;
  271.             float val = (yIntervalValue * (10 - i));
  272.             if(val > 0){
  273.                 yAxisLabel.text = [NSString stringWithFormat:@"%.1f%@", val, _yAxisSuffix];
  274.             } else {
  275.                 yAxisLabel.text = [NSString stringWithFormat:@"%.0f", val];
  276.             }
  277.             [yAxisLabel sizeToFit];
  278.             CGRect newLabelFrame = CGRectMake(0, currentLinePoint.y - (yAxisLabel.layer.frame.size.height / 2), yAxisLabel.frame.size.width, yAxisLabel.layer.frame.size.height);
  279.             yAxisLabel.frame = newLabelFrame;
  280.            
  281.             if(newLabelFrame.size.width > maxWidth) {
  282.                 maxWidth = newLabelFrame.size.width;
  283.             }
  284.            
  285.             [labelArray addObject:yAxisLabel];
  286.             [self addSubview:yAxisLabel];
  287.         }
  288.     }
  289.    
  290.     _leftMarginToLeave = maxWidth + [_themeAttributes[kYAxisLabelSideMarginsKey] doubleValue];
  291.    
  292.     for( UILabel *l in labelArray) {
  293.         CGSize newSize = CGSizeMake(_leftMarginToLeave, l.frame.size.height);
  294.         CGRect newFrame = l.frame;
  295.         newFrame.size = newSize;
  296.         l.frame = newFrame;
  297.     }
  298. }
  299.  
  300. - (void)drawLines:(SHPlot *)plot {
  301.    
  302.     CAShapeLayer *linesLayer = [CAShapeLayer layer];
  303.     linesLayer.frame = self.bounds;
  304.     linesLayer.fillColor = [UIColor clearColor].CGColor;
  305.     linesLayer.backgroundColor = [UIColor clearColor].CGColor;
  306.     linesLayer.strokeColor = ((UIColor *)_themeAttributes[kPlotBackgroundLineColorKey]).CGColor;
  307.     linesLayer.lineWidth = 1;
  308.    
  309.     CGMutablePathRef linesPath = CGPathCreateMutable();
  310.    
  311.     double intervalInPx = (self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE) / (INTERVAL_COUNT + 1);
  312.     for(int i= INTERVAL_COUNT + 1; i > 0; i--){
  313.        
  314.         CGPoint currentLinePoint = CGPointMake(_leftMarginToLeave, (i * intervalInPx));
  315.        
  316.         CGPathMoveToPoint(linesPath, NULL, currentLinePoint.x, currentLinePoint.y);
  317.         CGPathAddLineToPoint(linesPath, NULL, currentLinePoint.x + PLOT_WIDTH, currentLinePoint.y);
  318.     }
  319.    
  320.     linesLayer.path = linesPath;
  321.     [self.layer addSublayer:linesLayer];
  322. }
  323.  
  324. #pragma mark - UIButton event methods
  325.  
  326. - (void)clicked:(id)sender
  327. {
  328.     @try {
  329.         UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 120, 30)];
  330.         lbl.backgroundColor = [UIColor clearColor];
  331.         UIButton *btn = (UIButton *)sender;
  332.         NSUInteger tag = btn.tag;
  333.        
  334.         SHPlot *_plot = objc_getAssociatedObject(btn, kAssociatedPlotObject);
  335.         NSString *text = [_plot.plottingPointsLabels objectAtIndex:tag];
  336.        
  337.         lbl.text = text;
  338.         lbl.textColor = [UIColor whiteColor];
  339.         lbl.textAlignment = NSTextAlignmentCenter;
  340.         lbl.font = (UIFont *)_plot.plotThemeAttributes[kPlotPointValueFontKey];
  341.         [lbl sizeToFit];
  342.         lbl.frame = CGRectMake(0, 0, lbl.frame.size.width + 5, lbl.frame.size.height);
  343.        
  344.         CGPoint point =((UIButton *)sender).center;
  345.         point.y -= 15;
  346.        
  347.         dispatch_async(dispatch_get_main_queue(), ^{
  348.             [PopoverView showPopoverAtPoint:point
  349.                                      inView:self
  350.                             withContentView:lbl
  351.                                    delegate:nil];
  352.         });
  353.     }
  354.     @catch (NSException *exception) {
  355.         NSLog(@"plotting label is not available for this point");
  356.     }
  357. }
  358.  
  359. #pragma mark - Theme Key Extern Keys
  360.  
  361. NSString *const kXAxisLabelColorKey         = @"kXAxisLabelColorKey";
  362. NSString *const kXAxisLabelFontKey          = @"kXAxisLabelFontKey";
  363. NSString *const kYAxisLabelColorKey         = @"kYAxisLabelColorKey";
  364. NSString *const kYAxisLabelFontKey          = @"kYAxisLabelFontKey";
  365. NSString *const kYAxisLabelSideMarginsKey   = @"kYAxisLabelSideMarginsKey";
  366. NSString *const kPlotBackgroundLineColorKey = @"kPlotBackgroundLineColorKey";
  367. NSString *const kDotSizeKey                 = @"kDotSizeKey";
  368.  
  369. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement