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
Related
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.
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.
I’m trying to rotate a downloaded image that comes in PNG format with a 4:3 ratio that’s landscape.
I need the image rotated by 90 degrees so it’s portrait with the same dimensions.
I tried the .transform function which worked to start with but doesn’t anymore after adding a scrollView with a lot of settings to allow it to zoom and pan, Id rather not go down the route of editing the srollView content as it took a long time to get all the constrains to work properly to allow free zoom and pan.
After downloading my image I save it to app file. Then it’s loaded for display in another function.
Is it possible to rotate the downloaded file whilst saving so it can be retrieved in the correct way?
I found this in another post which I believe would work with the downloaded image, how can I change the orientation for my need?
func normalizedImage() -> UIImage
{
if (self.imageOrientation == UIImageOrientation.Up) {
return self;
}
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
self.drawInRect(rect)
let normalizedImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
return normalizedImage;
}
If you display the UIImage in an image view you can try modifying the orientation.
It is basically the same UIImage but with a flag that is interpreted by UIImageView to rotate the image display.
let rotated = UIImage(cgImage: image.cgImage!, scale: 1, orientation: .right)
If you really want to save a new image to disk, you will need to actually redraw the image. Some info is already available here : How to Rotate a UIImage 90 degrees?
When I take a screenshot to share the current view of my device (iPhone), it only takes the upper part of it, and when I scroll down to the bottom (of my tableview at runtime), the screenshot is blank as if not capturing the current view on the device - I hope I am explaining alright there.
Am I missing anything?
func captureScreen() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image
}
Change your code to the following :
func captureScreen() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image
}
Note the change from bound to frame in your code. The last argument in UIGraphicsBeginImageContextWithOptions has to do with the scale. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen
Learn more about it here
Working through a Core Graphics tutorial http://www.raywenderlich.com/76285/beginning-core-image-swift
Theres one part where you need to preserve the UIImageOrientation (of course!). However I'm noticing something VERY quirky and I'm not sure what the cause is.
Here is the code block
#IBAction func amountSliderValueChanged(sender: UISlider) {
let sliderValue = sender.value
filter.setValue(sliderValue, forKey: kCIInputIntensityKey)
let outputImage = filter.outputImage;
let cgimg = context.createCGImage(filter.outputImage, fromRect: filter.outputImage.extent())
let newImage = UIImage(CGImage: cgimg, scale:1, orientation: UIImageOrientation.UpMirrored)
// let newImage = UIImage(CGImage: cgimg)
println("New image is \(newImage)")
self.imageView.image = newImage
}
When I change the image to certain rotations, say Up, the image appears. However when I change to right or left it moves outside the UIImageView (and if I set the scale up really high I can see parts of it coming back on the screen).
I am not rotating the UIImageView, only the UIImage. Before, when I was doing this in Objective-C I never had this issue. I would just set the UIImage rotation and it would always be 0,0 in the UIImageView.
Using Swift (or perhaps something else?) seems to result in different behaviour.
Can you think of any reason why rotating the image moves it way from the its 0,0 of its UIImageView, and what I can do fix that?
Thanks!