I got an AsyncImage and want to use UIImageWriteToSavedPhotosAlbum for save my AsyncImage to user library.
I used Paul Hudson course for find what to do but it downloads an empty white picture on library on my iPhone and on simulator.
Here's the code:
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .blue
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}`
On the view:
`Button("Save to image") {
let image = AstronomyImageView(astronomy: article) //AsyncImage
let uiImage: UIImage = image.snapshot()
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}```
Related
I'm using extension to View I found on hackingwithswift.com:
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
I'm using in the following way:
I have object of type Canvas, which contains some drawing and I also added a border to make it more visible. Then I'm saving it to Photo Album, but finall photo is is out of position in relation to the original. I'm attaching screenshot of my view and finall photo.
let image = canvas.snapshot()
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
Add .edgesIgnoringSafeArea(.all) to the rootView: self parameter of the UIHostingController init call.
let controller = UIHostingController(rootView: self.edgesIgnoringSafeArea(.all))
That will make the screenshot not be clipped.
SwiftUI 2.0 | Swift 5.4 | Xcode 12.4 | macOS Big Sur 11.4
In iOS there is a way to render SwiftUI views to a UIImage that doesn't work in AppKit, only UIKit:
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
Is there any workaround to get that working on AppKit? The method UIGraphicsImageRenderer(size:) doesn't work on AppKit and there is no equivalent.
If I'm understanding your question correctly, you should be able to use an NSBitmapImageRef to do the trick!
Here's a simple example of generating an NSImage from SwiftUI view (Note: your host view must be visible!):
let view = MyView() // some SwiftUI view
let host = NSHostingView(rootView: view)
// Note: the host must be visible on screen, which I am guessing you already have it that way. If not, do that here.
let bitmapRep = host.bitmapImageRepForCachingDisplay(in: host.bounds)
host.cacheDisplay(in: host.bounds, to: bitmapRep!)
let image = NSImage(size: host.frame.size)
image.addRepresentation(bitmapRep!)
Here's an extension to NSHostingView that should safely make snapshots:
extension NSHostingView {
func snapshot() -> NSImage? {
// Make sure the view is visible:
guard self.window != nil else { return nil }
// Get bitmap data:
let bitmapRep = self.bitmapImageRepForCachingDisplay(in:
self.bounds)
self.cacheDisplay(in: self.bounds, to: bitmapRep!)
// Create NSImage from NSBitmapImageRep:
let image = NSImage(size: self.frame.size)
image.addRepresentation(bitmapRep!)
return image
}
}
I hope this helps. Let me know if you have any questions!
This is the code I am using
extension UIImage {
var ellipseMasked: UIImage? {
guard let cgImage = cgImage else { return nil }
let rect = CGRect(origin: .zero, size: size)
return UIGraphicsImageRenderer(size: size, format: imageRendererFormat)
.image{ _ in
UIBezierPath(ovalIn: rect).addClip()
UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
.draw(in: rect)
}
}
}
This is the image I got
The background color is black.
How can I make the background transparent?
I tried different ways but haven't made it work yet.
You can subclass UIImageView and mask its CALayer instead of clipping the image itself:
extension CAShapeLayer {
convenience init(path: UIBezierPath) {
self.init()
self.path = path.cgPath
}
}
class EllipsedView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.mask = CAShapeLayer(path: .init(ovalIn: bounds))
}
}
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
let iv = EllipsedView(image: profilePicture)
edit/update
If you need to clip the UIImage itself you can do it as follow:
extension UIImage {
var ellipseMasked: UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
defer { UIGraphicsEndImageContext() }
UIBezierPath(ovalIn: .init(origin: .zero, size: size)).addClip()
draw(in: .init(origin: .zero, size: size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
For iOS10+ you can use UIGraphicsImageRenderer.
extension UIImage {
var ellipseMasked: UIImage {
let rect = CGRect(origin: .zero, size: size)
let format = imageRendererFormat
format.opaque = false
return UIGraphicsImageRenderer(size: size, format: format).image{ _ in
UIBezierPath(ovalIn: rect).addClip()
draw(in: rect)
}
}
}
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
profilePicture.ellipseMasked
Here are two solutions using SwiftUI.
This solution can be used to clip the image shape to a circle.
Image("imagename").resizable()
.clipShape(Circle())
.scaledToFit()
This solution can be used to get more of an eclipse or oval shape from the image.
Image("imagename").resizable()
.cornerRadius(100)
.scaledToFit()
.padding()
Below is the code for the rear camera, I tried adding rear camera is activated but I receive an error.
func addImagePickerToContainerView(){
imagePickers = UIImagePickerController()
if UIImagePickerController.isCameraDeviceAvailable( UIImagePickerControllerCameraDevice.front) {
imagePickers?.delegate = self
imagePickers?.sourceType = UIImagePickerControllerSourceType.camera
imagePickers?.cameraDevice = .rear
addChildViewController(imagePickers!)
// Add the child's View as a subview
self.cameraContainerView.addSubview((imagePickers?.view)!)
self.cameraContainerView.addSubview(layerKTP)
imagePickers?.view.frame = cameraContainerView.bounds
imagePickers?.allowsEditing = false
imagePickers?.showsCameraControls = false
imagePickers?.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
Message from debugger: Terminated due to memory issue
Try with below code
extension UIImage
{
func resizeImage(toSize size:CGSize, scale:CGFloat) ->UIImage
{
let imageRect = CGRect(origin: CGPoint(x:0.0, y:0.0), size: size)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
self.draw(in: imageRect)
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage!
}
}
//Use this method like
let resizedImage = selectedImg.resizeImage(toSize:CGSize(width:100.0, height:100.0), scale: UIScreen.main.scale)
I want to create multipage PDF of self.view. I have following viewController with tableView now I want to create the pdf of the view.
This is what I have done but it's create single page pdf only
func pdfDataWithTableView(tableView: UITableView) {
let priorBounds = tableView.bounds
let fittedSize = tableView.sizeThatFits(CGSize(width:priorBounds.size.width, height:tableView.contentSize.height))
tableView.bounds = CGRect(x:0, y:0, width:fittedSize.width, height:fittedSize.height)
let pdfPageBounds = CGRect(x:0, y:0, width:tableView.frame.width, height:self.view.frame.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds,nil)
var pageOriginY: CGFloat = 0
while pageOriginY < fittedSize.height {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
UIGraphicsGetCurrentContext()!.saveGState()
UIGraphicsGetCurrentContext()!.translateBy(x: 0, y: -pageOriginY)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsGetCurrentContext()!.restoreGState()
pageOriginY += pdfPageBounds.size.height
}
UIGraphicsEndPDFContext()
tableView.bounds = priorBounds
var docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
docURL = docURL.appendingPathComponent("myPassBook.pdf")
pdfData.write(to: docURL as URL, atomically: true)
share()
}
On save Button Call Below function
func createImg() -> UIImage {
guard tblView.numberOfSections > 0, tblView.numberOfRows(inSection: 0) > 0 else {
let errorImage = UIImage(named: "Error Image")
return errorImage!
}
UIGraphicsBeginImageContextWithOptions(CGSize(width: tblView.contentSize.width, height: tblView.contentSize.height), false, 0.0)
let context = UIGraphicsGetCurrentContext()
let previousFrame = tblView.frame
tblView.frame = CGRect(x: tblView.frame.origin.x, y: tblView.frame.origin.y, width: tblView.contentSize.width, height: tblView.contentSize.height)
// Draw view in that context
tblView.layer.render(in: context!)
tblView.frame = previousFrame
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
createPDFDataFromImage(image: image)
return image;
}
Create PDF from image and Share
func createPDFDataFromImage(image: UIImage){
let pdfData = NSMutableData()
let imgView = UIImageView.init(image: image)
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
UIGraphicsBeginPDFContextToData(pdfData, imageRect, nil)
UIGraphicsBeginPDFPage()
let context = UIGraphicsGetCurrentContext()
imgView.layer.render(in: context!)
UIGraphicsEndPDFContext()
//try saving in doc dir to confirm:
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
let path = dir?.appendingPathComponent("file.pdf")
do {
try pdfData.write(to: path!, options: NSData.WritingOptions.atomic)
} catch {
print("error catched")
}
let activityViewController = UIActivityViewController(activityItems: [pdfData] , applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}