Restrict size of NSTextAttachment image - swift

I am trying to insert an image in UITextView. I have used the following code.
extension TextView {
func add(image: UIImage) {
let attachment = NSTextAttachment()
attachment.image = image
attachment.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
let attString = NSAttributedString(attachment: attachment)
self.attributedText = attString
}
}
The parent UIViewController calls add(image: UIImage).
In func textViewDidChange(_ textView: UITextView), I save the attributedText in CoreData as a Transforable NSAttributedString. I use
NSAttributedStringTransformer for Transformer
The image's size is 40X40 when added. The image has also the same size when I dismiss the parent UIViewController and present it back. However, if I quit the app and relaunch it, the image is not 40X40. It is larger than then UIScreen's size.
How to set size of the image to be 40X40 even after quitting the app?

By setting attachment bounds, you are not resizing the actual image, just the display bounds. Looks like the attributed string transformer doesn’t serialize the bounds you set. You will have to either resize the image directly, or extend the transformer to add the bounds after deserializing from data.
Edit: I see that NSAttributedStringTransformer is not an Apple-provided transformer. So take a look at the source code and see why the bounds are not serialized properly.

Related

Drawing (to PDF) an UIImage that has been written to a file does not give the same result as drawing the original UIImage

Some context first:
I simply draw a UIImage to a PDFPage by subclassing PDFPage and overriding draw(with box,to context):
override func draw(with box: PDFDisplayBox, to context: CGContext) {
/* Draw image on PDF */
UIGraphicsPushContext(context)
// Change the PDF context to match the UIKit coordinate system.
context.translateBy(x: 0, y: pageBounds.height)
context.scaleBy(x: 1, y: -1)
context.interpolationQuality = .high
// The important line is here: drawing the image
self.myImage.draw(in: CGRect(x: leftMargin, y: topMargin, width: fittedImageSize.width, height: fittedImageSize.height))
}
where self.myImage contains a UIImage. So far so good.
The problem -> if I persist the image to save memory
If I init my CustomPDFPage with the original UIImage from memory --> I get a PDF file with a reasonable size, everything works well
However: if I persist the image using pngData(), then reload it using UIImage(contentsOfFile: url.path) for drawing, my PDF file is suddenly MUCH more heavier in size.
Writing the image to TMP:
let urlToWrite = tmpDir.appendingPathComponent(fileName)
do {
if let tmpData = image.png() {
DLog("TMPDATA SIZE = \(tmpData.count). Image dimensions = \(image.size) with scale = \(image.scale)")
}
try image.pngData()?.write(to: urlToWrite)
self.tmpImgURL = urlToWrite
} catch {
DLog("ERROR: could not write image to \(urlToWrite). Error is \(error)")
}
Reloading the image into memory:
var image = UIImage(contentsOfFile: self.tmpImgURL.path)
--> using that image to draw the PDF increases the PDF size dramatically.
Inspecting the UIImage size, the scale, and the bytes count of the image before writing to file and after reading to file give the exact same values.
So the reason behind this mess is because the user has the possibility to choose to reduce the quality of the image.
In that case, the source UIImage was an image recreated from jpegData (that was used to apply compression).
In short, calling UIImage.pngData() after UIImage.jpegData(...) is not a good idea. Just write directly the jpegData when the image might have been compressed.

Adjust imageview size according to image coming from server and then adjusting other labels

I am having a tableview cell containing an ImageView, two labels. I need to adjust the size of image view in accordance with the image coming from server and then adjust the labels.
Please refer to the cell screen shot.
I am downloading image using
cell.imageViewKnowledgeFeed.sd_setImage(with: URL(string: urlStr as String), completed: { (image, error,imageCacheType , nil) in
cell.imageViewKnowledgeFeed.image = image
print(image?.size)
cell.imageViewKnowledgeFeed.frame = CGRect(x: cell.imageViewKnowledgeFeed.frame.origin.x,
y: 100,
width: cell.imageViewKnowledgeFeed.frame.size.width,
height: cell.imageViewKnowledgeFeed.frame.size.height)
// tableView.reloadRows(at: [indexPath], with: .none)
})
Constraints are as follows
I am using content mode AspectFill into UIImageView.
You need to create outlets for constraints and they set them programmatically in accordance with size of the image and afterwards you can call layoutSubviews() to adjust other labels and associated designs.

macOS - Programmatically display an image using NSImageView

Using a Swift app targeting the macOS platform, I am trying to programmatically create an NSImageView object, assign an image to it, and then display it, using:
let imageViewObject = NSImageView();
let img = NSImage(cgImage: winCGImage, size: NSZeroSize);
imageViewObject.image = img
self.window.contentView?.addSubview(imageViewObject)
However, no image is displayed when I run this.
On the debugger, I can see that imageViewObject.image (i.e. img) has a valid image, and I can view it using the eye icon.
What am I missing?
You are creating an image view with a zero frame, first create the image and then the view passing the proper size.
let img = NSImage(cgImage: winCGImage, size: NSZeroSize)
let imageViewObject = NSImageView(frame: NSRect(origin: .zero, size: img.size))
imageViewObject.image = img
Avoid to create views with the default initializer ().

Programmatically place partial image over another in UIView using Swift 3

I just started working on Swift last week and i need a suggestion if the following approach is right ways of laying partial image on top of another image.
I have a UIView in which I am creating 3 images programmatically. Left arrow image, middle mobile image and right arrow image as shown below. Can I partially place arrow images 50% on the mobile image?
I have tried:
func setupUI(){
let mobileImage = UIImage(named: "mobile")
let arrowImage = UIImage(named: "arrow")
middleView = UIImageView(frame: CGRect(x: arrowImage!.size.width/2, y:0 , width:mobileImage!.size.width, height:mobileImage!.size.height))
middleView.image = mobileImage
middleView.layer.borderWidth = 1.0
middleView.layer.cornerRadius = 5.0
self.addSubview(middleView)
let yForArrow = mobileImage!.size.height - arrowImage!.size.height
leftArrow = UIImageView(frame: CGRect(x: 0, y:yForArrow, width:arrowImage!.size.width, height:arrowImage!.size.height))
leftArrow.image = arrowImage
self.addSubview(leftArrow)
let rightArrowX = mobileImage!.size.width
rightView = UIImageView(frame: CGRect(x: rightArrowX, y:yForArrow, width:arrowImage!.size.width, height:arrowImage!.size.height))
rightView.image = arrowImage
self.addSubview(rightView)
}
*At start it was not working, as i forgot to add setupUI() in init method. As shown in answer bellow.
Is setting frame correct way of doing it OR i should be using constraints?
To me it looks bad approach as i am hard coding the numbers in CGRect.
*This image is created in MS paint to show what it should look on iPhone.
I found the problem i missed adding setupUI() in init method.
SetupUI programatically adds images on UIView. As it was missing so no image was appearing in the iPhone simulator.
override init(frame: CGRect) {
super.init(frame: frame)
setupUI() // Code to add images in UIView
}

Create bitmap of UITextView's entire content

I'm trying to get a bitmap of a UITextView's content. I'm able to get a bitmap of the UITextView's content that is currently on the screen with:
UIGraphicsBeginImageContext(myTextView.bounds.size);
[myTextView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
myImageView.image=resultingImage;
UIGraphicsEndImageContext();
But I want a bitmap of ALL the content, not just the content visible on the screen. Is this possible? Note that I don't want only the text of the UITextView, I want a bitmap of the content itself. I searched around and found how do do this in Android, with getDrawingCache, but couldn't find anything for objective c.
One way to do this is to make a copy of the UITextView and then resize the copy to it's content size (as long as the content size is bigger than the frame size).
In Swift it looks like this:
func imageFromTextView(textView: UITextView) -> UIImage {
// Make a copy of the textView first so that it can be resized
// without affecting the original.
let textViewCopy = UITextView(frame: textView.frame)
textViewCopy.attributedText = textView.attributedText
// resize if the contentView is larger than the frame
if textViewCopy.contentSize.height > textViewCopy.frame.height {
textViewCopy.frame = CGRect(origin: CGPointZero, size: textViewCopy.contentSize)
}
// draw the text view to an image
UIGraphicsBeginImageContextWithOptions(textViewCopy.bounds.size, false, UIScreen.mainScreen().scale)
textViewCopy.drawViewHierarchyInRect(textViewCopy.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
This allows you to get an image of all of the content, even the part that in not visible without scrolling.
Notes
My somewhat more general answer is here.
The textViewCopy is only a copy of the frame and the attributed text. That should be enough to get a full image. However, if for some reason a more exact copy is needed, then one could do the following: (explanation)
let tempArchive = NSKeyedArchiver.archivedDataWithRootObject(textView)
let textViewCopy = NSKeyedUnarchiver.unarchiveObjectWithData(tempArchive) as! UITextView