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:
Is it possible in anyway to achieve this result? And in case, how?
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.
Two UIViews.
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With