Search code examples
iosanimationuiviewuibezierpathuicolor

Transition from Green to Red UIColor within a defined pixel range


I've got this horizontal bar (rectangular area) that represents a voltage from 0 to 5V. If, for example, the voltage is 2V, the bar fills up to the 2V mark with the color green.

From 0-2.1V, the color of the area representing the voltage should be green. From 2.9-5V, the color should be red.

I want to make a color transition from 2.1-2.9 that goes from green to red. I've tried one solution that I got from another StackOverflow topic, but I don't like the results because there are too many colors in the spectrum that don't look like they belong.

Here is that solution (the 80 is for the range 2.90 - 2.10 = .80):

- (void)updateLayerProperties {
    CGRect barRect = self.bounds;
    barRect.size.width = (self.bounds.size.width * self.value)/3.5;
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:barRect];
    self.barLayer.path = path.CGPath;
    if (self.value >= 1.30 && self.value <= 1.70) {
        self.barLayer.fillColor = [UIColor colorWithRed:((255 * (self.value - 1.00)) / 80)
                                                  green:((255 * (80 - (self.value - 1.00))) / 80)
                                                   blue:0
                                                  alpha:1.0].CGColor;
    } else {
        self.barLayer.fillColor = (self.value >= self.threshold) ? self.fullColor.CGColor : self.emptyColor.CGColor;
    }
    self.layer.borderWidth = self.borderWidth;
    self.layer.borderColor = self.borderColor.CGColor;
    self.layer.cornerRadius = 2.0f;
    self.layer.masksToBounds = YES;
}

Originally, I just had 0-2.5V is green, 2.5-5V is red...but this looks bad to eye, so I'm trying this out.

Is there some type of animation to use instead of calculating colors based on value of voltage? Assuming the transition gap is the 2.1 - 2.9, the transition should know how to display the best colors between Green and Red. Thanks.

Bar image, with value > 2.9


Solution

  • This should get you started:


    //
    //  GradientBarView.h
    //
    //  Created by Don Mag on 4/5/18.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface GradientBarView : UIView
    
    @end
    

    //
    //  GradientBarView.m
    //
    //  Created by Don Mag on 4/5/18.
    //
    
    #import "GradientBarView.h"
    
    @interface GradientBarView ()
    @property (strong, nonatomic) CAGradientLayer *gradLayer;
    @end
    
    @implementation GradientBarView
    
    - (id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self commonInit];
        }
        return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
            [self commonInit];
        }
        return self;
    }
    
    
    - (void)commonInit {
    
        // instantiate the gradient layer
        _gradLayer = [CAGradientLayer new];
    
        // initial size
        _gradLayer.frame = self.bounds;
    
        // 4 colors, so we can have solid - gradient - solid
        _gradLayer.colors = @[
                              (__bridge id)[UIColor greenColor].CGColor,
                              (__bridge id)[UIColor greenColor].CGColor,
                              (__bridge id)[UIColor redColor].CGColor,
                              (__bridge id)[UIColor redColor].CGColor,
                              ];
    
        // 0.0 -> 2.1 should be green
        // 2.1 -> 2.9 should be a green-to-red gradient
        // 2.9 -> 5.0 should be red
    
        _gradLayer.locations = @[
                                 @(0.0),
                                 @(2.1 / 5.0),
                                 @(2.9 / 5.0),
                                 @(1.0)
                                 ];
    
        // make it horizontal
        _gradLayer.startPoint = CGPointMake(0.0, 0.5);
        _gradLayer.endPoint = CGPointMake(1.0, 0.5);
    
        // add the gradient layer
        [self.layer addSublayer:_gradLayer];
    
    }
    
    - (void)layoutSubviews {
        _gradLayer.frame = self.bounds;
    }
    
    @end
    

    //
    //  GradBarViewController.h
    //
    //  Created by Don Mag on 4/5/18.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface GradBarViewController : UIViewController
    
    @end
    

    //
    //  GradBarViewController.m
    //
    //  Created by Don Mag on 4/5/18.
    //
    
    #import "GradBarViewController.h"
    #import "GradientBarView.h"
    
    @interface GradBarViewController ()
    @property (strong, nonatomic) GradientBarView *gradBarView;
    @end
    
    @implementation GradBarViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    
        // instantiate a GradientBarView
        _gradBarView = [GradientBarView new];
    
        // we'll use autolayout
        _gradBarView.translatesAutoresizingMaskIntoConstraints = NO;
    
        // add the view
        [self.view addSubview:_gradBarView];
    
        // center a 300 x 40 view
    
        /* center horizontally to superview */
        [NSLayoutConstraint constraintWithItem:_gradBarView
                                     attribute:NSLayoutAttributeCenterX
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:self.view
                                     attribute: NSLayoutAttributeCenterX
                                    multiplier:1.0
                                      constant:0].active = YES;
    
        /* center vertically to superview */
        [NSLayoutConstraint constraintWithItem:_gradBarView
                                     attribute:NSLayoutAttributeCenterY
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:self.view
                                     attribute: NSLayoutAttributeCenterY
                                    multiplier:1.0
                                      constant:0].active = YES;
    
        /* Fixed width */
        [NSLayoutConstraint constraintWithItem:_gradBarView
                                     attribute:NSLayoutAttributeWidth
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:1.0
                                      constant:300.0].active = YES;
        /* Fixed Height */
        [NSLayoutConstraint constraintWithItem:_gradBarView
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:1.0
                                      constant:40.0].active = YES;
    
    }
    
    @end
    

    Result (click the image to see it full-size):

    enter image description here

    I'll leave it up to you to look up how to mask the view so you're only showing X-number of points from the left.