How to draw a line / paint on a image? - ios

So far in my attempts, I'm able to draw lines on a plain image, Like create my own plain context of CGRect size and draw lines on it. All the tutorials I see is how to draw on a created image context of x*y size. But I would like to draw lines on an already present image context like a picture of a dog and make some drawings. But so far I'm not getting results I look for. This is the code I tested with, without assigning the image, i'm able to get a line draw. But with import of image, I do not get the desired line on the picture.
let myImage = UIImage(named: "hqdefault.jpg")!
let myRGBA = RGBAImage(image: myImage)!
UIGraphicsBeginImageContextWithOptions(CGSize(width: rgbaImage.width, height: rgbaImage.height), false, 0)
let context:CGContextRef = UIGraphicsGetCurrentContext()!
CGContextMoveToPoint(context, CGFloat(100.0), CGFloat(100.0))
CGContextAddLineToPoint(context, CGFloat(150.0), CGFloat(150.0))
CGContextSetStrokeColorWithColor(context, UIColor.blackColor().CGColor)
CGContextStrokePath(context)
let image = UIGraphicsGetImageFromCurrentImageContext()
CGContextRestoreState(context)
UIGraphicsEndImageContext()
//: Return image
let view = UIImageView.init(image: image)
view.setNeedsDisplay()
These are the lines I wrote to draw a line inside an image and return it's context. I am not able to pick the imported picture context or draw lines if I try to , as it returns nil in the end. I couldn't figure out my mistake so far. Can you suggest how to draw a simple line on a picture in image view ?

Can you suggest how to draw a simple line on a picture in image view
Sure. Make an image context. Draw the image view's image into the context. Draw the line into the context. Extract the resulting image from the context and close the context. Assign the extracted image to the image view.
Example:
let im = self.iv.image! // iv is the image view
UIGraphicsBeginImageContextWithOptions(im.size, true, 0)
im.drawAtPoint(CGPointMake(0,0))
let p = UIBezierPath()
p.moveToPoint(CGPointMake(CGFloat(100.0), CGFloat(100.0)))
p.addLineToPoint(CGPointMake(CGFloat(150.0), CGFloat(150.0)))
p.stroke()
self.iv.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Before:
After:
In the second image, notice the line running from (100,100) to (150,150).

Related

I can save the image view and the sticker together

I added a sticker to image view.
So I saved it with the following source.
imageView.addSubview(StickerView[0])
// Create the image context to draw in
UIGraphicsBeginImageContextWithOptions((imageView.bounds.size), false, 0)
// Get that context
let context = UIGraphicsGetCurrentContext()
// Draw the image view in the context
imageView.layer.render(in: context!)
// You may or may not need to repeat the above with the imageView's subviews
// Then you grab the "screenshot" of the context
let image = UIGraphicsGetImageFromCurrentImageContext()
// Be sure to end the context
UIGraphicsEndImageContext()
CustomPhotoAlbum.sharedInstance.save(image: image! )
Well saved. However, the black background on both sides of the image is saved as well. Can I save only part of the image except the black background?
This is saved together. I want to save only the part of the image except this one.

Cropping UIImage to custom path and keeping correct resolution?

I have a view (blue background...) which I'll call "main" here, on main I added a UIImageView that I then rotate, pan and scale. On main I have a another subview that shows the cropping area. Anything out of that under the darker area needs to be cropped.
I am trying to figure out how to properly create a cropped image from this state. I want the resulting image to look like this:
I want to make sure to keep the resolution of the image.
Any idea?
I have tried to figure out how to use the layer.mask property of the UIImageView. After some feedback, I think I could have another view (B) on the blue view, on B I would then add the image view, so then I would make sure that B's frame would match the rect of the cropping mask overlay. I think that could work? The only thing is I want to make sure I don't lose resolution.
So, earlier I tried this:
maskShape.frame = imageView.bounds
maskShape.path = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 200, height: 200)).cgPath
imageView.layer.mask = maskShape
The rect was just a test rect and the image would be cropped to that path, but, I wasn't sure how to get a UIImage from all this that could keep the large resolution of the original image
So, I have implemented the method suggested by marco, it all works with the exception of keeping the resolution.
I use this call to take a screenshot of the view the contains the image and I have it clip to bounds:
public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage {
let rendererFormat = UIGraphicsImageRendererFormat.default()
rendererFormat.opaque = isOpaque
let renderer = UIGraphicsImageRenderer(size: bounds.size, format: rendererFormat)
let snapshotImage = renderer.image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
}
return snapshotImage
}
The image I get is correct, but is not as sharp as the one I crop.
Hoe can I keep the resolution high?
In your view which keeps the image you must set clipsToBounds to true. Not sure if I got well but I suppose it's your "cropping area"

How to render a complex UIView into a PDF Context with high resolution?

There are several questions on SO asking how to render a UIView into a PDF context, but they all use view.layer.renderInContext(pdfContext), which results in a 72 DPI image (and one that looks terrible when printed). What I'm looking for is a technique to somehow get the UIView to render at something like 300 DPI.
In the end, I was able to take hints from several prior posts and put together a solution. I'm posting this since it took me a long time to get working, and I really hope to save someone else time and effort doing the same.
This solution uses two basic techniques:
Render the UIView into a scaled bitmap context to produce a large image
Draw the image into a PDF Context which has been scaled down, so that the drawn image has a high resolution
Build your view:
let v = UIView()
... // then add subviews, constraints, etc
Create the PDF Context:
UIGraphicsBeginPDFContextToData(data, docRect, stats.headerDict) // zero == (612 by 792 points)
defer { UIGraphicsEndPDFContext() }
UIGraphicsBeginPDFPage();
guard let pdfContext = UIGraphicsGetCurrentContext() else { return nil }
// I tried 300.0/72.0 but was not happy with the results
let rescale: CGFloat = 4 // 288 DPI rendering of VIew
// You need to change the scale factor on all subviews, not just the top view!
// This is a vital step, and there may be other types of views that need to be excluded
Then create a large bitmap of the image with an expanded scale:
func scaler(v: UIView) {
if !v.isKindOfClass(UIStackView.self) {
v.contentScaleFactor = 8
}
for sv in v.subviews {
scaler(sv)
}
}
scaler(v)
// Create a large Image by rendering the scaled view
let bigSize = CGSize(width: v.frame.size.width*rescale, height: v.frame.size.height*rescale)
UIGraphicsBeginImageContextWithOptions(bigSize, true, 1)
let context = UIGraphicsGetCurrentContext()!
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, CGRect(origin: CGPoint(x: 0, y: 0), size: bigSize))
// Must increase the transform scale
CGContextScaleCTM(context, rescale, rescale)
v.layer.renderInContext(context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Now we have a large image with each point representing one pixel.
To get it drawn into the PDF at high resolution, we need to scale the PDF down while drawing the image at its large size:
CGContextSaveGState(pdfContext)
CGContextTranslateCTM(pdfContext, v.frame.origin.x, v.frame.origin.y) // where the view should be shown
CGContextScaleCTM(pdfContext, 1/rescale, 1/rescale)
let frame = CGRect(origin: CGPoint(x: 0, y: 0), size: bigSize)
image.drawInRect(frame)
CGContextRestoreGState(pdfContext)
... // Continue with adding other items
You can see that the left "S" contained in the cream colored bitmap looks pretty nice compared to a "S" drawn but an attributed string:
When the same PDF is viewed by a simple rendering of the PDF without all the scaling, this is what you would see:

How do I take a PARTIAL screenshot & save to CameraRoll?

I am building a simple motivational app - my pet project. Pretty simple. It prints a random motivational message when a button is pressed.
I would like to user to be able to press a button and crop the motivational message itself on the screen and save it to the camera roll.
I found a tutorial that does what I wanted, but it takes a FULL screenshot AND a PARTIAL screenshot.
I'm trying to modify the code so it takes ONLY a partial screenshot.
Here's the Xcode:
print("SchreenShot")
// Start full screenshot
UIGraphicsBeginImageContext(view.frame.size)
view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
var sourceImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(sourceImage,nil,nil,nil)
//partial Screen Shot
print("partial ss")
UIGraphicsBeginImageContext(view.frame.size)
sourceImage.drawAtPoint(CGPointMake(0, -100))
var croppedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(croppedImage,nil,nil,nil)
Also, in the PARTIAL screenshot, it takes a snapshot of the "page" 100 pixels from the top down to the bottom. How can I make it take a snapshot of the contents of the page say 100 pixels from the top of page to 150 pixels from bottom of page?
Many, many, many thanks!
Your sample code draws the view into a graphics context (the snapshot), crops it, and saves it. I am altering it a little with some extra comments because it looks like you are new to this API
// Declare the snapshot boundaries
let top: CGFloat = 100
let bottom: CGFloat = 150
// The size of the cropped image
let size = CGSize(width: view.frame.size.width, height: view.frame.size.height - top - bottom)
// Start the context
UIGraphicsBeginImageContext(size)
// we are going to use context in a couple of places
let context = UIGraphicsGetCurrentContext()!
// Transform the context so that anything drawn into it is displaced "top" pixels up
// Something drawn at coordinate (0, 0) will now be drawn at (0, -top)
// This will result in the "top" pixels being cut off
// The bottom pixels are cut off because the size of the of the context
CGContextTranslateCTM(context, 0, -top)
// Draw the view into the context (this is the snapshot)
view.layer.renderInContext(context)
let snapshot = UIGraphicsGetImageFromCurrentImageContext()
// End the context (this is required to not leak resources)
UIGraphicsEndImageContext()
// Save to photos
UIImageWriteToSavedPhotosAlbum(snapshot, nil, nil, nil)

Drawing a UIBezierPath to UIImage to Base64 Image not working in Swift

I created some code to create a UIBezierPath within a UIImage context. Then take the image and create a base64 string. I believe I am supposed to draw the path within the beginning and end of the UIImage. However after many hours, it is not working. I am copying the base64 string to a website to download the image to see if it works. I am writing this in playground:
import Foundation
import UIKit
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), false, 0.0)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIColor.blackColor().setStroke()
let path = UIBezierPath()
path.lineWidth = 2
path.moveToPoint(CGPointMake(100, 0))
path.addLineToPoint(CGPointMake(200, 40))
path.addLineToPoint(CGPointMake(160, 140))
path.addLineToPoint(CGPointMake(40, 140))
path.addLineToPoint(CGPointMake(0, 40))
path.closePath()
UIGraphicsEndImageContext();
let data = UIImagePNGRepresentation(image)
let b64 = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
print(b64!)
The resulting PNG is 1000x1000 I am guessing because of my retina display. The image itself is completely transparent and nothing is visible. I am using this site to decode base64 and save a file, I've tried 2 others to see if it was the site. http://www.motobit.com/util/base64-decoder-encoder.asp
EDIT
I just tried the following code to see if the issue with with my image or bezier or saving to bas64. This code worked great. So I think it's a problem with my bezier.
import Foundation
import UIKit
let image = UIImage(data: NSData(contentsOfURL: NSURL(string: "http://i.imgur.com/crr4m48.jpg")!)!)!
let data = UIImagePNGRepresentation(image)
let b64 = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
print(b64!)
It looks like you're doing things out of order. For example:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), false, 0.0)
let image = UIGraphicsGetImageFromCurrentImageContext()
Why are you getting image before you draw anything into the graphics context? I think you're going to get an empty image -- it's not like the image you get at the outset will change as you draw things in the context. Wait to get the image until after you finish all your drawing. Also, the docs for that function say:
You should call this function only when a bitmap-based graphics context is the current graphics context. If the current context is nil or was not created by a call to UIGraphicsBeginImageContext, this function returns nil.
So, make sure that you've satisfied that requirement and that the image you get back when you do call UIGraphicsBeginImageContext() is not nil.
Next, you're creating a bezier path:
let path = UIBezierPath()
path.lineWidth = 2
path.moveToPoint(CGPointMake(100, 0))
path.addLineToPoint(CGPointMake(200, 40))
//...
but in order for that path to actually be drawn in your context, you have to do something that draws, such as calling path.fill() or path.stroke(). I don't see that anywhere, so even if you fix the first problem above, you're still going to end up with an empty image until you do some drawing.
You need to work with the image context directly. You can try create CGContextRef with current image context like:
UIGraphicsBeginImageContext(CGSizeMake(200, 200))
var context: CGContextRef = UIGraphicsGetCurrentContext()
And then directly add your path to it:
CGContextAddPath(context, path)
Or you can create you curves with context methods like CGContextAddCurveToPoint and then stroke path with:
CGContextStrokePath(context)
And the last action to create image from it:
let image = UIGraphicsGetImageFromCurrentImageContext()
One more Point In addition to #Caleb :-
If you are drawing image.png which result of 1000x1000 pixels then UIGraphicsBeginImageContext parameter must be different as above one :-
//Swift-3 Syntax
UIGraphicsBeginImageContextWithOptions( CGSize(width:500, height:500 ), false , 2.0 )
Reference

Resources