2013-11-25 5 views
20

Я попытался найти решения для этой проблемы, но я даже не могу выразить это правильно словами, которые я предполагаю.UILabel: цвет, зависящий от фона

В основном у меня есть полоска, заполняемая цветом во время операции. У меня есть метка с процентом прогресса, который имеет тот же цвет, имеет цвет заливки, поэтому мне нужно, чтобы он менялся, когда цвет заливки находится на обратной стороне. Что-то вроде этого:

enter image description here

Можно в любом случае, чтобы добиться этого результата? А в случае, как?

+4

Интересный вопрос !! – Mutawe

+5

Возможный дубликат [iOS UILabel использовать негативный цвет фона как цвет текста] (http://stackoverflow.com/questions/19917378/ios-uilabel-use-negative-background-color-as-text-color) – rmaddy

+2

, но в этом случай я не прошу отрицательный цвет. отрицательный свет голубой не будет, вероятно, белым. –

ответ

6

Самый простой способ - создать подкласс 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]; 
} 

Вы можете расширить класс по мере необходимости с свойствами для цвета фона, цвет текста, шрифт и т.д.

+1

Вау, это решение похоже на лучшее использование Core Graphics без трюков с UIViews. Я тоже попробую, когда закончу работу, спасибо! –

4

Два UIViews.

  1. Давайте назовем один фон, а другой - progressBar. progressBar укладывается поверх фона с тем же самым происхождением в общем представлении.

  2. Оба они имеют UILabel как подпункт, и обе метки одинакового происхождения относительно их родителя. background имеет темный backgroundColor и его ярлык имеет светлый textColor, и в представлении прогресса все происходит наоборот.

  3. Progressbar имеет более узкую ширину, чем фон кадра и имеет clipsToBounds == ДА

Фокус в том, с описанием происхождения одинаковыми и наклеек Взгляды Происхождение же, и clipsToBounds на вершине взгляд, все будет выглядеть правильно.

Капля эти два вида в новый UIView подкласс под названием ReallyCoolProgressView, и дать ему один публичный метод:

-(void)setProgress:(float)progress 

прогресс представляет собой число от 0,0 до 1,0. Метод масштабирует ширину progressBar и устанавливает текст обоих меток @ «Прогресс% f», прогресс * 100

+0

Выглядит законное решение, я дам ему снимок позже, спасибо! –

+0

@NicolaMiotto ok bro .. попробуйте, и вы получите свое решение из моего ответа, чем принять его –

+0

, пожалуйста, не копируйте и вставляйте [ответы других людей] (http://stackoverflow.com/a/15539083/362730). – Tim

2

Просто идея «подделать» желаемый эффект. Вы можете создать подкласс UIView с одним ярлыком фона (белый и синий текст) и перед ним перед ним смотреть синий цвет фона и белый текст. Вы можете анимировать после ширины передней метки от 0 до 100% на ярлыке фона. Возможно, вам придется проверить, нужно ли вам наклеить ярлык внутри подвью, чтобы избежать смещения текста при увеличении ширины.

1

Чтобы упростить без переопределения drawRect:, вы можете создать 2 UIViews, чтобы обойти это.

Голубой фон UIView (A) содержит белый UILabel. (Подъемные отсеки включаются)

Белый фон UIView (B) содержит синий UILabel.

А будет накладывать на B.

Примечание: 2 UILabels будет иметь те же размеры, одни и те же шрифты и те же позиции.

Регулируя ширину UIView A, вы создадите панель процесса, как хотите.

0

Лучшее и чистым способом сделайте это, чтобы использовать маски. мнения иерархии вашего прогресса бара должна выглядеть следующим образом:

  • Base
    • Subview at index #1 вид, показывающий прогресс
    • Subview at index #2UILabel с текстом цветом так же, как основание
    • Subview at index #3UILabel с цветом текста аналогично представлению, показывающему прогресс

Мы будем применять маску к #3. Мы начинаем применять маску, как только представление, показывающее прогресс, достигнет кадра #3. Рамка маски должна следить за ходом. Когда #3 постепенно маскируется, ярлык внизу раскрывается, и вы достигаете эффекта красиво и с небольшим усилием. Here you are how to create masks with CALayers.

+0

Я также подумал, что в этом случае маски бы как-то пригодились, но не знали, как их использовать. Я тоже задумаюсь над этим решением, спасибо! Можете ли вы тем временем объяснить, почему это должно быть лучшим и чистым? Потому что тот, который предлагает @Morten, также выглядит довольно круто. –

+1

Это намного проще, требуется меньше кода, и что самое главное - CALayers получают анимированные вручную. Представьте себе ситуацию, когда вы устанавливаете свой прогресс с 0,1 до 0,5, без анимации, а также с помощью решения Мортена, это выглядит довольно плохо. В этом случае CALayer (маска) красиво анимируется, и вам предоставляется лучший UX. – wczekalski

+0

@NicolaMiotto Пожалуйста, подумайте о принятии моего ответа, если вы используете этот подход. – wczekalski

1

Swift 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() 
    } 
} 
Смежные вопросы