Adding text to image not in the right position Swift - swift

I use this code to add text from textview to Image
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
}
I download image from server, and add text for this. I already resize the image before I add text for that to fit the device i am using, in this case is Iphone 6s, 375*667
When I add text from textView which is red in the picture to my image, it isn't in the right position. It is always pushed to the right without reason.
Does anyone know about this problem ?
Thank you very much !!!

Imagine you have a square. If you set both the X and Y to 0, it won't place the middle of the square to the x: 0 and y: 0. It will place the top left of that square to that coordinate. Your issue is that when you call your function, it's placing the top left of the text to the point of where you tapped. You have to minus half of the texts width for the X and half of the texts height for the Y. It should look something like this:
func textToImage(drawText: "Hello, inImage: ***YOUR_IMAGE***, atPoint: CGPoint(x: point.x - (text.frame.width / 2), y: point.y - (text.frame.height / 2))

Related

How to use CGContext with AttributedString and Image attachment

Using Swift 4, I'm drawing a NSMutableAttributedString using CGContext to a PDF file. The string contains an attachment with an UIImage. But the image gets lost when using the UIGraphicsGetCurrentContext.
Creation of NSMutableAttributedString with attachment:
let aString = NSMutableAttributedString()
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "miniatureIcon")!
aString.append( attachmentString )
Creation of the context.
UIGraphicsBeginPDFPageWithInfo....
print("attributedString \(attributedString)") // attachement still here
// Get current graphics context
let currentContext = UIGraphicsGetCurrentContext()!
// Create a core text frame setter
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
// Save context pre manipulation
currentContext.saveGState()
// Reset text matrix, so no text scaling is affected
currentContext.textMatrix = CGAffineTransform.identity
// Create the frame and a rectangular path of the text frame
let frameRect = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
let framePath = UIBezierPath(rect: frameRect).cgPath
// Create core text frame for the given attributed string
// The whole text should fit the frame, as calculations were already done
let frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedString.length), framePath, nil)
// Translate by 100% graphics height up and reverse scale, as core text does draw from bottom up and not from top down
currentContext.translateBy(x: 0, y: UIGraphicsGetPDFContextBounds().height)
currentContext.scaleBy(x: 1.0, y: -1.0)
// Translate context to actual position of text
currentContext.translateBy(x: frame.minX, y: UIGraphicsGetPDFContextBounds().height - frame.maxY)
// Draw text into context
CTFrameDraw(frameRef, currentContext)
// Restore context to pre manipulation
currentContext.restoreGState()
UIGraphicsEndPDFContext()
Since this is the last time the text gets manipulated before it gets drawn on the PDF file and since I have debugged it down to this part where the attachment is available for the last time, I assume that the error is as part of the CGContext.
Other attributes like bold or center remain on the final file.
But the attachment is gone.
What am I missing? Help is very appreciated.

How to center a CGImage on a CALayer?

I was trying to set a UIImage's CGImage as a layer's content and then add the layer to a view's layer.
It's should be five stars at the center of the yellow view. That's what I want it to be.
But it seems the center of the stars is aligned with the origin of the view.
What should I do to rectify it?
func putOnStars() {
let rect = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
rect.backgroundColor = .yellow
view.addSubview(rect)
let baseLayer = CALayer()
baseLayer.contents = UIImage(named: "stars")?.cgImage
baseLayer.contentsGravity = kCAGravityCenter
rect.layer.addSublayer(baseLayer)
}
Here is the stars image for you in case of you want to test.
baseLayer doesn't have a defined frame so baseLayer.contentsGravity = kCAGravityCenter will work fine but it'll still be on the potision (0, 0).
There are two possible solutions:
1 : Make a frame of baseLayer that is identical to rect. Implement this code:
baseLayer.frame = rect.frame
2 : Set the position of baseLayer to the center of rect. Implement this code:
baseLayer.position = rect.center
To place the stars image in the centre of the CALayer, give the frame of the layer, i.e.
let baseLayer = CALayer()
baseLayer.frame = rect.bounds //This Line
baseLayer.contents = UIImage(named: "stars")?.cgImage
baseLayer.contentsGravity = kCAGravityCenter
rect.layer.addSublayer(baseLayer)
For any kind of CALayer, you need to define its frame explicitly.

Images being flipped when adding to NSAttributedString

I have a strange problem when resizing an image that's in a NSAttributedString. The resizing extension is working fine, but when the image is added to the NSAttributedString, it gets flipped vertically for some reason.
This is the resizing extension:
extension NSImage {
func resize(containerWidth: CGFloat) -> NSImage {
var scale : CGFloat = 1.0
let currentWidth = self.size.width
let currentHeight = self.size.height
if currentWidth > containerWidth {
scale = (containerWidth * 0.9) / currentWidth
}
let newWidth = currentWidth * scale
let newHeight = currentHeight * scale
self.size = NSSize(width: newWidth, height: newHeight)
return self
}
}
And here is the enumeration over the images in the attributed string:
newAttributedString.enumerateAttribute(NSAttributedStringKey.attachment, in: NSMakeRange(0, newAttributedString.length), options: []) { value, range, stop in
if let attachement = value as? NSTextAttachment {
let image = attachement.image(forBounds: attachement.bounds, textContainer: NSTextContainer(), characterIndex: range.location)!
let newImage = image.resize(containerWidth: markdown.bounds.width)
let newAttribute = NSTextAttachment()
newAttribute.image = newImage
newAttributedString.addAttribute(NSAttributedStringKey.attachment, value: newAttribute, range: range)
}
}
I've set breakpoints and inspected the images, and they are all in the correct rotation, except when it reaches this line:
newAttributedString.addAttribute(NSAttributedStringKey.attachment, value: newAttribute, range: range)
where the image gets flipped vertically.
I have no clue what could be causing this vertical flip. Is there a way to fix this?
If you look at the developer docs for NSTextAttachment:
https://developer.apple.com/documentation/uikit/nstextattachment
The bounds parameter is defined as follows:
“Defines the layout bounds of the receiver's graphical representation in the text coordinate system.”
I know that when using CoreText to layout text, you need to flip the coordinates, so I should imagine you need to transform your bounds parameter with a vertical reflection too.
Hope that helps.
I figured it out and it was so much simpler than I was making it.
Because the image was in a NSAttribuetdString being appended into a NSTextView I didn't need to resize each image in the NSAttributedString, rather I just had to set the attachment scaling inside the NSTextView with
markdown.layoutManager?.defaultAttachmentScaling = NSImageScaling.scaleProportionallyDown
One line is all it took

Draw NSTextField setting the content NSRect

Given a struct Concept that has an associated text: String and area: NSRect, that when drawn on the view, it will draw the String in the given NSRect.
I would like to on click, show a NSTextField which "content" (cell) NSRect is equal to the given NSRect
If I tried to set up textField.frame = concept.area, the cell will be rendered in an inset position considering the border + padding of the NSTextField, so it will render the text in a different, slightly moved, NSRect.
In code the idea would be something like
struct Concept {
let text: String
let area: NSRect
func draw() {
text.draw(inRect: area)
}
}
let conceptRect = NSRect(x: 50, y: 60, width: 80, height: 20)
let concept = Concept(text: "sample text", area: conceptRect)
let textField = NSTextField()
textField.stringValue = concept.text
textField.???? = area
let textFieldRect = textField.frame
assert(textFieldRect != conceptRect)
assert(textFieldRect.contains(conceptRect))
And the expected result should look like:
any ideas on how can I achieve this?
thanks
You don't say how you're drawing the string in the area, which can affect exactly where it draws.
If you want to figure out what the text field's border padding is, you can compare its cell's drawingRect(forBounds:) with the text field's bounds. The drawing rect will be inset by a bit from the bounds. To compute a frame to get a particular drawing rect, you reverse that by "outsetting" from the desired drawing rect by the same amount.

I need help integrating a specific UIImage resizing extension into my current draw CGRect function

I found this extension online, it allows me to have images adhere to aspect fit/fill even when drawn inside dynamically growing/shrinking image views (currently when image is saved to camera roll after my draw function the image reverts to "scale fill" regardless of what the content mode of the image view is. I suspect the reasoning for this is because I have it drawing the image to size/bounds of the image view, but since the image view is dynamic, i don't see any way around this without using this extension):
// MARK: - Image Scaling.
extension UIImage {
/// Scales an image to fit within a bounds with a size governed by the passed size. Also keeps the aspect ratio.
/// Switch MIN to MAX for aspect fill instead of fit.
///
/// - parameter newSize: newSize the size of the bounds the image must fit within.
///
/// - returns: a new scaled image.
func scaleImageToSize(newSize: CGSize) -> UIImage {
var scaledImageRect = CGRect.zero
let aspectWidth = newSize.width/size.width
let aspectheight = newSize.height/size.height
let aspectRatio = max(aspectWidth, aspectheight)
scaledImageRect.size.width = size.width * aspectRatio;
scaledImageRect.size.height = size.height * aspectRatio;
scaledImageRect.origin.x = (newSize.width - scaledImageRect.size.width) / 2.0;
scaledImageRect.origin.y = (newSize.height - scaledImageRect.size.height) / 2.0;
UIGraphicsBeginImageContext(newSize)
draw(in: scaledImageRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage!
}
}
This is my current function I'm using for drawing the image on screen to be able to save it to camera roll (this function combines two images, a frame and an image from camera roll:
func drawImagesAndText() {
let renderer = UIGraphicsImageRenderer(size: CGSize(width: imageView.bounds.size.width, height: imageView.bounds.size.height))
img = renderer.image { ctx in
// var newSize = currentImage.scaleImageToSize
let bgImage = currentImage
bgImage?.draw(in: CGRect(x: 0, y: 0, width: imageView.bounds.size.width, height: imageView.bounds.size.height))
frames = UIImage(named: framesAr)
frames?.draw(in: CGRect(x: 0, y: 0, width: imageView.bounds.size.width, height: imageView.bounds.size.height))
}
}
All the tutorials I've found on how to use extensions don't cover how to pass in and out variables like this one requires. Any insight would be greatly appreciated.
I understand that you don't know how to use the extension, is that correct? Since it just adds a function to every UIImage, you can simply call it on your image like this: currentImage.scaleImageToSize(newSize: someSize) and pass the size you want the image to fit into.
Dorian Roy was telling me to use that call in place of using just "currentImage", and that's what worked!
(I commented on his initial answer saying I was having issues because I was trying to use the return value from the extension itself in place of "currentImage")