UILabel: цвет, зависящий от фона
Я попытался найти решения для этой проблемы, но я даже не могу правильно это выразить словами, я думаю.
В основном у меня есть панель, заполняемая цветом при продолжении операции. У меня есть метка с процентом прогресса, который имеет тот же цвет, имеет цвет заливки, поэтому мне нужно, чтобы он менялся, когда цвет заливки находится на обратной стороне. Что-то вроде этого:
![enter image description here]()
Можно ли в любом случае добиться этого результата? А в случае, как?
Ответы
Ответ 1
Самый простой способ - создать подкласс UIView
, который имеет свойство progress
и перезаписывает -drawRect:
.
Весь код, который вам нужен, следующий:
- (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];
}
Вы можете расширить класс по мере необходимости с помощью свойств для цвета фона, цвета текста, шрифта и т.д.
Ответ 2
Два UIViews.
-
Позвольте называть один фон, а другой - progressBar. progressBar укладывается поверх фона с тем же самым происхождением в своем общем просмотре.
-
Оба они имеют UILabel как подпункт, и обе метки одинаковы с родителями. background имеет темный backgroundColor, и на нем есть светлый textColor, и в представлении прогресса все происходит наоборот.
-
progressBar имеет более узкую ширину кадра, чем фоновый, и имеет клипы для следующих целей: YES:
Трюк заключается в том, что исходное изображение выглядит так же, как и исходные метки, и клипы на верхнем уровне, все будет выглядеть правильно.
Отбросьте эти два представления в новый подкласс UIView под названием ReallyCoolProgressView и дайте ему один общедоступный метод:
-(void)setProgress:(float)progress
прогресс - это число от 0.0 до 1.0. Метод масштабирует ширину progressBar и устанавливает оба этикета: "Progress% f", прогресс * 100
Ответ 3
Просто идея "подделать" желаемый эффект.
Вы можете создать подкласс UIView с одним ярлыком фона (белый и синий текст) и подвью перед ним с голубым цветом фона и белым текстом.
Вы можете оживить после ширины передней метки от 0 до 100% на ярлыке фона. Возможно, вам придется проверить, нужно ли вам указывать метку внутри подзаголовка, чтобы избежать смещения текста при увеличении ширины.
Ответ 4
Чтобы упростить без переопределения drawRect:
, вы можете создать 2 UIViews, чтобы обойти это.
Синий фон UIView (A) содержит белый UILabel. (Clipping subviews включается)
Белый фон UIView (B) содержит синий UILabel.
A будет накладываться на B.
Примечание: 2 UILabels будут иметь одинаковые размеры, одинаковые шрифты и одинаковые позиции.
Регулируя ширину UIView A, вы создадите рабочую панель, как хотите.
Ответ 5
Свифт 4 версии Мортена:
class ProgressLabel: UIView {
var progressBarColor = UIColor(red:108.0/255.0, green:200.0/255.0, blue:226.0/255.0, alpha:1.0)
var textColor = UIColor.white
var font = UIFont.boldSystemFont(ofSize: 42)
var progress: Float = 0 {
didSet {
progress = Float.minimum(100.0, Float.maximum(progress, 0.0))
self.setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
// Set up environment.
let size = self.bounds.size
// Prepare progress as a string.
let progressMessage = NSString(format:"%d %%", Int(progress))
var attributes: [NSAttributedStringKey:Any] = [ NSAttributedStringKey.font : font ]
let textSize = progressMessage.size(withAttributes: attributes)
let progressX = ceil(CGFloat(progress) / 100 * size.width)
let textPoint = CGPoint(x: ceil((size.width - textSize.width) / 2.0), y: ceil((size.height - textSize.height) / 2.0))
// Draw background + foreground text
progressBarColor.setFill()
context.fill(self.bounds)
attributes[NSAttributedStringKey.foregroundColor] = textColor
progressMessage.draw(at: textPoint, withAttributes: attributes)
// Clip the drawing that follows to the remaining progress' frame.
context.saveGState()
let remainingProgressRect = CGRect(x: progressX, y: 0.0, width: size.width - progressX, height: size.height)
context.addRect(remainingProgressRect)
context.clip()
// Draw again with inverted colors.
textColor.setFill()
context.fill(self.bounds)
attributes[NSAttributedStringKey.foregroundColor] = progressBarColor
progressMessage.draw(at: textPoint, withAttributes: attributes)
context.restoreGState()
}
}
Ответ 6
Лучший и самый чистый способ сделать это - использовать маски.
Иерархия представлений вашего индикатора выполнения должна выглядеть так:
- Base
-
Subview at index #1
Просмотр показаний прогресса
-
Subview at index #2
UILabel
с цветом текста так же, как базовый
-
Subview at index #3
UILabel
с цветом текста, аналогичным показанному на рисунке.
Мы будем применять маску к #3
. Мы начинаем применять маску, как только представление, показывающее прогресс, достигнет кадра #3
. Рамка маски должна следить за ходом. Когда #3
постепенно маскируется, ярлык внизу раскрывается, и вы достигаете эффекта красиво и с небольшим усилием.
Здесь вы создаете маски с CALayers.