How to save a layered image using a extension function - uiview

I am using a extension function to save a uiview as a uiimage. The code works to save the uiimage. However what I am trying to do is save a transparent image over the image being saved to the photo gallery. So I am trying to save a layered image using a extension function. Right now only the uiivew is being save and the 2nd layer is not being saved.
class ViewController: UIViewController,UINavigationControllerDelegate {
#IBAction func press(_ sender: Any) {
let jake = drawingView.takeSnapshotOfView(view: drawingView)
guard let selectedImage = jake else {
print("Image not found!")
return
}
UIImageWriteToSavedPhotosAlbum(selectedImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}}
func takeSnapshotOfView(view:UIView) -> UIImage? {
UIGraphicsBeginImageContext(CGSize(width: view.frame.size.width, height: view.frame.size.height))
view.drawHierarchy(in: CGRect(x: 0.0, y: 0.0, width: view.frame.size.width, height: view.frame.size.height), afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let star:UIImage = UIImage(named: "e.png")!
let newSize = CGSize(width: star.size.width, height: star.size.height )
UIGraphicsBeginImageContextWithOptions(newSize, false, star.scale)
star.draw(in: CGRect(x: newSize.width/12,
y: newSize.height/8,
width: newSize.width/1.2,
height: newSize.height/1.2),
blendMode:CGBlendMode.normal, alpha:1)
UIGraphicsEndImageContext()
return image
}

Here is a UIView extension that takes in CGRect and UIImage, and alternitavely you can also supply it another CGRect or CGSize to make it more dynamic for the watermark placement/size
extension UIView {
/// Takes a screenshot of a UIView, with an option to clip to view bounds and place a waterwark image
/// - Parameter rect: offset and size of the screenshot to take
/// - Parameter clipToBounds: Bool to check where self.bounds and rect intersect and adjust size so there is no empty space
/// - Parameter watermark: UIImage of the watermark to place on top
func screenshot(for rect: CGRect, clipToBounds: Bool = true, with watermark: UIImage? = nil) -> UIImage {
var imageRect = rect
if clipToBounds {
imageRect = bounds.intersection(rect)
}
return UIGraphicsImageRenderer(bounds: imageRect).image { _ in
drawHierarchy(in: CGRect(origin: .zero, size: bounds.size), afterScreenUpdates: true)
watermark?.draw(in: CGRect(origin: imageRect.origin, size: CGSize(width: 32, height: 32))) // update origin to place watermark where you want, with this update it will place it in top left or screenshot.
}
}
}
You can call it like so:
let image = self.view.screenshot(for: CGRect(x: 0, y: 0, width: 200, height: 200), with: UIImage(named: "star"))
This will work for all subviews of the view that calls screenshot(...)
For a anyone using the above extension, I added additional info in this answer

Related

Using UIGraphicsImageRenderer to resize UIImage to fixed pixel size

func thumbImage(image: UIImage) -> UIImage {
let cgSize: CGSize = CGSize(width: 100, height: 100)
let thumb = UIGraphicsImageRenderer(size: cgSize)
return thumb.image { _ in
image.draw(in: CGRect(origin: .zero, size: cgSize))
}
}
The final image is 300x300.
I would like, not matter the iPhone screen resolution, to have the image to be 100x100 (it is a square image of course).
How modify this code to achieve this result?
(I'm open to alternate ways of achieving this)
func thumbImage(image: UIImage, pxWidth: Int, pxHeight:Int ) -> UIImage {
let cgSize: CGSize = CGSize(width: pxWidth, height: pxHeight)
let rect = CGRect(x: 0, y: 0, width: pxWidth, height: pxHeight)
UIGraphicsBeginImageContextWithOptions(cgSize, false, 1.0)
image.draw(in: rect)
let thumb = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let compressedThumb = thumb!.jpegData(compressionQuality: 0.70)
return UIImage(data: compressedThumb!)!
}
This alternative with UIGraphicsBeginImageContextWithOptions works and keeps the code as short as the initial one. (I also added some compression and conversion code).

Screenshot a view with label and transparent background in iOS and upload it to server

I want to screenshot a view and create an UIImage from it. I want the transparency attribute of the view to be maintained in my image. I tried this method after creating an extension of UIImage but the background is not transparent in the resultant image when uploaded to the server.
Kindly help or point me if I am doing something wrong!! This means that the resultant png was not having transparency.
class func createTransparentImageFrom(label: UILabel, imageSize: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(imageSize, false, 2.0)
let currentView = UIView.init(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
currentView.backgroundColor = UIColor.clear
currentView.addSubview(label)
currentView.layer.render(in: UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
This was due to the fact that when we render a view the image is JPEG/JPG, so we need to convert it to png in order to get layer information.
Try this :
extension UIImage {
class func createTransparentPNGFrom(label: UILabel, imageSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
let currentView = UIView.init(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
currentView.backgroundColor = UIColor.clear
currentView.addSubview(label)
currentView.layer.render(in: UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let newImg = img else {
return nil
}
guard let imageData = UIImagePNGRepresentation(newImg) else {
return nil
}
return UIImage.init(data: imageData)
}
}
Swift 5 UIImage extension: screen UIView with transparent background
this is for taking a screenshot of a view which should have .backgroundColor = .clear
extension UIView {
func takeSnapshotOfView() -> UIImage? {
let size = CGSize(width: frame.size.width, height: frame.size.height)
let rect = CGRect.init(origin: .init(x: 0, y: 0), size: frame.size)
UIGraphicsBeginImageContext(size)
drawHierarchy(in: rect, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let imageData = image?.pngData() else {
return nil
}
return UIImage.init(data: imageData)
}
}
You need to put opaque value true for transparent result
Extension of UIView Of transparent screenshot
func screenShot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, UIScreen.main.scale)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}

How to get a screenshot of the View (Drawing View) used for drawing using UIBezeir path

I have a Drawing View which is on a Scroll View. After drawing is completed I need a screenshot of the drawing which I will be uploading to the server.
I am using UIBezeir path to draw on the view.
let path = UIBezierPath()
for i in self.drawView.path{
path.append(i)
}
self.drawView.path is an NSArray with all the bezeir paths of the drawing.
But when I use the bounding box of this path and get max and min values of coordinates and try to capture a screenshot I get this
var rect:CGRect = CGRect(x: path.bounds.minX, y: path.bounds.minY, width: path.bounds.maxX, height: path.bounds.maxY)
I also tried to give the bounds of the path itself
let rect:CGRect = CGRect(x: path.bounds.origin.x - 5, y: path.bounds.origin.y - 5, width: path.bounds.size.width + 5, height: path.bounds.size.height + 5)
Just for reference I tried using this rect and create a view (clear color with border layer) and placed it over the Drawing, it work pretty fine but when I try to capture an image it goes out of bounds
This is the function I am using to capture the screen
func imgScreenShot(bounds:CGRect) -> UIImage{
let rect: CGRect = bounds
self.drawView.isOpaque = false
self.drawView.backgroundColor = UIColor.clear
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
var context: CGContext? = UIGraphicsGetCurrentContext()
if let aContext = context {
self.drawView.layer.render(in: aContext)
}
var capturedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//let finalImage = scaleImage(image: capturedImage)
return capturedImage!
}
I am also tried getting a UIView with this function
let vw = self.drawView.resizableSnapshotView(from: rect, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero)
This gives me a perfect UIView with the drawing in that, but again when I try to convert the UIView to UIImage using the function giving the views bounds, I get a blank image.
Can anyone suggest what I am doing wrong or any other solution for how I can get this, bounds of image starting right exactly at the bounds of the drawing
let vw = self.drawView.resizableSnapshotView(from: rect2, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero)
vw?.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
vw?.layer.borderColor = UIColor.red.cgColor
vw?.layer.borderWidth = 1
self.drawView.addSubview(vw!)
let image = vw?.snapshotImage
let imgView = UIImageView(frame: CGRect(x: 250, y: 50, width: 100, height: 100))
imgView.layer.borderColor = UIColor.gray.cgColor
imgView.layer.borderWidth = 1
self.drawView.addSubview(imgView)
Make an extension of UIView and UIImage , so in whole application lifecycle you can use those methods(which one i will be describe at below) for capture the screenshort of any perticular UIView and resize the existing image(if needed).
Here is the extension of UIView :-
extension UIView {
var snapshotImage : UIImage? {
var snapShotImage:UIImage?
UIGraphicsBeginImageContext(self.frame.size)
if let context = UIGraphicsGetCurrentContext() {
self.layer.render(in: context)
if let image = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
snapShotImage = image
}
}
return snapShotImage
}
}
Here is the extension of UIImage :-
extension UIImage {
func resizeImage(newSize:CGSize) -> UIImage? {
var newImage:UIImage?
let horizontalRatio = newSize.width / size.width
let verticalRatio = newSize.height / size.height
let ratio = max(horizontalRatio, verticalRatio)
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContext(newSize)
if let _ = UIGraphicsGetCurrentContext() {
draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
if let image = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
newImage = image
}
}
return newImage
}
}
How to use those functions in our desired class ?
if let snapImage = yourUIView.snapshotImage {
///... snapImage is the desired image you want and its dataType is `UIImage`.
///... Now resize the snapImage into desired size by using this one
if let resizableImage = snapImage.resizeImage(newSize: CGSize(width: 150.0, height: 150.0)) {
print(resizableImage)
}
}
here yourUIView means , the one you have taken for drawing some inputs. it can be IBOutlet as well as your UIView (which you have taken programmatically)

How apply and move a UIImageView layered above another UIImageView Swift3

i read here many guides how to create a new image merging two existing ones, using the UIGraphics and the layer.render methods for the two UIImageViews, and finally i can create an then save my new image. The problem is that i can't understand how to put the second UIImageView where i want, at the bottom for example. I 'll post now a image of an merged image and the function that my code run making this possible.
Captured merged photo
And here's my code that do the trick:
extension UIImage {
class func imageWithWatermark(image1: UIImageView, image2: UIImageView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image1.bounds.size, false, 0.0)
let frame = image1.frame
image2.frame = CGRect(x: 0, y: frame.size.height * 0.80, width: frame.size.width, height: frame.size.height * 0.20 )
image1.layer.render(in: UIGraphicsGetCurrentContext()!)
image2.layer.render(in: UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}
And then my func that saves the merged image:
func addWatermark() {
let newImage = UIImage.imageWithWatermark(image1: cameraPreview, image2: provaImage)
UIImageWriteToSavedPhotosAlbum(newImage, nil,nil,nil)
}
You can use this function which merge two images and the second will be replaces on bottom
func mergeTwoImageSeconInBottom(backgroundImage: UIImage, imageOnBottom: UIImage) -> UIImage {
let size = YOUR_CG_SIZE
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
backgroundImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
imageOnBottom.draw(at: CGPoint(x: (size.width - imageOnBottom.size.width) / 2, y: size.height - imageOnBottom.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}

How do I draw on an image in Swift?

I need to be able to programmatically draw on an image, and save that image for later use. Say, draw a line on specific x and y coordinates on the image, save the image, and display it onto a simple view controller. How would I go about doing this in Swift? (Preferably Swift 2, I am still in development and haven't updated my mac to Sierra)
Update: Possibly something to do with converting a UIImage to a CGLayer, drawing on it, and then converting it back to a UIImage.
All you need to do is create and get an Image Context object and access all its powerful drawing methods. You can learn more about the CGContext object features here.
This function draws a line and a circle on an UIImage and returns the modified image:
Swift 4
func drawOnImage(_ image: UIImage) -> UIImage {
// Create a context of the starting image size and set it as the current one
UIGraphicsBeginImageContext(image.size)
// Draw the starting image in the current context as background
image.draw(at: CGPoint.zero)
// Get the current context
let context = UIGraphicsGetCurrentContext()!
// Draw a red line
context.setLineWidth(2.0)
context.setStrokeColor(UIColor.red.cgColor)
context.move(to: CGPoint(x: 100, y: 100))
context.addLine(to: CGPoint(x: 200, y: 200))
context.strokePath()
// Draw a transparent green Circle
context.setStrokeColor(UIColor.green.cgColor)
context.setAlpha(0.5)
context.setLineWidth(10.0)
context.addEllipse(in: CGRect(x: 100, y: 100, width: 100, height: 100))
context.drawPath(using: .stroke) // or .fillStroke if need filling
// Save the context as a new UIImage
let myImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Return modified image
return myImage
}
It's simple:
Make an image graphics context. (Before iOS 10, you would do this by calling UIGraphicsBeginImageContextWithOptions. In iOS 10 there's another way, UIGraphicsImageRenderer, but you don't have to use it if you don't want to.)
Draw (i.e. copy) the image into the context. (UIImage actually has draw... methods for this very purpose.)
Draw your line into the context. (There are CGContext functions for this.)
Extract the resulting image from the context. (For example, if you used UIGraphicsBeginImageContextWithOptions, you would use UIGraphicsGetImageFromCurrentImageContext.) Then close the context.
Details
Xcode 9.1, Swift 4
Solution
extension UIImage
extension UIImage {
typealias RectCalculationClosure = (_ parentSize: CGSize, _ newImageSize: CGSize)->(CGRect)
func with(image named: String, rectCalculation: RectCalculationClosure) -> UIImage {
return with(image: UIImage(named: named), rectCalculation: rectCalculation)
}
func with(image: UIImage?, rectCalculation: RectCalculationClosure) -> UIImage {
if let image = image {
UIGraphicsBeginImageContext(size)
draw(in: CGRect(origin: .zero, size: size))
image.draw(in: rectCalculation(size, image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
return self
}
}
extension UIImageView
extension UIImageView {
enum ImageAddingMode {
case changeOriginalImage
case addSubview
}
func drawOnCurrentImage(anotherImage: UIImage?, mode: ImageAddingMode, rectCalculation: UIImage.RectCalculationClosure) {
guard let image = image else {
return
}
switch mode {
case .changeOriginalImage:
self.image = image.with(image: anotherImage, rectCalculation: rectCalculation)
case .addSubview:
let newImageView = UIImageView(frame: rectCalculation(frame.size, image.size))
newImageView.image = anotherImage
addSubview(newImageView)
}
}
}
Images samples
Parent Image:
Child Image:
Usage example 1
func sample1(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")?.with(image: "child") { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
return CGRect(x: 50, y: 50, width: 90, height: 90)
}
}
Result 1
Usage example 2
func sample2(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .changeOriginalImage) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 50
return CGRect(x: parentSize.width-sideLength-indent, y: parentSize.height-sideLength-indent, width: sideLength, height: sideLength)
}
}
Result 2
Usage example 3
func sample3(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .addSubview) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 15
return CGRect(x: parentSize.width-sideLength-indent, y: indent, width: sideLength, height: sideLength)
}
}
Result 3
Full sample code
Don't forget to add Solution code here
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(frame: UIScreen.main.bounds)
view.addSubview(imageView)
sample1(imageView: imageView)
// sample2(imageView: imageView)
// sample3(imageView: imageView)
}
func sample1(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")?.with(image: "child") { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
return CGRect(x: 50, y: 50, width: 90, height: 90)
}
}
func sample2(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .changeOriginalImage) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 50
return CGRect(x: parentSize.width-sideLength-indent, y: parentSize.height-sideLength-indent, width: sideLength, height: sideLength)
}
}
func sample3(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .addSubview) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 15
return CGRect(x: parentSize.width-sideLength-indent, y: indent, width: sideLength, height: sideLength)
}
}
}
Since iOS 10 you can use the UIGraphicImageRenderer, which has a better syntax and has some great features!
Swift 4
let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { context in
// draw your image into your view
context.cgContext.draw(UIImage(named: "myImage")!.cgImage!, in: view.frame)
// draw even more...
context.cgContext.setFillColor(UIColor.red.cgColor)
context.cgContext.setStrokeColor(UIColor.black.cgColor)
context.cgContext.setLineWidth(10)
context.cgContext.addRect(view.frame)
context.cgContext.drawPath(using: .fillStroke)
}
Updated Answer: Once you get the From and To coordinates, here is how to draw a line in a UIImage with those coordinates. From and To coordinates are in image pixels.
func drawLineOnImage(size: CGSize, image: UIImage, from: CGPoint, to: CGPoint) -> UIImage {
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(size)
// draw original image into the context
image.drawAtPoint(CGPointZero)
// get the context for CoreGraphics
let context = UIGraphicsGetCurrentContext()
// set stroking width and color of the context
CGContextSetLineWidth(context, 1.0)
CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
// set stroking from & to coordinates of the context
CGContextMoveToPoint(context, from.x, from.y)
CGContextAddLineToPoint(context, to.x, to.y)
// apply the stroke to the context
CGContextStrokePath(context)
// get the image from the graphics context
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
// end the graphics context
UIGraphicsEndImageContext()
return resultImage }

Resources