Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UILabel: background dependent color

I have tried to search for solutions for this problem, but I am not even able to put it correctly in words I guess.

Basically I have a bar that gets filled up with a color while an operation proceeds. I have a label with the progress percentage that has the same color has the fill color, so I need it to change when the fill color is on the back. Something like this:

enter image description here

Is it possible in anyway to achieve this result? And in case, how?

like image 927
Nicola Miotto Avatar asked Nov 25 '13 07:11

Nicola Miotto


3 Answers

The easiest way is to create a UIView subclass that has a progress property and overwrites -drawRect:.

All the code you need is this:

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Set up environment.
    CGSize size = [self bounds].size;
    UIColor *backgroundColor = [UIColor colorWithRed:108.0/255.0 green:200.0/255.0 blue:226.0/255.0 alpha:1.0];
    UIColor *foregroundColor = [UIColor whiteColor];
    UIFont *font = [UIFont boldSystemFontOfSize:42.0];

    // Prepare progress as a string.
    NSString *progress = [NSString stringWithFormat:@"%d%%", (int)round([self progress] * 100)];
    NSMutableDictionary *attributes = [@{ NSFontAttributeName : font } mutableCopy];
    CGSize textSize = [progress sizeWithAttributes:attributes];
    CGFloat progressX = ceil([self progress] * size.width);
    CGPoint textPoint = CGPointMake(ceil((size.width - textSize.width) / 2.0), ceil((size.height - textSize.height) / 2.0));

    // Draw background + foreground text
    [backgroundColor setFill];
    CGContextFillRect(context, [self bounds]);
    attributes[NSForegroundColorAttributeName] = foregroundColor;
    [progress drawAtPoint:textPoint withAttributes:attributes];

    // Clip the drawing that follows to the remaining progress' frame.
    CGContextSaveGState(context);
    CGRect remainingProgressRect = CGRectMake(progressX, 0.0, size.width - progressX, size.height);
    CGContextAddRect(context, remainingProgressRect);
    CGContextClip(context);

    // Draw again with inverted colors.
    [foregroundColor setFill];
    CGContextFillRect(context, [self bounds]);
    attributes[NSForegroundColorAttributeName] = backgroundColor;
    [progress drawAtPoint:textPoint withAttributes:attributes];

    CGContextRestoreGState(context);
}

- (void)setProgress:(CGFloat)progress {
    _progress = fminf(1.0, fmaxf(progress, 0.0));
    [self setNeedsDisplay];
}

You can expand the class as needed with properties for background color, text color, font, etc.

like image 115
Morten Fast Avatar answered Oct 17 '22 17:10

Morten Fast


Two UIViews.

  1. Let's call one the background and the other the progressBar. progressBar is stacked on top of background with the same origin on their common superview.

  2. They both have a UILabel as subview, and both labels at the same origin relative to their parent. background has a dark backgroundColor and it's label has light textColor and the progress view has things the other way around.

  3. progressBar has a narrower frame width than background and has clipsToBounds==YES

The trick is, with the views' origins the same and the labels' origins the same, and clipsToBounds on the top view, everything is going to look right.

Drop those two views into a new UIView subclass called ReallyCoolProgressView, and give it one public method:

-(void)setProgress:(float)progress 

progress is a number from 0.0 to 1.0. The method scales the progressBar width and sets both label's text @"Progress %f", progress*100

like image 41
Pradhyuman sinh Avatar answered Oct 17 '22 16:10

Pradhyuman sinh


Just an idea of "faking" the effect you want. You may create a subclass of UIView with one background label (white and blue text) and a subview in front of it with blue background color and white text. You may animate after the width of the front label from 0 to 100% on the background label. You may have to check if you need to pu the label inside a subview to avoid displacement of the text while increasing width.

like image 2
slecorne Avatar answered Oct 17 '22 16:10

slecorne