How to Cut image from path in swift - ios

I am trying to crop my image using UIBezierPath (the user draw the path) I want the path area to be removed from the UIImage, I have this code implementation but the cut is not working in the path area
let originalImage = treaCameraImage!
UIGraphicsBeginImageContext(originalImage.size)
originalImage.draw(at: .zero)
let context = UIGraphicsGetCurrentContext()
context!.addPath(path.cgPath)
context!.clip()
context!.setFillColor(UIColor.red.cgColor)
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.width))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageCut.image = newImage

Well for the solution you need first add this code in the viewDidAppear
override func viewDidAppear(_ animated: Bool) {
//Implemented to overcome trackerview problem(UIBreizerPath is not correct) when working direcly on an image bigger than the screen(For brackets cut image).
if (self.treaCameraImage!.size.width != self.view.bounds.size.width || self.treaCameraImage!.size.height != self.view.bounds.size.width) {
UIGraphicsBeginImageContext(self.imageCut.bounds.size);
let context = UIGraphicsGetCurrentContext()
if let context = context {
imageCut.layer.render(in: context)
}
let screenShot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
self.treaCameraImage = screenShot;
//self.tempOriginalImage = screenShot;
}
}
The treaCameraImage is the default image and imageCut is the image container
In my method to cut the image i changed:
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.width))
to this:
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width * 2, height: originalImage.size.width * 2))
and that works for me!

Related

How to apply scale when drawing and composing UIImage

I have the following functions.
extension UIImage
{
var width: CGFloat
{
return size.width
}
var height: CGFloat
{
return size.height
}
private static func circularImage(diameter: CGFloat, color: UIColor) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let rect = CGRect(x: 0, y: 0, width: diameter, height: diameter)
context.setFillColor(color.cgColor)
context.fillEllipse(in: rect)
context.restoreGState()
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
private func addCentered(image: UIImage, tintColor: UIColor) -> UIImage
{
let topImage = image.withTintColor(tintColor, renderingMode: .alwaysTemplate)
let bottomImage = self
UIGraphicsBeginImageContext(size)
let bottomRect = CGRect(x: 0, y: 0, width: bottomImage.width, height: bottomImage.height)
bottomImage.draw(in: bottomRect)
let topRect = CGRect(x: (bottomImage.width - topImage.width) / 2.0,
y: (bottomImage.height - topImage.height) / 2.0,
width: topImage.width,
height: topImage.height)
topImage.draw(in: topRect, blendMode: .normal, alpha: 1.0)
let mergedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return mergedImage
}
}
They work fine, but how do I properly apply UIScreen.main.scale to support retina screens?
I've looked at what's been done here but can't figure it out yet.
Any ideas?
Accessing UIScreen.main.scale itself is a bit problematic, as you have to access it only from main thread (while you usually want to put a heavier image processing on a background thread). So I suggest one of these ways instead.
First of all, you can replace UIGraphicsBeginImageContext(size) with
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
The last argument (0.0) is a scale, and based on docs "if you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen."
If instead you want to retain original image's scale on resulting UIImage, you can do this: after topImage.draw, instead of getting the UIImage with UIGraphicsGetImageFromCurrentImageContext, get CGImage with
let cgImage = context.makeImage()
and then construct UIImage with the scale and orientation of the original image (as opposed to defaults)
let mergedImage = UIImage(
cgImage: cgImage,
scale: image.scale,
orientation: image.opientation)

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)

Draw text onto image at an angle [Swift 3]

I'm trying to make function that will take text from a textfield (textField) and draw it onto an image. The function at the moment can only change the drawing's coordinates x and y, and width and height. What I was wondering is how I can make the text be drawn at an angle (eg 45˚, 18˚, etc...)
Thanks in advance.
func drawText() {
let font = UIFont.boldSystemFont(ofSize: 30)
let showText:NSString = textField.text as! NSString
// setting attr: font name, color...etc.
let attr = [NSFontAttributeName: font, NSForegroundColorAttributeName:UIColor.white]
// getting size
let sizeOfText = showText.size(attributes: attr)
let image = UIImage(named: "image")!
let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
UIGraphicsBeginImageContextWithOptions(CGSize(width: rect.size.width, height: rect.size.height), true, 0)
// drawing our image to the graphics context
image.draw(in: rect)
// drawing text
showText.draw(in: CGRect(x: rect.size.width-sizeOfText.width-10, y: rect.size.height-sizeOfText.height-10, width: rect.size.width, height: rect.size.height), withAttributes: attr)
// getting an image from it
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
self.imageView.image = newImage
}
1.Draw text to an image first, then rotate the image.
2.Draw rotated image(with text) onto the background image.
//First create the rotated and transparent image with text
UIGraphicsBeginImageContextWithOptions(CGSize(width: rect.size.width, height: rect.size.height), false, 0)
if let context = UIGraphicsGetCurrentContext() {
context.rotate (by: 45.0 * CGFloat.pi/180.0) //45˚
}
showText.draw(in: CGRect(x: 10, y: 10, width: rect.size.width, height: rect.size.height), withAttributes: attr)
let rotatedImageWithText = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
//Then, draw rotated image(with text) onto the background image
UIGraphicsBeginImageContextWithOptions(CGSize(width: rect.size.width, height: rect.size.height), true, 0)
image.draw(in: rect)
rotatedImageWithText?.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
self.imageView.image = newImage

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
}

Why the image is rotated , by calling CGContextDrawImage

Why the image is rotated , by calling CGContextDrawImage.Thanks for your help.
// Initialization code
UIImage *img = [UIImage imageNamed:#"logo.png"];
UIImagePNGRepresentation(img);
_image_ref = img.CGImage;
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect img_rect = CGRectMake(20, 40, 100, 150);
CGContextDrawImage(context, img_rect, _image_ref);
core graphics's coordinated system not like UIKit, you need to calculate the right coordinate.
http://blog.ddg.com/?p=10
Following this explanation. I created solution which allow to draw multiple images with custom rects in one context.
func foo() -> UIImage? {
let image = UIImage(named: "back.png")!
let contextSize = CGSize(width: 500, height: 500)
UIGraphicsBeginImageContextWithOptions(contextSize, true, image.scale)
guard let ctx = UIGraphicsGetCurrentContext() else { return nil }
guard let cgImage = image.cgImage else { return nil}
//Start code which can by copy/paste
let imageRect = CGRect(origin: CGPoint(x: 200.0, y: 200.0), size: image.size) //custom rect
let ty = imageRect.origin.y + imageRect.size.height //calculate translation Y
let imageRectWithoutOriginY = CGRect(origin: CGPoint(x: imageRect.origin.x, y: 0), size: imageRect.size)
ctx.translateBy(x: 0.0, y: ty) //prepare context for custom rect
ctx.scaleBy(x: 1.0, y: -1.0)
ctx.draw(cgImage, in: imageRectWithoutOriginY) //draw image
ctx.translateBy(x: 0.0, y:-ty) //restore default context setup (so you can select new area to place another image)
ctx.scaleBy(x: 1.0, y: -1.0)
//End code which can by copy/paste
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
Example with to images:
I know that it can be refactored. I duplicated code for more clarity.

Resources