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

Как добавить градиент к тексту UILabel, но не к фону?

Эй, я хочу иметь градиентную заливку текста в UILabel, который я знаю о CGGradient, но я не знаю, как использовать его в тексте UILabel

Я нашел это в google, но я не могу заставить его работать

http://silverity.livejournal.com/26436.html

4b9b3361

Ответ 1

(Пропустить в начало для полного исходного кода)

Действительно полезные ответы как Брэда Ларсона, так и Баха. Второй работал у меня, но для этого требуется, чтобы изображение присутствовало заранее. Я хотел что-то более динамичное, поэтому я объединил оба решения в один:

  • нарисуйте желаемый градиент на UIImage
  • используйте UIImage для установки цветового паттерна

Результат работает, и на скриншоте ниже вы можете увидеть некоторые греческие символы, сделанные отлично. (Я также добавил удар и тень поверх градиента)

iOS stylized UILabel, the big brown fox

Здесь пользовательский метод инициализации моей метки вместе с методом, который отображает градиент на UIImage (часть кода для этой функции, которую я получил из сообщения в блоге, я не могу найти сейчас, чтобы ссылаться на нее):

- (id)initWithFrame:(CGRect)frame text:(NSString *)aText {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        self.text = aText;

        self.textColor = [UIColor colorWithPatternImage:[self gradientImage]];

    }
    return self;
}

- (UIImage *)gradientImage
{
    CGSize textSize = [self.text sizeWithFont:self.font];
    CGFloat width = textSize.width;         // max 1024 due to Core Graphics limitations
    CGFloat height = textSize.height;       // max 1024 due to Core Graphics limitations

    // create a new bitmap image context
    UIGraphicsBeginImageContext(CGSizeMake(width, height));

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();       

    // push context to make it current (need to do this manually because we are not drawing in a UIView)
    UIGraphicsPushContext(context);                             

    //draw gradient    
    CGGradientRef glossGradient;
    CGColorSpaceRef rgbColorspace;
    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 0.0, 1.0, 1.0, 1.0,  // Start color
                            1.0, 1.0, 0.0, 1.0 }; // End color
    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    CGPoint topCenter = CGPointMake(0, 0);
    CGPoint bottomCenter = CGPointMake(0, textSize.height);
    CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, 0);

    CGGradientRelease(glossGradient);
    CGColorSpaceRelease(rgbColorspace); 

    // pop context 
    UIGraphicsPopContext();                             

    // get a UIImage from the image context
    UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up drawing environment
    UIGraphicsEndImageContext();

    return  gradientImage;
}

Я постараюсь завершить этот подкласс UILabel и опубликовать его.

ИЗМЕНИТЬ

Класс сделан, и он на мой репозиторий GitHub. Читайте об этом здесь!

Ответ 2

Я искал решение, и у DotSlashSlash есть ответ, спрятанный в одном из комментариев!

Для полноты ответа и простейшего решения:

UIImage *myGradient = [UIImage imageNamed:@"textGradient.png"];
myLabel.textColor   = [UIColor colorWithPatternImage:myGradient];

Ответ 3

В примере, который вы предоставляете, используются частные функции рисования текста, к которым у вас нет доступа на iPhone. Автор дает пример того, как это сделать, используя публичный API в последующий пост. В его более позднем примере используется Изображение градиента для цвета текста. (К сожалению, похоже, что его блог с тех пор был удален, но см. Bach ответ здесь за тот подход, который он использовал.)

Если вы все еще хотите нарисовать градиент для цвета текста в коде, это может быть сделано путем подкласса UILabel и переопределения -drawRect: для этого в нем должен быть следующий код:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0f, self.bounds.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);

CGContextSelectFont(context, "Helvetica", 20.0f, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextClip);
CGContextSetTextPosition(context, 0.0f, round(20.0f / 4.0f));
CGContextShowText(context, [self.text UTF8String], strlen([self.text UTF8String]));

CGContextClip(context);

CGGradientRef gradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0,  // Start color
    1.0, 1.0, 1.0, 0.1 }; // End color

rgbColorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(context, gradient, topCenter, midCenter, 0);

CGGradientRelease(gradient);
CGColorSpaceRelease(rgbColorspace);         

CGContextRestoreGState(context);

Одним из недостатков этого подхода является то, что используемые функции Core Graphics не обрабатывают текст Unicode должным образом.

Что делает код, он переворачивает контекст чертежа вертикально (iPhone инвертирует нормальную систему кварцевых координат для оси Y), устанавливает режим рисования текста, чтобы пересечь нарисованный текст с обтравочным контуром, закрепил область для рисования к тексту, а затем рисует градиент. Градиент будет заполнять только текст, а не фон.

Я попробовал использовать NSString -drawAtPoint: метод для этого, который поддерживает Unicode, но все символы запускались друг над другом, когда я переключил текстовый режим на kCGTextClip.

Ответ 4

SWIFT 3+

Это решение основано на ответе @Dimitris. Это расширение класса UILabel, которое создаст градиент над текстом метки в соответствии с переданными startColor и endColor. Расширение UILabel ниже:

extension UILabel {

    func applyGradientWith(startColor: UIColor, endColor: UIColor) -> Bool {

        var startColorRed:CGFloat = 0
        var startColorGreen:CGFloat = 0
        var startColorBlue:CGFloat = 0
        var startAlpha:CGFloat = 0

        if !startColor.getRed(&startColorRed, green: &startColorGreen, blue: &startColorBlue, alpha: &startAlpha) {
            return false
        }

        var endColorRed:CGFloat = 0
        var endColorGreen:CGFloat = 0
        var endColorBlue:CGFloat = 0
        var endAlpha:CGFloat = 0

        if !endColor.getRed(&endColorRed, green: &endColorGreen, blue: &endColorBlue, alpha: &endAlpha) {
            return false
        }

        let gradientText = self.text ?? ""

        let name:String = NSFontAttributeName
        let textSize: CGSize = gradientText.size(attributes: [name:self.font])
        let width:CGFloat = textSize.width
        let height:CGFloat = textSize.height

        UIGraphicsBeginImageContext(CGSize(width: width, height: height))

        guard let context = UIGraphicsGetCurrentContext() else {
            UIGraphicsEndImageContext()
            return false
        }

        UIGraphicsPushContext(context)

        let glossGradient:CGGradient?
        let rgbColorspace:CGColorSpace?
        let num_locations:size_t = 2
        let locations:[CGFloat] = [ 0.0, 1.0 ]
        let components:[CGFloat] = [startColorRed, startColorGreen, startColorBlue, startAlpha, endColorRed, endColorGreen, endColorBlue, endAlpha]
        rgbColorspace = CGColorSpaceCreateDeviceRGB()
        glossGradient = CGGradient(colorSpace: rgbColorspace!, colorComponents: components, locations: locations, count: num_locations)
        let topCenter = CGPoint.zero
        let bottomCenter = CGPoint(x: 0, y: textSize.height)
        context.drawLinearGradient(glossGradient!, start: topCenter, end: bottomCenter, options: CGGradientDrawingOptions.drawsBeforeStartLocation)

        UIGraphicsPopContext()

        guard let gradientImage = UIGraphicsGetImageFromCurrentImageContext() else {
            UIGraphicsEndImageContext()
            return false
        }

        UIGraphicsEndImageContext()

        self.textColor = UIColor(patternImage: gradientImage)

        return true
    }

}

И использование:

let text = "YAAASSSSS!"
label.text = text
if label.applyGradientWith(startColor: .red, endColor: .blue) {
    print("Gradient applied!")
}
else {
    print("Could not apply gradient")
    label.textColor = .black
}

YAAASSSSS!


SWIFT 2

class func getGradientForText(text: NSString) -> UIImage {

    let font:UIFont = UIFont(name: "YourFontName", size: 50.0)!
    let name:String = NSFontAttributeName
    let textSize: CGSize = text.sizeWithAttributes([name:font])
    let width:CGFloat = textSize.width         // max 1024 due to Core Graphics limitations
    let height:CGFloat = textSize.height       // max 1024 due to Core Graphics limitations

    //create a new bitmap image context
    UIGraphicsBeginImageContext(CGSizeMake(width, height))

    // get context
    let context = UIGraphicsGetCurrentContext()

    // push context to make it current (need to do this manually because we are not drawing in a UIView)
    UIGraphicsPushContext(context!)

    //draw gradient
    let glossGradient:CGGradientRef?
    let rgbColorspace:CGColorSpaceRef?
    let num_locations:size_t = 2
    let locations:[CGFloat] = [ 0.0, 1.0 ]
    let components:[CGFloat] = [(202 / 255.0), (197 / 255.0), (52 / 255.0), 1.0,  // Start color
                                (253 / 255.0), (248 / 255.0), (101 / 255.0), 1.0] // End color
    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    let topCenter = CGPointMake(0, 0);
    let bottomCenter = CGPointMake(0, textSize.height);
    CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, CGGradientDrawingOptions.DrawsBeforeStartLocation);

    // pop context
    UIGraphicsPopContext();

    // get a UIImage from the image context
    let gradientImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up drawing environment
    UIGraphicsEndImageContext();

    return  gradientImage;
}

Реквизит @Dimitris

Ответ 5

Вот что я делаю в Swift 3

override func viewDidLoad() {
    super.viewDidLoad()
    timerLabel.textColor = UIColor(patternImage: gradientImage(size: timerLabel.frame.size, color1: CIColor(color: UIColor.green), color2: CIColor(color: UIColor.red), direction: .Left))
}

func gradientImage(size: CGSize, color1: CIColor, color2: CIColor, direction: GradientDirection = .Up) -> UIImage {

    let context = CIContext(options: nil)
    let filter = CIFilter(name: "CILinearGradient")
    var startVector: CIVector
    var endVector: CIVector

    filter!.setDefaults()

    switch direction {
    case .Up:
        startVector = CIVector(x: size.width * 0.5, y: 0)
        endVector = CIVector(x: size.width * 0.5, y: size.height)
    case .Left:
        startVector = CIVector(x: size.width, y: size.height * 0.5)
        endVector = CIVector(x: 0, y: size.height * 0.5)
    case .UpLeft:
        startVector = CIVector(x: size.width, y: 0)
        endVector = CIVector(x: 0, y: size.height)
    case .UpRight:
        startVector = CIVector(x: 0, y: 0)
        endVector = CIVector(x: size.width, y: size.height)
    }

    filter!.setValue(startVector, forKey: "inputPoint0")
    filter!.setValue(endVector, forKey: "inputPoint1")
    filter!.setValue(color1, forKey: "inputColor0")
    filter!.setValue(color2, forKey: "inputColor1")

    let image = UIImage(cgImage: context.createCGImage(filter!.outputImage!, from: CGRect(x: 0, y: 0, width: size.width, height: size.height))!)
    return image
}

Ответ 6

Swift 4.1

class GradientLabel: UILabel {
    var gradientColors: [CGColor] = []

    override func drawText(in rect: CGRect) {
        if let gradientColor = drawGradientColor(in: rect, colors: gradientColors) {
            self.textColor = gradientColor
        }
        super.drawText(in: rect)
    }

    private func drawGradientColor(in rect: CGRect, colors: [CGColor]) -> UIColor? {
        let currentContext = UIGraphicsGetCurrentContext()
        currentContext?.saveGState()
        defer { currentContext?.restoreGState() }

        let size = rect.size
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
                                        colors: colors as CFArray,
                                        locations: nil) else { return nil }

        let context = UIGraphicsGetCurrentContext()
        context?.drawLinearGradient(gradient,
                                    start: CGPoint.zero,
                                    end: CGPoint(x: size.width, y: 0),
                                    options: [])
        let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        guard let image = gradientImage else { return nil }
        return UIColor(patternImage: image)
    }
}

Использование:

label.gradientColors = [UIColor.blue.cgColor, UIColor.red.cgColor]

Ответ 7

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

Ответ 8

yourLabel.textColor = UIColor(patternImage: UIImage(named: "ur gradient image name ")!)

Ответ 9

Простейшее решение Swift 3

Добавьте изображение в свои активы проекта или создайте его программно, а затем сделайте следующее:

let image = UIImage(named: "myGradient.png")!
label.textColor = UIColor.init(patternImage: image)