Як додати текст до зображення в iOS Swift?


82

Я оглянувся і мені не вдалося зрозуміти, як взяти текст, накласти його на зображення, а потім об'єднати ці два в єдине UIImage.

Я вичерпав Google, використовуючи пошукові терміни, які я можу придумати, тому, якщо хтось має рішення чи хоча б підказку, на яку він може вказати, буде вдячний.

Відповіді:


186

Гаразд ... Я зрозумів:

func textToImage(drawText: NSString, inImage: UIImage, atPoint: CGPoint) -> UIImage{

    // Setup the font specific variables
    var textColor = UIColor.whiteColor()
    var textFont = UIFont(name: "Helvetica Bold", size: 12)!

    // Setup the image context using the passed image
    let scale = UIScreen.mainScreen().scale
    UIGraphicsBeginImageContextWithOptions(inImage.size, false, scale)

    // Setup the font attributes that will be later used to dictate how the text should be drawn
    let textFontAttributes = [
        NSFontAttributeName: textFont,
        NSForegroundColorAttributeName: textColor,
    ]

    // Put the image into a rectangle as large as the original image
    inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))

    // Create a point within the space that is as bit as the image
    var rect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)

    // Draw the text into an image
    drawText.drawInRect(rect, withAttributes: textFontAttributes)

    // Create a new image out of the images we have created
    var newImage = UIGraphicsGetImageFromCurrentImageContext()

    // End the context now that we have the image we need
    UIGraphicsEndImageContext()

    //Pass the image back up to the caller
    return newImage

}

Щоб назвати це, ви просто передаєте зображення:

textToImage("000", inImage: UIImage(named:"thisImage.png")!, atPoint: CGPointMake(20, 20))

Наступні посилання допомогли мені зрозуміти це:

Swift - Малювання тексту за допомогою drawInRect: withAttributes:

Як написати текст на зображенні в Objective-C (iOS)?

Початковою метою було створити динамічне зображення, яке я міг би використати, AnnotaionViewнаприклад, встановити ціну на певному місці на карті, і це чудово для нього вийшло. Сподіваюся, це допомагає тому, хто намагається зробити те саме.

Для Swift 3:

 func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSFontAttributeName: textFont,
        NSForegroundColorAttributeName: textColor,
        ] as [String : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
 }

Для Swift 4:

 func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSAttributedStringKey.font: textFont,
        NSAttributedStringKey.foregroundColor: textColor,
        ] as [NSAttributedStringKey : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
 }

Для Swift 5:

func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSAttributedString.Key.font: textFont,
        NSAttributedString.Key.foregroundColor: textColor,
        ] as [NSAttributedString.Key : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
}

1
надзвичайно корисно! дякую за розміщення коду. і так добре прокоментували.
Аспен

чи можуть ці тексти або їх контейнери реагувати на відповідь на події натискання?
PPrasai

1
@JadeSync, якщо об’єкт, до якого ви імпортуєте зображення, можна натиснути, тоді так. Але не текст можна натискати, це контейнер, в якому використовуються кінцеві зображення, який повинен бути клікабельним. Насправді, це те, що я зробив із цим зображенням, тому що мені потрібен був спосіб, щоб на карті був маркер, який можна натиснути, а маркер повинен мати текст на зображенні. У маркера є делегат події кліку, тому не зображення, на яке вони натискають, а маркер, який його містить.
Крістофер Уейд Кентлі

2
Якщо я встановив точку внизу праворуч від зображення, а текст занадто довгий, він поширюється на зображення. Будь-які пропозиції щодо того, як змусити текст вирівняти ліворуч від точки, а не розтягувати зображення праворуч?
chickenparm

Я також зіткнувся з цією проблемою, і відверто кажучи, це залежить від типу та розміру шрифту та розміру зображення, де ви хочете, щоб текст починався на зображенні, оскільки ви, можливо, не хочете наступати на тінь PNG. Це була спробна помилка - помилка, яка вимагає від мене додавання символу, з’ясування кількості пікселів для переміщення початкової точки, щоб отримати її, а потім виконайте швидкий розрахунок, щоб визначити ковзну початкову точку на основі кількості символів (вгору до максимуму залежно від того, як це виглядає). Я не хотів надто ускладнюватися, тому звернувся до чистого рішення для свого запитання.
Крістофер Уейд Кентлі,

20

Моє просте рішення:

func generateImageWithText(text: String) -> UIImage? {
    let image = UIImage(named: "imageWithoutText")!

    let imageView = UIImageView(image: image)
    imageView.backgroundColor = UIColor.clear
    imageView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

    let label = UILabel(frame: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
    label.backgroundColor = UIColor.clear
    label.textAlignment = .center
    label.textColor = UIColor.white
    label.text = text

    UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
    imageView.layer.render(in: UIGraphicsGetCurrentContext()!)
    label.layer.render(in: UIGraphicsGetCurrentContext()!)
    let imageWithText = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return imageWithText
}

Так для коду, але як налаштувати розмір шрифту, кожен розмір зображення різний при різній довжині тексту. Як я можу зробити це динамічним.
iPhoneDev

Я думаю, вам слід використовувати Autoshrink. Встановіть максимальний розмір шрифту для максимальної ширини етикетки та додайте: label.adjustsFontSizeToFitWidth = true & label.minimumScaleFactor = 0,5
Darkngs

чи є спосіб додати текст мітки, вирівняний до будь-якого з кутів?
Ashh

11

Ви також можете зробити CATextLayer.

    // 1
let textLayer = CATextLayer()
textLayer.frame = someView.bounds

// 2
let string = String(
  repeating: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor arcu quis velit congue dictum. ", 
  count: 20
)

textLayer.string = string

// 3
let fontName: CFStringRef = "Noteworthy-Light"
textLayer.font = CTFontCreateWithName(fontName, fontSize, nil)

// 4
textLayer.foregroundColor = UIColor.darkGray.cgColor
textLayer.isWrapped = true
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.contentsScale = UIScreen.main.scale
someView.layer.addSublayer(textLayer)

https://www.raywenderlich.com/402-calayer-tutorial-for-ios-getting-started


1
Любіть речі Рея. Дякуємо за альтернативний метод!
Крістофер Уейд Кентлі

2
Ви додали текстовий шар до подання тут, а не до зображення
brainray

1
Варто зазначити , якщо один дивиться вертикально вирівнювати текст см stackoverflow.com/questions/4765461 / ...
JKRT

5

Я створив розширення для його використання скрізь:

import Foundation
import UIKit
extension UIImage {

    class func createImageWithLabelOverlay(label: UILabel,imageSize: CGSize, image: UIImage) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(CGSize(width: imageSize.width, height: imageSize.height), false, 2.0)
        let currentView = UIView.init(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
        let currentImage = UIImageView.init(image: image)
        currentImage.frame = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
        currentView.addSubview(currentImage)
        currentView.addSubview(label)
        currentView.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }

}

Використання: Будь-де на ViewController, де ви маєте розмір та ярлик для додавання, використовуйте його наступним чином -

let newImageWithOverlay = UIImage.createImageWithLabelOverlay(label: labelToAdd, imageSize: size, image: editedImage)

1
Чудово! Дякуємо за сприяння цьому.
Крістофер Уейд Кентлі

Він показує білий фон, навіть якщо я використовував прозоре зображення
EI Captain v2.0

Що ви робите, розміщуючи його у поданні або завантажуючи на сервер?
Ankit Kumar Gupta,

Просто візьміть зображення та покажіть його у вигляді зображення. На ньому показано біле тло
EI Captain v2.0

Вигляд зображення та його перегляд повинні мати чітке тло. Якщо ви опублікуєте його на сервері у форматі png, тоді він буде таким, як ви хочете. Зробіть це і перевірте. Або спробуйте зберегти його локально один раз.
Ankit Kumar Gupta,

3

Для швидкого 4:

func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {


    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center



    let attrs = [NSAttributedStringKey.font: UIFont(name: "Helvetica Bold", size: 12)!,NSAttributedStringKey.foregroundColor : UIColor.white , NSAttributedStringKey.paragraphStyle: paragraphStyle]


    text.draw(with: rect, options: .usesLineFragmentOrigin, attributes: attrs, context: nil)



    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
}

1

У вашому початковому запитанні я нічого не бачу, що це слід робити виключно в коді - так чому б просто не додати UILabel у конструктор інтерфейсу та додати обмеження, щоб надати йому однакову довжину та ширину, як ваше зображення, відцентрувати його вертикально та горизонтально (або, як вам це потрібно), видаліть текст ярлика, встановіть шрифт тексту, розмір, колір тощо за необхідності (включаючи галочку Автозвіска будь-яким мінімальним розміром або масштабом) і переконайтеся, що фон прозорий.

Потім просто підключіть його до IBOutlet і встановіть текст у коді за необхідності (наприклад, у viewWillAppear, або за допомогою підходу ViewModel та встановивши його на ініціалізацію вашого перегляду / контролера перегляду).


0

Я спробував ці основні компоненти. Сподіваюся, це спрацює.

func imageWithText(image : UIImage, text : String) -> UIImage {

        let outerView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width / 2, height: image.size.height / 2))
        let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: outerView.frame.width, height: outerView.frame.height))
        imgView.image = image
        outerView.addSubview(imgView)

        let lbl = UILabel(frame: CGRect(x: 5, y: 5, width: outerView.frame.width, height: 200))
        lbl.font = UIFont(name: "HelveticaNeue-Bold", size: 70)
        lbl.text = text
        lbl.textAlignment = .left
        lbl.textColor = UIColor.blue

        outerView.addSubview(lbl)

        let renderer = UIGraphicsImageRenderer(size: outerView.bounds.size)
        let convertedImage = renderer.image { ctx in
            outerView.drawHierarchy(in: outerView.bounds, afterScreenUpdates: true)

        }
        return convertedImage
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.