Draw border around content of UIImageView - ios

I've a UIImageView with a image with is a car with a transparent background:
And I want to draw a border around the car:
How can I reach this effect?
At the moment, I've tested CoreGraphics in this way, but without good results:
// load the image
UIImage *img = carImage;
UIGraphicsBeginImageContext(img.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor] setFill];
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, img.size.width * 1.1, img.size.height*1.1);
CGContextDrawImage(context, rect, img.CGImage);
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Any help? Thanks.

Here's what I did:
I did it in Swift just to check it in playgrounds, think you can translate it to Objective-C easily:
import UIKit
func drawOutlie(#image:UIImage, color:UIColor) -> UIImage
{
var newImageKoef:CGFloat = 1.08
var outlinedImageRect = CGRect(x: 0.0, y: 0.0, width: image.size.width * newImageKoef, height: image.size.height * newImageKoef)
var imageRect = CGRect(x: image.size.width * (newImageKoef - 1) * 0.5, y: image.size.height * (newImageKoef - 1) * 0.5, width: image.size.width, height: image.size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, newImageKoef)
image.drawInRect(outlinedImageRect)
var context = UIGraphicsGetCurrentContext()
CGContextSetBlendMode(context, kCGBlendModeSourceIn)
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, outlinedImageRect)
image.drawInRect(imageRect)
var newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
var imageIn = UIImage(named: "158jM")
var imageOut = drawOutlie(image: imageIn, UIColor.redColor())
So how does it work?
We create clean context (aka canvas) with a bit bigger size then original image (for outline)
We draw our image on whole canvas
We fill that image with color
We draw smaller image on top
You can change outline size changing this property : var newImageKoef:CGFloat = 1.08
Here's a result that I had in playgrounds
Swift 5:
extension UIImage {
func drawOutline(imageKeof: CGFloat = 0.2, color: UIColor = .white)-> UIImage? {
let outlinedImageRect = CGRect(x: 0.0, y: 0.0, width: size.width * imageKeof, height: size.height * imageKeof)
let imageRect = CGRect(x: self.size.width * (imageKeof - 1) * 0.5, y: self.size.height * (imageKeof - 1) * 0.5, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, imageKeof)
draw(in: outlinedImageRect)
let context = UIGraphicsGetCurrentContext()
context!.setBlendMode(.sourceIn)
context!.setFillColor(color.cgColor)
context!.fill(outlinedImageRect)
draw(in: imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}

haawa answer for Swift 5
extension UIImage {
func drawOutlie(imageKeof: CGFloat = 1.01, color: UIColor) -> UIImage? {
let outlinedImageRect = CGRect(x: 0.0, y: 0.0,
width: size.width * imageKeof,
height: size.height * imageKeof)
let imageRect = CGRect(x: size.width * (imageKeof - 1) * 0.5,
y: size.height * (imageKeof - 1) * 0.5,
width: size.width,
height: size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, imageKeof)
draw(in: outlinedImageRect)
guard let context = UIGraphicsGetCurrentContext() else {return nil}
context.setBlendMode(.sourceIn)
context.setFillColor(color.cgColor)
context.fill(outlinedImageRect)
draw(in: imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}

This approach is a little bit different but more simple.
imageView.layer.shadowColor = UIColor.red.cgColor
imageView.layer.shadowOffset = CGSize(width: 0, height: 0)
imageView.layer.shadowOpacity = 1
imageView.layer.shadowRadius = 10.0
imageView.clipsToBounds = false

Related

Rotate UIImage in swift

I want to rotate UIImage in clockwise direction. But my current code do not perform function accurate some time it rotate and some time its skip rotation. I want my UIImage will continuously rotate clockwise on my action button.
Here is my current code:
imageView.image = imageView.image!.imageRotatedByDegrees(angle, flip: false)
angle = angle + 90
if angle > 360{
angle = 0
}
This works for me:
works Fine :)
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRectMake(0, 0, oldImage.size.width, oldImage.size.height))
let t: CGAffineTransform = CGAffineTransformMakeRotation(degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContextRef = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2)
//Rotate the image context
CGContextRotateCTM(bitmap, (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-oldImage.size.width / 2, -oldImage.size.height / 2, oldImage.size.width, oldImage.size.height), oldImage.CGImage)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Swift 3:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
public func fixedOrientation() -> UIImage {
if imageOrientation == UIImageOrientation.up {
return self
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch imageOrientation {
case UIImageOrientation.down, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
break
case UIImageOrientation.left, UIImageOrientation.leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi/2)
break
case UIImageOrientation.right, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -CGFloat.pi/2)
break
case UIImageOrientation.up, UIImageOrientation.upMirrored:
break
}
switch imageOrientation {
case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
transform.translatedBy(x: size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
break
case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
transform.translatedBy(x: size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:
break
}
let ctx: CGContext = CGContext(data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: self.cgImage!.bitsPerComponent,
bytesPerRow: 0,
space: self.cgImage!.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
ctx.concatenate(transform)
switch imageOrientation {
case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
break
}
let cgImage: CGImage = ctx.makeImage()!
return UIImage(cgImage: cgImage)
}
}
#Jason, You should fix orientation of image before rotation.
let image = oldimage.fixedOrientation().imageRotatedByDegrees(90.0)
#Jason Chitla, I cant comment yet, so Im posting it as a new answer. The above solution from #Albert works, but only for square images. The problem is, that
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
is still using the original size, so if you rotate it 90 degrees, aspect is wrong.
What works for me is to use the new rotatedSize, like this:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -rotatedSize.width / 2, y: -rotatedSize.height / 2, width: rotatedSize.width, height: rotatedSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
You can create a function like this to make your image rotate in clockwise forever:
override func viewDidLoad() {
super.viewDidLoad()
let kRotationAnimationKey = "com.myapplication.rotationanimationkey"
func rotateView(view: UIView, duration: Double = 1) {
if view.layer.animationForKey(kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
}
}
rotateView(self.imageView)
}
Result is:
For those who need the code from #Mughees in Swift 3:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
I fixed first function of #gleb's answer little bit. Size calculation is not necessary there. We simply should do it with reverse.
Because if you do it size of 1149 x 356 with #gleb's function then you will get image sized 357 x 1149.
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
let rotatedSize: CGSize = CGRect(x: 0, y: 0, width: self.size.height, height: self.size.width).size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
This s probably easier and it is Swift 5 syntax:
// MARK: Rotate
extension UIImage {
func rotate(radians: Float) -> UIImage? {
var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
// Trim off the extremely small float value to prevent core graphics from rounding it up
newSize.width = floor(newSize.width)
newSize.height = floor(newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)
let context = UIGraphicsGetCurrentContext()!
// Move origin to middle
context.translateBy(x: newSize.width/2, y: newSize.height/2)
// Rotate around middle
context.rotate(by: CGFloat(radians))
// Draw the image at its center
self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
I believe it's worth to post solution using CoreImage. It's just couple of lines and it works great for me.
Please note: when getting final UIImage, it's necessary to convert to CGImage first to respect extent of CIImage
extension UIImage {
func imageRotated(by degrees: CGFloat) -> UIImage {
let orientation = CGImagePropertyOrientation(imageOrientation)
// Create CIImage respecting image's orientation
guard let inputImage = CIImage(image: self)?.oriented(orientation)
else { return self }
// Rotate the image itself
let rotation = CGAffineTransform(rotationAngle: (degrees * CGFloat.pi / 180))
let outputImage = inputImage.transformed(by: rotation)
// Create CGImage first
guard let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent)
else { return self }
// Create output UIImage from CGImage
return UIImage(cgImage: cgImage)
}
}

Add a shadow around a UIImageView's UIImage that has clear pixels [duplicate]

I've taken a look at this question: UIImage Shadow Trouble
But the accepted answer didn't work for me.
What I'm trying to do is take a UIImage and add a shadow to it, then return a whole new UIImage, shadow and all.
This is what I'm trying:
- (UIImage*)imageWithShadow {
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef shadowContext = CGBitmapContextCreate(NULL, self.size.width, self.size.height + 1, CGImageGetBitsPerComponent(self.CGImage), 0,
colourSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);
CGContextSetShadow(shadowContext, CGSizeMake(0, -1), 1);
CGContextDrawImage(shadowContext, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
CGImageRef shadowedCGImage = CGBitmapContextCreateImage(shadowContext);
CGContextRelease(shadowContext);
UIImage * shadowedImage = [UIImage imageWithCGImage:shadowedCGImage];
CGImageRelease(shadowedCGImage);
return shadowedImage;
}
The result is that I get exactly the same image as before I put it through this method.
I am doing this the correct way, or is there something obvious I'm missing?
There are several problems with your code:
The target image is too small. Be sure there enough place to draw the shadow.
Consider using CGContextSetShadowWithColor to define both the shadow and its color.
Don't forget that coordinate system is flipped, so the origin is bottom-left, not top-left.
By fixing these issues, the shadow should be drawn.
- (UIImage*)imageWithShadow {
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef shadowContext = CGBitmapContextCreate(NULL, self.size.width + 10, self.size.height + 10, CGImageGetBitsPerComponent(self.CGImage), 0,
colourSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);
CGContextSetShadowWithColor(shadowContext, CGSizeMake(5, -5), 5, [UIColor blackColor].CGColor);
CGContextDrawImage(shadowContext, CGRectMake(0, 10, self.size.width, self.size.height), self.CGImage);
CGImageRef shadowedCGImage = CGBitmapContextCreateImage(shadowContext);
CGContextRelease(shadowContext);
UIImage * shadowedImage = [UIImage imageWithCGImage:shadowedCGImage];
CGImageRelease(shadowedCGImage);
return shadowedImage;
}
Swift extension to UIImage:
extension UIImage {
func addShadow(blurSize: CGFloat = 6.0) -> UIImage {
let shadowColor = UIColor(white:0.0, alpha:0.8).cgColor
let context = CGContext(data: nil,
width: Int(self.size.width + blurSize),
height: Int(self.size.height + blurSize),
bitsPerComponent: self.cgImage!.bitsPerComponent,
bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
context.setShadow(offset: CGSize(width: blurSize/2,height: -blurSize/2),
blur: blurSize,
color: shadowColor)
context.draw(self.cgImage!,
in: CGRect(x: 0, y: blurSize, width: self.size.width, height: self.size.height),
byTiling:false)
return UIImage(cgImage: context.makeImage()!)
}
}
(converted from Laurent Etiemble's & capikaw answers)
Usage:
let sourceImage: UIImage(named: "some image")
let shadowImage = sourceImage.addShadow()
Whilst trying to use some of the other examples here, I noticed that they seem to either hard-code the offset & blur, or do not correctly take them into account when sizing and positioning the output.
The following code solves these issues (it's based on the original work by Igor Kulagin):
extension UIImage {
/// Returns a new image with the specified shadow properties.
/// This will increase the size of the image to fit the shadow and the original image.
func withShadow(blur: CGFloat = 6, offset: CGSize = .zero, color: UIColor = UIColor(white: 0, alpha: 0.8)) -> UIImage {
let shadowRect = CGRect(
x: offset.width - blur,
y: offset.height - blur,
width: size.width + blur * 2,
height: size.height + blur * 2
)
UIGraphicsBeginImageContextWithOptions(
CGSize(
width: max(shadowRect.maxX, size.width) - min(shadowRect.minX, 0),
height: max(shadowRect.maxY, size.height) - min(shadowRect.minY, 0)
),
false, 0
)
let context = UIGraphicsGetCurrentContext()!
context.setShadow(
offset: offset,
blur: blur,
color: color.cgColor
)
draw(
in: CGRect(
x: max(0, -shadowRect.origin.x),
y: max(0, -shadowRect.origin.y),
width: size.width,
height: size.height
)
)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
I needed a bigger shadow for my UIImages. Here is my variant of the answer that allows for a customized shadow size with no offset.
- (UIImage*)imageWithShadow:(UIImage*)originalImage BlurSize:(float)blurSize {
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef shadowContext = CGBitmapContextCreate(NULL, originalImage.size.width + (blurSize*2), originalImage.size.height + (blurSize*2), CGImageGetBitsPerComponent(originalImage.CGImage), 0, colourSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);
CGContextSetShadowWithColor(shadowContext, CGSizeMake(0, 0), blurSize, [UIColor blackColor].CGColor);
CGContextDrawImage(shadowContext, CGRectMake(blurSize, blurSize, originalImage.size.width, originalImage.size.height), originalImage.CGImage);
CGImageRef shadowedCGImage = CGBitmapContextCreateImage(shadowContext);
CGContextRelease(shadowContext);
UIImage * shadowedImage = [UIImage imageWithCGImage:shadowedCGImage];
CGImageRelease(shadowedCGImage);
return shadowedImage;
}
Use it like:
UIImage *shadowedImage = [self imageWithShadow:myImage BlurSize:30.0f];
I needed a method that would accept an UIImage as a parameter and return a slightly larger UIImage with shadows. The posts here have been very helpful to me so here's my method.
The returned image has a centered 5px shadow on each side.
+ (UIImage*)imageWithShadowForImage:(UIImage *)initialImage {
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef shadowContext = CGBitmapContextCreate(NULL, initialImage.size.width + 10, initialImage.size.height + 10, CGImageGetBitsPerComponent(initialImage.CGImage), 0, colourSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);
CGContextSetShadowWithColor(shadowContext, CGSizeMake(0,0), 5, [UIColor blackColor].CGColor);
CGContextDrawImage(shadowContext, CGRectMake(5, 5, initialImage.size.width, initialImage.size.height), initialImage.CGImage);
CGImageRef shadowedCGImage = CGBitmapContextCreateImage(shadowContext);
CGContextRelease(shadowContext);
UIImage * shadowedImage = [UIImage imageWithCGImage:shadowedCGImage];
CGImageRelease(shadowedCGImage);
return shadowedImage;
}
My method that draws a shadow on an image. It has custom shadow parameters and renders image in correct size and screen scale. Should be implemented in UIImage extension. (Swift 3).
func addingShadow(blur: CGFloat = 1, shadowColor: UIColor = UIColor(white: 0, alpha: 1), offset: CGSize = CGSize(width: 1, height: 1) ) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSize(width: size.width + 2 * blur, height: size.height + 2 * blur), false, 0)
let context = UIGraphicsGetCurrentContext()!
context.setShadow(offset: offset, blur: blur, color: shadowColor.cgColor)
draw(in: CGRect(x: blur - offset.width / 2, y: blur - offset.height / 2, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
To make an image a CGImage as in this case , try
self.shadowImage.CGImage
GK100's answer in Swift 2 / iOS9
func addShadow(blurSize: CGFloat = 6.0) -> UIImage {
let data : UnsafeMutablePointer<Void> = nil
let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB()!
let bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)
let myColorValues:[CGFloat] = [0.0, 0.0, 0.0, 0.8]
let myColor = CGColorCreate(colorSpace, myColorValues)
let shadowContext : CGContextRef = CGBitmapContextCreate(data, Int(self.size.width + blurSize), Int(self.size.height + blurSize), CGImageGetBitsPerComponent(self.CGImage), 0, colorSpace, bitmapInfo.rawValue)!
CGContextSetShadowWithColor(shadowContext, CGSize(width: blurSize/2,height: -blurSize/2), blurSize, myColor)
CGContextDrawImage(shadowContext, CGRect(x: 0, y: blurSize, width: self.size.width, height: self.size.height), self.CGImage)
let shadowedCGImage : CGImageRef = CGBitmapContextCreateImage(shadowContext)!
let shadowedImage : UIImage = UIImage(CGImage: shadowedCGImage)
return shadowedImage
}

Add transparent space around a UIImage

Lets say we have an image of 600X400 pixel and we want to end up with an new image of 1000x1000 pixel which contains the initial image in the centre and transparent space around it. How can I achieve that in code?
In Swift you can write an extension to UIImage that draws image with insets around it.
Swift 3:
import UIKit
extension UIImage {
func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: self.size.width + insets.left + insets.right,
height: self.size.height + insets.top + insets.bottom), false, self.scale)
let _ = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
OLD ANSWER:
import UIKit
extension UIImage {
func imageWithInsets(insets: UIEdgeInsets) -> UIImage {
UIGraphicsBeginImageContextWithOptions(
CGSizeMake(self.size.width + insets.left + insets.right,
self.size.height + insets.top + insets.bottom), false, self.scale)
let context = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.drawAtPoint(origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
This is the solution in Swift 4 inspired by DrummerB answer:
import UIKit
extension UIImage {
func addImagePadding(x: CGFloat, y: CGFloat) -> UIImage? {
let width: CGFloat = size.width + x
let height: CGFloat = size.height + y
UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0)
let origin: CGPoint = CGPoint(x: (width - size.width) / 2, y: (height - size.height) / 2)
draw(at: origin)
let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithPadding
}
}
How to apply:
let image = UIImage(named: "your-image")!
let imageView = UIImageView(image: image.addImagePadding(x: 50, y: 50))
imageView.backgroundColor = UIColor.gray
view.addSubview(imageView)
Features:
Simply pass padding values via parameters
Colored padding (by setting the UIGraphicsBeginImageContextWithOptions opaque parameter to false)
You create a new image context that is 1000x1000, draw your old image in the middle, then get the new UIImage from the context.
// Setup a new context with the correct size
CGFloat width = 1000;
CGFloat height = 1000;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f,
(height - oldImage.size.height) / 2.0f);
[oldImage drawAtPoint:origin];
// Clean up and get the new image.
UIGraphicsPopContext();
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
A fix for appsunited's answer with better naming convension. To not confuse it the function is mutating or not:
extension UIImage {
func withPadding(_ padding: CGFloat) -> UIImage? {
return withPadding(x: padding, y: padding)
}
func withPadding(x: CGFloat, y: CGFloat) -> UIImage? {
let newWidth = size.width + 2 * x
let newHeight = size.height + 2 * y
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
let origin = CGPoint(x: (newWidth - size.width) / 2, y: (newHeight - size.height) / 2)
draw(at: origin)
let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithPadding
}
}
Make a category on UIImage and try this:
+ (UIImage *)imageWithInsets:(CGRect)insetRect image:(UIImage *)image {
CGRect newRect = CGRectMake(0.0, 0.0, insetRect.origin.x+insetRect.size.width+image.size.width, insetRect.origin.y+insetRect.size.height+image.size.height);
// Setup a new context with the correct size
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake(insetRect.origin.x, insetRect.origin.y);
[image drawAtPoint:origin];
// Clean up and get the new image.
UIGraphicsPopContext();
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

UIGraphicsBeginImageContextWithOptions not capturing shadows / gradients

THE PROBLEM
I'm using UIGraphicsBeginImageContextWithOptions to save a screenshot into a UIImage. While this works, the resulting UIImage is missing any shadows and gradients.
How do I get it to also screenshot the layer stuff?
TEST CODE SHOWING THE PROBLEM
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation ViewController
- (void)viewDidLoad {
//super
[super viewDidLoad];
//background
self.view.backgroundColor = [UIColor whiteColor];
//container
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
container.backgroundColor = [UIColor blackColor];
[self.view addSubview:container];
//circle
UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 10, 10)];
circle.backgroundColor = [UIColor whiteColor];
circle.layer.cornerRadius = 5;
circle.layer.shadowColor = [UIColor redColor].CGColor;
circle.layer.shadowOffset = CGSizeZero;
circle.layer.shadowOpacity = 1;
circle.layer.shadowRadius = 10;
circle.layer.shadowPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-10, -10, 30, 30)].CGPath;
[container addSubview:circle];
//screenshot
UIGraphicsBeginImageContextWithOptions(container.bounds.size, container.opaque, 0.0);
[container.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//image view
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(10, 568-200-20-10, 300, 200)];
iv.image = screenshot;
[self.view addSubview:iv];
}
#end
SIMULATOR SCREENSHOT SHOWING THE PROBLEM
The top area is the original view. The bottom area is a UIImageView with the screenshot.
In this line ,
UIGraphicsBeginImageContextWithOptions(container.bounds.size, container.opaque, 0.0);
put NO instead of container.opaque
Use this method ,
- (UIImage *)imageOfView:(UIView *)view
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(view.bounds.size);
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
For drop shadow on an Image:
public func generateShadow(for image: UIImage, shadowOffset: CGSize,blur: CGFloat, shadowColor: CGColor ) -> UIImage? {
let size = CGSize(width: image.size.width + blur, height:
image.size.height + blur)
return MediaPickerFramework.generateImage(CGSize(width: size.width, height: size.width), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: shadowOffset,
blur: blur,
color: shadowColor)
let center = CGPoint(x: size.width / 2, y: size.height / 2)
let imageOrigin = CGPoint(x: center.x - (image.size.width / 2), y: center.y - (image.size.height / 2))
context.draw(image.cgImage!,
in: CGRect(x: imageOrigin.x, y: imageOrigin.y, width: image.size.width, height: image.size.height),
byTiling:false)
})
}
and for draw Gradient on an image, you can use the below function
func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> UIImage? {
guard let image = image else {
return nil
}
let imageSize = image.size
UIGraphicsBeginImageContextWithOptions(imageSize, false, image.scale)
if let context = UIGraphicsGetCurrentContext() {
let imageRect = CGRect(origin: CGPoint(), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
let gradientColors = colors.map { $0.cgColor } as CFArray
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
var locations: [CGFloat] = []
for i in 0 ..< colors.count {
locations.append(delta * CGFloat(i))
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
context.restoreGState()
}
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return tintedImage
}

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