I am trying to draw long string on UIImage but unable to achieve . Currently when i draw the text on image only one line shows .
struct ContentView: View {
@State var bottomImage = UIImage(named: "2")
let text = "SwiftUI helps you build great-looking apps across all Apple platforms with the power of Swift — and surprisingly little code."
var body: some View {
VStack{
Image(uiImage: bottomImage!)
.resizable()
.scaledToFit()
Button {
drawTextOnImage()
} label: {
Text("DrawText")
}
}
}
func drawTextOnImage(){
let newSize = CGSize(width: 660, height: 700)
let topNew = UIImage(named: "3")
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
topNew!.draw(in: CGRect(origin: .zero, size: newSize))
let font = UIFont.systemFont(ofSize: 40)
let text_style=NSMutableParagraphStyle()
text_style.lineBreakMode = .byTruncatingTail
// text_style.numberOfLines = 0
text_style.alignment=NSTextAlignment.center
let text_color=UIColor.yellow
let attributes=[NSAttributedString.Key.font:font, NSAttributedString.Key.paragraphStyle:text_style, NSAttributedString.Key.foregroundColor:text_color]
let text_h=font.lineHeight
let text_y=((topNew?.size.height)!-text_h)/1.2
let text_rect=CGRect(x: 0, y: text_y, width: (topNew?.size.width)!, height: text_h)
text.draw(in: text_rect.integral, withAttributes: attributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
DispatchQueue.main.async {
self.bottomImage = newImage
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Achieved Result :
Expected Result : i want to show the whole string saved in text variable on the top of UIImage
There's a few things going on here, but the main issue you're facing is in let text_h=font.lineHeight
. By limiting the height of the drawable rectangle to the line height, there's not enough vertical space to wrap the text. Additionally, since you've already defined a size for the new image in let newSize = CGSize(width: 660, height: 700)
, referencing the asset's size will give you bounds that are different than your drawable area. You might also want to use .byWordWrapping
instead of .byTruncatingTail
for your line break mode if you want to see every word on the image instead of "...".
Here's a modification of your drawTextOnImage()
function addressing those points:
func drawTextOnImage() {
let newSize = CGSize(width: 660, height: 700)
let topNew = UIImage(named: "3")
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
topNew!.draw(in: CGRect(origin: .zero, size: newSize))
let font = UIFont.systemFont(ofSize: 40)
let text_style=NSMutableParagraphStyle()
text_style.lineBreakMode = .byWordWrapping
text_style.alignment=NSTextAlignment.center
let text_color=UIColor.yellow
let attributes=[NSAttributedString.Key.font:font, NSAttributedString.Key.paragraphStyle:text_style, NSAttributedString.Key.foregroundColor:text_color]
// Center the text on the finished image
let text_y=((newSize.height - font.pointSize)/2)
let text_rect=CGRect(x: 0, y: text_y, width: newSize.width, height: newSize.height)
text.draw(in: text_rect, withAttributes: attributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
DispatchQueue.main.async {
self.bottomImage = newImage
}
}
What's changed? We're calculating the center based on the font's size (thanks to this SO Answer), referencing your variable defined image dimensions, and are instructing the text to wrap instead of truncate.
Result:
As a bonus, if UIImage(named: "2")
and UIImage(named: "3")
are the same image, you can just call UIImage(named: "2")
both times. Drawing doesn't mutate the image in the asset catalog in any way.