Подтвердить что ты не робот

UILabel: цвет, зависящий от фона

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

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

enter image description here

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

4b9b3361

Ответ 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.