I have looked around and have been unsuccessful at figuring out how take text, overlay it on an image, and then combine the two into a single UIImage.
I have exhausted Google using the search terms I can think of so if anyone has a solution or at least a hint they can point to it would be greatly appreciated.
I figured it out:
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
}
To call it, you just pass in an image:
textToImage("000", inImage: UIImage(named:"thisImage.png")!, atPoint: CGPointMake(20, 20))
The following links helped me get this straight:
Swift - Drawing text with drawInRect:withAttributes:
How to write text on image in Objective-C (iOS)?
The original goal was to create a dynamic image that I could use in an AnnotaionView such as putting a price at a given location on a map and this worked out great for it.
For 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!
}
For 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!
}
For 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!
}
My simple solution:
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
}
You can also do a 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
I have created an extension for using it everywhere :
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!
}
}
Usage :
Anywhere on your ViewController where you have the size and the label to add use it as follows -
let newImageWithOverlay = UIImage.createImageWithLabelOverlay(label: labelToAdd, imageSize: size, image: editedImage)
For swift 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!
}
I can't see anything in your initial question suggesting that this must be done exclusively in code - so why not simply add a UILabel in interface builder, and add constraints to give it the same length and width as your image, center it vertically and horizontally (or however you need it placed), delete the label text, set the text font, size, colour, etc. as needed (including ticking Autoshrink with whatever minimum size or scale you need), and ensure it's background is transparent.
Then just connect it to an IBOutlet, and set the text in code as needed (e.g. in viewWillAppear, or by using a ViewModel approach and setting it on initialisation of your view/viewcontroller).
I have tried this basic components. Hope it will work.
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
}
It's also possible to use the QLPreviewController. Just save the imageFile to an url like the applicationsDocuments directory under the .userDomainMask and open the apple' editor. You can draw, add shapes, arrow and even your signature.
I explained the implementation in detail in the following post:
https://stackoverflow.com/a/68743098/12035498
Related
So I have the following code:
let number = cluster.memberAnnotations.count
displayPriority = .defaultHigh
image = textToImage(
drawText: "\(number)",
inImage: UIImage(named: "map-pin-full-cluster-1")!
.withTintColor(.blue, renderingMode: . alwaysTemplate)
.resizeWith(
newSize: CGSize(width: 35, height: 35)), atPoint: CGPointMake(14.5, 3.5)
)
Which outputs the following look on the map:
Here is a function that I am using to assist me:
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!
}
Problem:
When the number ends up going into double digit, it isn't contained within the circle, how can I format my code so that I can center a number based on a CRect or other call?
:
Got it working thanks to #Larme's guidance:
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
let textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.paragraphStyle: paragraph,
] as [NSAttributedString.Key : Any]
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
Define a NSMutableParagraphStyle() and set the paragraphStyle to it's attribute.
I want to write a text string on a fully transparent image (alpha 0 everywhere) but it doesn't work. The background of the image turns to be white if alpha of background image is 0. Here are the approaches I tried:
extension UIColor {
func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { rendererContext in
self.setFill()
rendererContext.fill(CGRect(origin: .zero, size: size))
}
}
}
func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
let textColor = UIColor.blue
let textFont = UIFont(name: "Helvetica Bold", size: 40)!
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!
}
And then:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let inImage = UIColor.black.withAlphaComponent(0).image(CGSize(width: 800, height: 800))
//Even tried inImage = UIImage(named: "Transparent") where Transparent.png is fully transparent image! //
let image = textToImage(drawText: "Test String", inImage: inImage, atPoint: CGPoint(x: 0, y: 0))
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
No matter what I do, the background is white.
Try converting to png
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let inImage = UIColor.black.withAlphaComponent(0).image(CGSize(width: 800, height: 800))
//Even tried inImage = UIImage(named: "Transparent") where Transparent.png is fully transparent image! //
let image = textToImage(drawText: "Test String", inImage: inImage, atPoint: CGPoint(x: 0, y: 0))
self.image.image = image
if let pngdata = image.pngData() {
if let newImage = UIImage(data: pngdata) {
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
}
}
}
I've tested Your code in playgroung, and it works as it should
So The reason is Apple Gallery representations of alpha channels. Looks like it's not supported
I'm trying to add a convenience initialiser to the UIImage which will allow me to create an image with a given text centred in the middle, and given background color:
I've got the following code:
public convenience init?(color: UIColor, withText: String, withTextColor: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let label = UILabel()
label.text = withText
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 40)
label.textColor = withTextColor
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
This creates the image with the correct background color - I'm not sure how to embed the UILabel in the middle of the image though.
EDIT
Attributed strings know how to draw themselves, so you don't need a label. They also can tell you how big they are or even wrap to fit an available width. Here is playground that shows how to get an image from any attributed string:
import UIKit
import PlaygroundSupport
extension NSAttributedString {
func asImage(size: CGSize = .init(width: .max, height: .max)) -> UIImage {
UIGraphicsImageRenderer(bounds: boundingRect(with: size, options: [.usesLineFragmentOrigin], context: nil)).image { context in
self.draw(at: .zero)
}
}
}
let attributedString = NSAttributedString.init(string: "testing", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
let image = attributedString.asImage()
let imageView = UIImageView(image: image)
PlaygroundPage.current.liveView = imageView
Here is the convenience initialiser drawing white text in the centre of a red image:
convenience init?(letters: String) {
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
let sizeOfImage = CGSize(width: 120, height: 120)
let font = UIFont(name: "Georgia", size: 50)!
let imageText = NSAttributedString(string: letters, attributes: [.font: font, .foregroundColor: UIColor.white, .paragraphStyle: style])
let textHight = font.lineHeight
let renderer = UIGraphicsImageRenderer(size: sizeOfImage)
let image = renderer.image { context in
UIColor.red.setFill()
context.fill(CGRect(origin: .zero, size: sizeOfImage))
imageText.draw(in: CGRect(origin: CGPoint(x: 0, y: (sizeOfImage.height - textHight) / 2), size: sizeOfImage))
}
self.init(cgImage: image.cgImage!)
}
I want to set my text Sold Out to the center in UIimage. but it not in center .
func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
let textColor = UIColor.white
let textFont = UIFont(name: "Helvetica Bold", size: 32)!
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)
let rect = CGRect(origin: CGPoint(x:(image.size.width/2), y: (image.size.height/2)), size: image.size)
text.draw(in: rect, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
caller function
imageViewSoldStatus.image = textToImage(drawText: "Sold out", inImage: UIImage(named:"soldout.png")!, atPoint: CGPoint(x: 5.0, y: 5.0))
I have spent of very long time to solve this problem but I could not find any solution.
Red flag my image
above is the screen I obtained. below is the design that I want to achieve.
update
Marie Dm answerd used! it is text align center but not image center
enter image description here
Try this:
func textToImage(drawText text: NSString, inImage image: UIImage) -> UIImage? {
//draw image first
UIGraphicsBeginImageContext(image.size)
image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
//text attributes
let font=UIFont(name: "Helvetica-Bold", size: 32)!
let text_style=NSMutableParagraphStyle()
text_style.alignment=NSTextAlignment.center
let text_color=UIColor.white
let attributes=[NSFontAttributeName:font, NSParagraphStyleAttributeName:text_style, NSForegroundColorAttributeName:text_color]
//vertically center (depending on font)
let text_h=font.lineHeight
let text_y=(image.size.height-text_h)/2
let text_rect=CGRect(x: 0, y: text_y, width: image.size.width, height: text_h)
text.draw(in: text_rect.integral, withAttributes: attributes)
let result=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
updated for Swift 5
func textToImage(drawText text: NSString, inImage image: UIImage) -> UIImage {
UIGraphicsBeginImageContext(image.size)
image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
let font=UIFont(name: "Helvetica-Bold", size: 14)!
let text_style=NSMutableParagraphStyle()
text_style.alignment=NSTextAlignment.center
let text_color=UIColor.white
let attributes=[NSAttributedString.Key.font:font, NSAttributedString.Key.paragraphStyle:text_style, NSAttributedString.Key.foregroundColor:text_color]
let text_h=font.lineHeight
let text_y=(image.size.height-text_h)/2
let text_rect=CGRect(x: 0, y: text_y, width: image.size.width, height: text_h)
text.draw(in: text_rect.integral, withAttributes: attributes)
let result=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
Marie's answer updated for swift 4
func textToImage(drawText text: NSString, inImage image: UIImage) -> UIImage {
UIGraphicsBeginImageContext(image.size)
image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
let font=UIFont(name: "Helvetica-Bold", size: 14)!
let text_style=NSMutableParagraphStyle()
text_style.alignment=NSTextAlignment.center
let text_color=UIColor.white
let attributes=[NSAttributedStringKey.font:font, NSAttributedStringKey.paragraphStyle:text_style, NSAttributedStringKey.foregroundColor:text_color]
let text_h=font.lineHeight
let text_y=(image.size.height-text_h)/2
let text_rect=CGRect(x: 0, y: text_y, width: image.size.width, height: text_h)
text.draw(in: text_rect.integral, withAttributes: attributes)
let result=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
I'm trying to edit an image by adding text to it, but the code I got from this post does not work: How to write text on image in Objective-C (iOS)?
My image-editing code looks like this:
func addTextToImage(image:UIImage, text:NSString, pointof: CGPoint) -> UIImage{
let font:UIFont = UIFont.boldSystemFontOfSize(12)
let dict:NSDictionary = [NSFontAttributeName : font]
UIGraphicsBeginImageContext(image.size)
image.drawInRect(CGRectMake(0, 0, image.size.width, image.size.height))
let rect: CGRect = CGRectMake(pointof.x, pointof.y, image.size.width, image.size.height)
let color: UIColor = UIColor.whiteColor()
text.drawInRect(CGRectIntegral(rect), withAttributes:dict)
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
And then I use the textViewDidChange method to add the text to the image, but it does not work:
func textViewDidChange(textView: UITextView){
backgroundImage.image = addTextToImage(imageToView, text: textViewer.text, pointof: CGPointMake(0, 0))
}
Any suggestions on what I'm doing wrong would be appreciated.
func addTextToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage
{
let textColor = UIColor.blue
let textFont = UIFont(name: "ChalkboardSE-Regular", size: 26)!
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!
}