Convert Square UIImage to round UIIMage for use with CAEmitterCell.contents - ios

I have a custom UI component that produces an image of a round ball with some labels superimposed over top of it.
I am taking a snapshot of the component using the following UIView extension I found here on StackOverflow.
I am taking the resulting UIImage and using it in a CAEmitterCell.
My problem is that the snapshot image is square - my round ball on a white background. I would like the background to be clear when it is emitted but I can't seem to find a way to do it.
Is there any way I can modify the UIImage to make its corners transparent?
Thanks.
extension UIView {
/// Create snapshot
///
/// - parameter rect: The `CGRect` of the portion of the view to return. If `nil` (or omitted),
/// return snapshot of the whole view.
///
/// - returns: Returns `UIImage` of the specified portion of the view.
func snapshot(of rect: CGRect? = nil) -> UIImage? {
// snapshot entire view
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: true)
let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// if no `rect` provided, return image of whole view
guard let image = wholeImage, let rect = rect else { return wholeImage }
// otherwise, grab specified `rect` of image
let scale = image.scale
let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
guard let cgImage = image.cgImage?.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
}

After figuring out how to word my question here, I thought of another way to search for my answer and found a solution.
Someone posted an extension to turn white backgrounds transparent as an answer to a previous question. White didn't work for me, but a simple edit and a name change made the extension work for black backgrounds instead of white.
extension UIImage {
func imageByMakingBlackBackgroundTransparent() -> UIImage? {
let image = UIImage(data: UIImageJPEGRepresentation(self, 1.0)!)!
let rawImageRef: CGImage = image.cgImage!
let colorMasking: [CGFloat] = [0, 0, 0, 0, 0, 0]
UIGraphicsBeginImageContext(image.size);
let maskedImageRef = rawImageRef.copy(maskingColorComponents: colorMasking)
UIGraphicsGetCurrentContext()?.translateBy(x: 0.0,y: image.size.height)
UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
UIGraphicsGetCurrentContext()?.draw(maskedImageRef!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height))
let result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result
}
}
My image was originally on a white background, but there was also white in the important part of the image. I temporarily changed my background to black, took a snapshot, then converted black to transparent, then changed my background back to white.
The end result is that my problem is fixed.
Thanks to anyone who took any time to read or think about this problem.

Related

Swift - How to change a png tint color without losing the clear background? [duplicate]

I am new to swift and trying to achieve this essentially,
This image to
This image ->
I am using this code from here to change the tint on image but do not get the desired output
func tint(image: UIImage, color: UIColor) -> UIImage
{
let ciImage = CIImage(image: image)
let filter = CIFilter(name: "CIMultiplyCompositing")
let colorFilter = CIFilter(name: "CIConstantColorGenerator")
let ciColor = CIColor(color: color)
colorFilter.setValue(ciColor, forKey: kCIInputColorKey)
let colorImage = colorFilter.outputImage
filter.setValue(colorImage, forKey: kCIInputImageKey)
filter.setValue(ciImage, forKey: kCIInputBackgroundImageKey)
return UIImage(CIImage: filter.outputImage)!
}
If this is a noob question, apologies. I was able to do this easily in javascript but not in swift.
hi you can use it the following way. First the UIImage Extension that you used needs some updates.
you can copy the code below for Swift 3
extension UIImage{
func tint(color: UIColor, blendMode: CGBlendMode) -> UIImage
{
let drawRect = CGRect(x: 0,y: 0,width: size.width,height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
color.setFill()
UIRectFill(drawRect)
draw(in: drawRect, blendMode: blendMode, alpha: 1.0)
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return tintedImage!
}
}
Then in your viewDidLoad where you are using the image
for example i used the image from IBOutlet imageWood
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.imageWood.image = self.imageWood.image?.tint(color: UIColor.green, blendMode: .saturation)
}
You will have to use the appropriate color and the images
The other Extension i found
extension UIImage {
// colorize image with given tint color
// this is similar to Photoshop's "Color" layer blend mode
// this is perfect for non-greyscale source images, and images that have both highlights and shadows that should be preserved
// white will stay white and black will stay black as the lightness of the image is preserved
func tint(_ tintColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw black background - workaround to preserve color of partially transparent pixels
context.setBlendMode(.normal)
UIColor.black.setFill()
context.fill(rect)
// draw original image
context.setBlendMode(.normal)
context.draw(self.cgImage!, in: rect)
// tint image (loosing alpha) - the luminosity of the original image is preserved
context.setBlendMode(.color)
tintColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(self.cgImage!, in: rect)
}
}
// fills the alpha channel of the source image with the given color
// any color information except to the alpha channel will be ignored
func fillAlpha(_ fillColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw tint color
context.setBlendMode(.normal)
fillColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(self.cgImage!, in: rect)
}
}
fileprivate func modifiedImage(_ draw: (CGContext, CGRect) -> ()) -> UIImage {
// using scale correctly preserves retina images
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context: CGContext! = UIGraphicsGetCurrentContext()
assert(context != nil)
// correctly rotate image
context.translateBy(x: 0, y: size.height);
context.scaleBy(x: 1.0, y: -1.0);
let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
draw(context, rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
Using it like this
self.imageWood.image = self.imageWood.image?.tint(UIColor.purple.withAlphaComponent(1))
Try the below code, should work for you.
if let myImage = UIImage(named: "imageName")?.withRenderingMode(.alwaysTemplate) {
myImageView.image = myImage
myImageView.tintColor = UIColor.white
}
Your image is simple and only has one color. I would suggest making your image transparent except where the lines are, and then layer it on top of a white background to get your results.
It looks like you got the results you're after using drawing modes. There are also a number of Core image filters that let you apply tinting effects to images, as well as replacing specific colors. Those would be a good choice for more complex images.

Swift: How to save screenshot of a view with all views which below that view?

I need to make a screenshot of tableview(it has a clear background). But under it, I have one ImageView and blur view as well. I need to get screenshot with all those views.
I tried different solutions, but they did not capture the bottom views. Any ways to do that?
Also, I do not need a full screenshot of the screen. Only bounds which are limited by tableView. I guess, I had some option how to make a full screenshot, but it did not work with view
I tried:
1:
func snapshot(of rect: CGRect? = nil) -> UIImage? {
// snapshot entire view
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: true)
let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// if no `rect` provided, return image of whole view
guard let image = wholeImage, let rect = rect else { return wholeImage }
// otherwise, grab specified `rect` of image
let scale = image.scale
let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
guard let cgImage = image.cgImage?.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
2:
UIGraphicsBeginImageContext(tableView.frame.size)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image1 = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
My hierarchy is:
base UIView
UIImageView with image
UIBlurEffect
UITableView with clear background which displays chart
If I understand your question, you want a true screen shot but not of the entire screen. Therefore, take a raw screenshot:
let snap = UIApplication.shared.keyWindow!.snapshotView(afterScreenUpdates: true)
snap.frame = view.bounds // or something, if necessary
Apply a mask to the screenshot with a frame equal to the bounds of the table view:
let mask = CALayer()
mask.frame = tableView.frame.bounds
mask.backgroundColor = UIColor.white.cgColor
snap.layer.mask = mask

Make UIImage a Circle without Losing quality? (swift)

The image becomes blurry once applying roundImage:
Making a UIImage to a circle form
extension UIImage
{
func roundImage() -> UIImage
{
let newImage = self.copy() as! UIImage
let cornerRadius = self.size.height/2
UIGraphicsBeginImageContextWithOptions(self.size, false, 1.0)
let bounds = CGRect(origin: CGPointZero, size: self.size)
UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).addClip()
newImage.drawInRect(bounds)
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return finalImage
}
}
Why are you using bezierpath? Just set cornerradius for uiimageview.
If your image is larger than the imageview then you have to resize your image to your imageview size and then set cornerradius for that uiimageview.
It will work. Works for me
Replace the following line
UIGraphicsBeginImageContextWithOptions(self.size, false, 1.0)
with
UIGraphicsBeginImageContextWithOptions(self.size, view.opaque , 0.0)
try this one
let image = UIImageView(frame: CGRectMake(0, 0, 100, 100))
I recommend that you can use AlamofireImage (https://github.com/Alamofire/AlamofireImage)
It's very easily to make rounded image or circle image without losing quality.
just like this:
let image = UIImage(named: "unicorn")!
let radius: CGFloat = 20.0
let roundedImage = image.af_imageWithRoundedCornerRadius(radius)
let circularImage = image.af_imageRoundedIntoCircle()
Voila!
Your issue is that you are using scale 1, which is the lowest "quality".
Setting the scale to 0 will use the device scale, which just uses the image as is.
A side note: Functions inside a class that return a new instance of that class can be implemented as class functions. This makes it very clear what the function does. It does not manipulate the existing image. It returns a new one.
Since you were talking about circles, I also corrected your code so it will now make a circle of any image and crop it. You might want to center this.
extension UIImage {
class func roundImage(image : UIImage) -> UIImage? {
// copy
guard let newImage = image.copy() as? UIImage else {
return nil
}
// start context
UIGraphicsBeginImageContextWithOptions(newImage.size, false, 0.0)
// bounds
let cornerRadius = newImage.size.height / 2
let minDim = min(newImage.size.height, newImage.size.width)
let bounds = CGRect(origin: CGPointZero, size: CGSize(width: minDim, height: minDim))
UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).addClip()
// new image
newImage.drawInRect(bounds)
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// crop
let maybeCrop = UIImage.crop(finalImage, cropRect: bounds)
return maybeCrop
}
class func crop(image: UIImage, cropRect : CGRect) -> UIImage? {
guard let imgRef = CGImageCreateWithImageInRect(image.CGImage, cropRect) else {
return nil
}
return UIImage(CGImage: imgRef)
}
}

I have a png file with no background, how can I create a clear color background for this image in iOS?

So I understand that a UIImage inherently doesn't have a background. As many of us know, a lot of PNG files don't have a background Color thus making it clear. I'm attempting to upload a png file that doesn't have a background color, thus a clear color. Yes, I know I can set the background Color myself in adobe or sketch, but I'm assuming that other users don't know how to do this.
Here is a screenshot of the png that I have created:
As you can see, it's just two lines that are unioned together so there's no background set.
Now below is a screenshot of the aftermath of using the imagePicker to choose this png image from my photo roll.
Notice that the area that is supposed to be transparent is actually black. I want to color in the black part and make it actually clearColor instead and keep the green cross as it is. Now, I'm not sure if the black color is actually even a black color because perhaps it's just empty space. Can I fill in the empty black space and turn it into a clear color?
Here's my code right now that isn't working very well:
func overlayImage(image: UIImage, color: UIColor) -> UIImage? {
let rect = CGRectMake(0, 0, image.size.width, image.size.height)
let backgroundView = UIView(frame: rect)
backgroundView.backgroundColor = color
UIGraphicsBeginImageContext(rect.size)
let gcSize: CGSize = backgroundView.frame.size
UIGraphicsBeginImageContext(gcSize)
let context: CGContextRef = UIGraphicsGetCurrentContext()!
backgroundView.layer.renderInContext(context)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Any help in either obj-C or Swift would be greatly appreciated.
I got rid of the overlay method above and am using the code below:
Updated with new code that still doesn't work
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
scaleImage(overlayImage(image, color: UIColor.clearColor()))
}
func scaleImageAndAddAugmented(image: UIImage?) {
let rect = CGRectMake(0, 0, image!.size.width, image!.size.height)
let backgroundView = UIView(frame: rect)
backgroundView.backgroundColor = UIColor.clearColor()
let size = CGSizeApplyAffineTransform(image!.size, CGAffineTransformMakeScale(0.25, 0.25))
let hasAlpha = false
let scale: CGFloat = 0.0
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image!.drawInRect(CGRect(origin: CGPointZero, size: size))
let context = CGBitmapContextCreate(nil, Int(image!.size.width), Int(image!.size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast.rawValue)
let myGeneratedImage = CGBitmapContextCreateImage(context)
CGContextDrawImage(context, rect, myGeneratedImage)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.dismissViewControllerAnimated(true, completion: nil)
// set image below
}
It seems a bit of excess work to use UIGraphicsGetImageFromCurrentImageContext to generate an image from context, when you can just create an image from a file.
As first mentioned it is required to have Opaque = NO:
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
My bet is its the image itself, because the whole thing is faded.
Make 100% certain that the passed colour is a clear colour for:
backgroundView.backgroundColor = color
You could create a bitmap context and use that instead:
CGContextRef context = CGBitmapContextCreate(NULL,
width,
height,
8,
0,
rgbColorSpace,
kCGImageAlphaPremultipliedLast);
Rather than using the current context and you can use:
myGeneratedImage = CGBitmapContextCreateImage(context)
Drawing is easy as pie:
CGContextDrawImage(realContext,bounds,myGeneratedImage)
Swift code below:
func scaleImage(image: UIImage?) {
if let image = image {
let rect = CGRectMake(0, 0, image.size.width, image.size.height)
let size: CGSize = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.25, 0.25))
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let context = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast.rawValue)
let myGeneratedImage = CGBitmapContextCreateImage(context)
CGContextDrawImage(context, rect, myGeneratedImage)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}

Rendering image with textview

I am placing a textview on a image. With following code:
var previewImageView=UIImageView()
self.previewImageView.frame = CGRect(x:0, y:0, width:UIScreen.mainScreen().bounds.width, height:UIScreen.mainScreen().bounds.height)
self.view.addSubview(self.previewImageView)
textField = UITextView(frame: CGRect(x: 0, y: self.view.bounds.height/2 - 50, width: self.view.bounds.width, height: 36))
textField.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
self.view.addSubview(textField)
It works well. But I want to render this image with textview. Then I will save it to gallery.
How can I do this?
Edit:
I tried following code but it is only rendering image not textview:
//Setup the image context using the passed image.
UIGraphicsBeginImageContext(inImage.size)
//Put the image into a rectangle as large as the original image.
inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))
// Creating a point within the space that is as bit as the image.
var rect: CGRect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)
//Now Draw the text into an image.
drawText.drawRect(rect)
// Create a new image out of the images we have created
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//And pass it back up to the caller.
return newImage
You can render whole view into an UIImage using CGGraphicsContext. Here is an example of the code:
func imageWithView(view: UIView!) -> UIImage! {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
view.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
When you pass scale = 0.0 it will take a scale of your screen.
An example of usage:
let image = imageWithView(view)
Code to save image:
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
More information about UIGraphicsContext and image saving can be found here.
Update:
If you want to render only specific views, then you should use CALayer, renderInContext. When you use renderInContext it does not know views.frame and draws at (0:0) in coordinate system, so you should use CGContextTranslateCTM to move coordinate system.
To render only these two views you can do:
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, previewImageView.frame.origin.x, previewImageView.frame.origin.y)
previewImageView.layer.renderInContext(context)
CGContextTranslateCTM(context, textField.frame.origin.x - previewImageView.frame.origin.x, textField.frame.origin.y - previewImageView.frame.origin.y)
textField.layer.renderInContext(context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Please set opaque true/false as you need. When I was testing in playground I was using not opaque views.
Update 2:
Code which should work in all scenarios:
func imageFromViews(views: [UIView]!, contextSize: CGSize!) -> UIImage {
UIGraphicsBeginImageContextWithOptions(contextSize, false, 0.0)
let context = UIGraphicsGetCurrentContext()
for view in views {
CGContextTranslateCTM(context, view.frame.origin.x, view.frame.origin.y)
view.layer.renderInContext(UIGraphicsGetCurrentContext())
CGContextTranslateCTM(context, -view.frame.origin.x, -view.frame.origin.y)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}

Resources