Taking a snapshot of UIImageView instead of main View - swift

I have in my app in my View a Button for taking snapshot with below code
open func takeScreenshot(_ shouldSave: Bool = true) -> UIImage? {
var screenshotImage :UIImage?
let layer = UIApplication.shared.keyWindow!.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
guard let context = UIGraphicsGetCurrentContext() else {return nil}
layer.render(in:context)
screenshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let image = screenshotImage, shouldSave {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
return screenshotImage
}
But in the same View is UIImageView and I want to take a snapshot of this UIImageView. How should I change the code? When I take a snapshot with this code, it will save only black screen with button for play/pause video and with button for taking snapshot.

Instead you using
let layer = UIApplication.shared.keyWindow!.layer
Just use your imageview Layer
let layer = YourImageView.layer

Related

Swift, blurred images in UITableViewCell does not run smooth

what i am trying to do is show a list with blurred images from the web. This works fine with this code in my custom UITableViewCell
func blurImage(image:UIImage, imageView: UIImageView) {
DispatchQueue.global(qos: .background).async {
let inputImage = CIImage(image: image)
let originalOrientation = image.imageOrientation
let originalScale = image.scale
let filter = CIFilter(name: "CIGaussianBlur")
filter?.setValue(inputImage, forKey: kCIInputImageKey)
filter?.setValue(15.0, forKey: kCIInputRadiusKey)
let outputImage = filter?.outputImage
var cgImage:CGImage?
if let outputImage = outputImage{
cgImage = self.context.createCGImage(outputImage, from: (inputImage?.extent)!)
}
DispatchQueue.main.async {
if let cgImageA = cgImage{
imageView.image = UIImage(cgImage: cgImageA, scale: originalScale, orientation: originalOrientation)
}
}
}
}
the problem is that the blur calculation takes sometime, and allthought its on a BG thread the scrolling is not that fast and smooth as if i don't have the blur effect at all.
Is there a way to make it run smoother OR to show a placeholder image until the blurred image is ready to been draw again resulting in smooth scrolling?
Step 1) Don't use qos: .background for user-initiated tasks. Docs say: Background tasks have the lowest priority of all tasks. Assign this class to tasks or dispatch queues that you use to perform work while your app is running in the background.

IMessage MSSticker view created from UIView incorrect sizing

Hey I have been struggling with this for a couple of days now and can't seem to find any documentation out side of the standard grid views for MSStickerView sizes
I am working on an app that creates MSStickerViews dynamically - it does this via converting a UIView into an UIImage saving this to disk then passing the URL to MSSticker before creating the MSStickerView the frame of this is then set to the size of the original view.
The problem I have is that when I drag the MSStickerView into the messages window, the MSStickerView shrinks while being dragged - then when dropped in the messages window, changes to a larger size. I have no idea how to control the size when dragged or the final image size
Heres my code to create an image from a view
extension UIView {
func imageFromView() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
self.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
return nil
}
}
And here's the code to save this to disk
extension UIImage {
func savedPath(name: String) -> URL{
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let filePath = "\(paths[0])/name.png"
let url = URL(fileURLWithPath: filePath)
// Save image.
if let data = self.pngData() {
do {
try data.write(to: url)
} catch let error as NSError {
}
}
return url
}
}
finally here is the code that converts the data path to a Sticker
if let stickerImage = backgroundBox.imageFromView() {
let url = stickerImage.savedPath(name: textBox.text ?? "StickerMCSticker")
if let msSticker = try? MSSticker(contentsOfFileURL: url, localizedDescription: "") {
var newFrame = self.backgroundBox.frame
newFrame.size.width = newFrame.size.width
newFrame.size.height = newFrame.size.height
let stickerView = MSStickerView(frame: newFrame, sticker: msSticker)
self.view.addSubview(stickerView)
print("** sticker frame \(stickerView.frame)")
self.sticker = stickerView
}
}
I wondered first off if there was something I need to do regarding retina sizes, but adding #2x in the file just breaks the image - so am stuck on this - the WWDC sessions seem to show stickers being created from file paths and not altering in size in the transition between drag and drop - any help would be appreciated!
I fixed this issue eventually by getting the frame from the view I was copying's frame then calling sizeToFit()-
init(sticker: MSSticker, size: CGSize) {
let stickerFrame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
self.sticker = MSStickerView(frame: stickerFrame, sticker: sticker)
self.sticker.sizeToFit()
super.init(nibName: nil, bund
as the StickerView was not setting the correct size. Essentially the experience I was seeing was that the sticker size on my view was not accurate with the size of the MSSticker - so the moment the drag was initialized, the real sticker size was implemented (which was different to the frame size / autoLayout I was applying in my view)

AirPrint multiple MVCs in swift

I am building a form style app that allows the users to fill in textfields. once they fill up the page it will continue onto a second view. This will be about 5 pages worth of patient forms. when they get to the last page I want to have a print button that prints all 5 or 6 pages they have just filled out. I am trying to use the AirPrint code that I have recently found on here, but it will only let me do the current view.
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
// drawHierarchy(in: self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
let pInfo = UIPrintInfo(dictionary:nil)
pInfo.outputType = UIPrintInfoOutputType.general
pInfo.jobName = "Print Patient Forms"
//Set up print controller
let printController = UIPrintInteractionController.shared
printController.printInfo = pInfo
//Assign a UIMage version of my UIView as a printing Item
printController.printingItem = self.view
var pInfo : UIPrintInfo = UIPrintInfo.printInfo()
pInfo.orientation = UIPrintInfoOrientation.portrait
let formatter = UIMarkupTextPrintFormatter(markupText: textView.text!)
printController.showsNumberOfCopies = false
printController.showsPageRange = true
printController.printFormatter = formatter printController.present(animated: true, completionHandler: nil)
How do I go about letting this print all the pages?

iOS 11 doesn't grab screen from MKMapView

I have an app that displays the locations the user has walked on an MKMapView. When the user leaves the map view the app grabs the screen and saves the image on disk. Up til iOSS 10.3 this method was always successful. With iOS 11.0 the screen grab is a blank image. I get no notification from xcode that there were some changes and that I need to adjust the code.
Interestingly, screen grabs from text pages are still grabbed and saved successfully.
Did anyone encounter the same problem and got the solution?
The code that has always been successful up til now, is:
override func viewWillDisappear(_ animated: Bool) {
//Set the full file name under which the track will be saved.
let fileBaseName = self.imageName.appending(String(describing: (self.display?.trackDate)!))
let fileFullName = fileBaseName.appending(".png")
//Check if the image already has been saved
if !AuxiliaryObjects.shared.getImageFileName(with: fileFullName ) {
//Create the sizes of the capture
let screenRect = self.trackMapView.frame
let screenSize = screenRect.size
let screenScale = UIScreen.main.scale
var grabRect = self.trackMapView.convertRegion(self.mapRegion, toRectTo: self.view)
var heightAdjustment : CGFloat = 0.0
//Grab the image from the screen
UIGraphicsBeginImageContextWithOptions(screenSize, false, screenScale)
self.trackMapView.drawHierarchy(in: screenRect, afterScreenUpdates: true)
let myImage = UIGraphicsGetImageFromCurrentImageContext()
grabRect.origin.x *= (myImage?.scale)!
grabRect.origin.y *= (myImage?.scale)!
grabRect.size.width *= (myImage?.scale)!
grabRect.size.height *= (myImage?.scale)!
let grabImage = (myImage?.cgImage)!.cropping(to: grabRect)
let mapImage = UIImage(cgImage: grabImage!)
UIGraphicsEndImageContext()
AuxiliaryObjects.shared.save(image: mapImage, with: fileFullName, and: self.imageName)
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
} else {
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
}
}
I submitted a code level support request at Apple to get the answer to the question. Apple does not support the use of drawHierarhy in grabbing a MapKit screen. The way to go is using the MKMapSnapshotter utility to create an MKMapSnapshot and then draw in the lines and annotations by converting all the map coordinates to view coordinates.
Since this gave me some problems with getting the a mirrored image and translating the coordinates properly, I decided to use the layer method render(in: CGContext) this provided me a well functioning very efficient screen grab.
func creatSnapshot(with fileName: String) {
UIGraphicsBeginImageContextWithOptions(self.trackMapView.frame.size, false, UIScreen.main.scale)
let currentContext = UIGraphicsGetCurrentContext()
self.trackMapView.layer.render(in: currentContext!)
let contextImage = (UIGraphicsGetImageFromCurrentImageContext())!
UIGraphicsEndImageContext()
let region = self.trackMapView.region
var cropRect = self.trackMapView.convertRegion(region, toRectTo: self.trackMapView.superview)
cropRect.origin.x *= contextImage.scale
cropRect.origin.y *= contextImage.scale
cropRect.size.height *= contextImage.scale
cropRect.size.width *= contextImage.scale
let cgMapImage = contextImage.cgImage?.cropping(to: cropRect)
let mapImage = UIImage(cgImage: cgMapImage!)
AuxiliaryObjects.shared.save(image: mapImage, with: fileName, and: self.imageName)
self.displayTrack?.displayImage = AuxiliaryObjects.shared.getImage(with: fileName, with: self.tableImageRect)!
NotificationCenter.default.post(name: self.imageSavedNotification, object: self)
}

Capture visible portion of UIScrollView and subview

I have zoomable image in a UIScrollview. Once the image is scrolled to the user's liking, the scrollview is locked. A UITextField can then be edited, and is (currently) added to the UIImageView. The context of the image is captured and saved.
Without scrolling, this works fine, however, the context is based on the frame of the UIImageView. How can it capture the visible portion of the screen instead, leaving out the navigation bar and toolbar? The function used to save the image is below.
#IBAction func saveButtonPressed(sender: AnyObject) {
let newImage = imageStore.createImage(textLabelBottom.text, imageName: "image1") { () -> UIImage in
// Can't add the textLabel to imageView because its size changes
self.imageView.addSubview(self.textLabelBottom)
UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, false, 0.0)
let ctx = UIGraphicsGetCurrentContext()
self.imageView.layer.renderInContext(ctx)
self.imageToSave = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return self.imageToSave
}
imageStore.saveImage(newImage)
self.showActivityViewController()
}
I wan't to save the visible portion of the image based on its position in the scrollview with the textLabel in a static spot. The code currently adds textLabel to the same point in the image regardless of zoom, with the saved image either very small or very large based on the UIScrollView.
Update: I changed the code to the following:
#IBAction func saveButtonPressed(sender: AnyObject) {
let newImage = imageStore.createImage(textLabelBottom.text, imageName: "image1") { () -> UIImage in
// Can't add the textLabel to imageView because its size changes
self.imageView.addSubview(self.textLabelBottom)
UIGraphicsBeginImageContextWithOptions(self.imageScrollView.bounds.size, false, UIScreen.mainScreen().scale)
let ctx = UIGraphicsGetCurrentContext()
let offset: CGPoint = self.imageScrollView.contentOffset
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -offset.x, -offset.y)
self.imageScrollView.layer.renderInContext(ctx)
self.imageToSave = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return self.imageToSave
}
imageStore.saveImage(newImage)
self.showActivityViewController()
}
This works properly, but the text field is obviously out of place. Do I add it to a specific CGPoint within the context?
Update 2:
It's my understanding that CGContextTranslateCTM changes the coordinate system to that of the layer you're working on. In this case, the new {0, 0} would be at the offset of the scrollview. When I want to place my textfield into the context, though, it doesn't show up in the right place (offscreen). Trying something like this:
#IBAction func saveButtonPressed(sender: AnyObject) {
let newImage = imageStore.createImage(textLabelBottom.text, imageName: "image1") { () -> UIImage in
// Can't add the textLabel to imageView because its size changes
self.imageView.addSubview(self.textLabelBottom)
UIGraphicsBeginImageContextWithOptions(self.imageScrollView.bounds.size, false, UIScreen.mainScreen().scale)
let ctx = UIGraphicsGetCurrentContext()
let offset: CGPoint = self.imageScrollView.contentOffset
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -offset.x, -offset.y)
self.imageScrollView.layer.renderInContext(ctx)
//Move to the correct point and render
CGContextMoveToPoint(ctx, /*point1*/, /*point2*/)
self.textLabelBottom.layer.renderInContext(ctx)
self.imageToSave = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return self.imageToSave
}
imageStore.saveImage(newImage)
self.showActivityViewController()
}
What else do I need to do to the context to do this properly?