How to generate an UIImage from custom text in Swift
Asked Answered
M

3

15

I am trying to generate an UIImage from custom text in Swift3.

Using iOS Controls, It's possible to create an UIImage, below is the code:

class func imageWithText(txtField: UITextField) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(txtField.bounds.size, false, 0.0)
        txtField.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }

Note: I know it's also possible to add some text to an image, but I don't want to do like this.

Can someone help me to resolve this? Thank you!

Muscovado answered 29/6, 2018 at 10:55 Comment(0)
B
27

You can use this function, you can send any text to this function, inside it i create UILabel and set text attribute as you like

func imageWith(name: String?) -> UIImage? {
     let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
     let nameLabel = UILabel(frame: frame)
     nameLabel.textAlignment = .center
     nameLabel.backgroundColor = .lightGray
     nameLabel.textColor = .white
     nameLabel.font = UIFont.boldSystemFont(ofSize: 40)
     nameLabel.text = name
     UIGraphicsBeginImageContext(frame.size)
      if let currentContext = UIGraphicsGetCurrentContext() {
         nameLabel.layer.render(in: currentContext)
         let nameImage = UIGraphicsGetImageFromCurrentImageContext()
         return nameImage
      }
      return nil
}
Baklava answered 1/7, 2018 at 14:43 Comment(2)
Thanks for your reply @A.Munzer, I don't want to use any controls as mentioned in my question. Is it possible to generate UIImage without using controls like UITextField, UILabel?Muscovado
It is missing the ending of the context. To fix that you can add this defer { UIGraphicsEndImageContext() } before lineUIGraphicsBeginImageContext(frame.size).Newsreel
S
37

Here is a String extension that can be used as shown below to create UIImage instances from a String instance, without the need for UI controls like UITextField or UILabel:

var image: UIImage? =
    "Test".image(withAttributes: [
        .foregroundColor: UIColor.red,
        .font: UIFont.systemFont(ofSize: 30.0),
    ], size: CGSize(width: 300.0, height: 80.0))

// Or
image = "Test".image(withAttributes: [.font: UIFont.systemFont(ofSize: 80.0)])

// Or
image = "Test".image(size: CGSize(width: 300.0, height: 80.0))

// Or even just
image = "Test".image()

Playground Sampler

Below are two possible implementations for achieving the desired effect demonstrated above:

UIGraphicsImageRenderer Method (more performant and recommended implementation by Apple)

extension String {
    
    /// Generates a `UIImage` instance from this string using a specified
    /// attributes and size.
    ///
    /// - Parameters:
    ///     - attributes: to draw this string with. Default is `nil`.
    ///     - size: of the image to return.
    /// - Returns: a `UIImage` instance from this string using a specified
    /// attributes and size, or `nil` if the operation fails.
    func image(withAttributes attributes: [NSAttributedString.Key: Any]? = nil, size: CGSize? = nil) -> UIImage? {
        let size = size ?? (self as NSString).size(withAttributes: attributes)
        return UIGraphicsImageRenderer(size: size).image { _ in
            (self as NSString).draw(in: CGRect(origin: .zero, size: size),
                                    withAttributes: attributes)
        }
    }
    
}

UIGraphicsImageContext Method (old-school; included for thoroughness)

extension String {
    
    /// Generates a `UIImage` instance from this string using a specified
    /// attributes and size.
    ///
    /// - Parameters:
    ///     - attributes: to draw this string with. Default is `nil`.
    ///     - size: of the image to return.
    /// - Returns: a `UIImage` instance from this string using a specified
    /// attributes and size, or `nil` if the operation fails.
    func image(withAttributes attributes: [NSAttributedString.Key: Any]? = nil, size: CGSize? = nil) -> UIImage? {
        let size = size ?? (self as NSString).size(withAttributes: attributes)
        UIGraphicsBeginImageContext(size)
        (self as NSString).draw(in: CGRect(origin: .zero, size: size), 
                                withAttributes: attributes)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
    
}
Shenyang answered 4/3, 2019 at 21:25 Comment(3)
Good solution. I'd also include a call to UIGraphicsEndImageContext(). Alternatively, you might consider using the newer UIGraphicsImageRenderer - which is more performant and recommended by Apple.Mickens
@Mickens added the UIGraphicsEndImageContext() snippet and I will look into updating this answer with the newer implementation.Shenyang
@Muscovado if this answer seems to you like a more appropriate "correct" and more thorough answer, would appreciate you marking it so and for other developersShenyang
B
27

You can use this function, you can send any text to this function, inside it i create UILabel and set text attribute as you like

func imageWith(name: String?) -> UIImage? {
     let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
     let nameLabel = UILabel(frame: frame)
     nameLabel.textAlignment = .center
     nameLabel.backgroundColor = .lightGray
     nameLabel.textColor = .white
     nameLabel.font = UIFont.boldSystemFont(ofSize: 40)
     nameLabel.text = name
     UIGraphicsBeginImageContext(frame.size)
      if let currentContext = UIGraphicsGetCurrentContext() {
         nameLabel.layer.render(in: currentContext)
         let nameImage = UIGraphicsGetImageFromCurrentImageContext()
         return nameImage
      }
      return nil
}
Baklava answered 1/7, 2018 at 14:43 Comment(2)
Thanks for your reply @A.Munzer, I don't want to use any controls as mentioned in my question. Is it possible to generate UIImage without using controls like UITextField, UILabel?Muscovado
It is missing the ending of the context. To fix that you can add this defer { UIGraphicsEndImageContext() } before lineUIGraphicsBeginImageContext(frame.size).Newsreel
P
2

In re this very old question, it's honestly now this simple:

Make a square of color:

let image = UIGraphicsImageRenderer(size: size).image { rendererContext in
    UIColor.yellow.setFill()
    rendererContext.fill(rect)
}

Adding text is nowadays this simple:

let image = UIGraphicsImageRenderer(size: sz).image { rendererContext in
    UIColor.yellow.setFill()
    rendererContext.fill(rect)
    "fattie".draw(in: rect)
}

That's the whole thing.

    "some text".draw(in: rect)

To set the scale (likely "1"), font, blah blah etc ...

let sz = CGSize(width: 500, height: 300)
let frame = CGRect(origin: .zero, size: sz).insetBy(dx: 40, dy: 40)
let fmt = UIGraphicsImageRendererFormat()
fmt.scale = 1
let att = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 50.0)]

let image = UIGraphicsImageRenderer(size: sz, format: fmt).image { rendererContext in
    UIColor.systemYellow.setFill()
    rendererContext.fill(CGRect(origin: .zero, size: sz))
    "fattie?!".draw(in: frame, withAttributes: att)
}

if let data = image.jpegData(compressionQuality: 0.4) {
    let pp = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let ff = pp.appendingPathComponent("example.jpg")
    try? data.write(to: ff)
    print("On your mac look in: \(pp.path)")
}

enter image description here

ok?

Piedadpiedmont answered 2/7, 2023 at 23:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.