SKSpriteNode - create a round corner node? - ios

Is there a way to make a SKSpriteNode round cornered? I am trying to create a Tile likesqaure blocks with color filled SKSpriteNode:
SKSpriteNode *tile = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithRed:0.0/255.0
green:128.0/255.0
blue:255.0/255.0
alpha:1.0] size:CGSizeMake(30, 30)];
How can I make it round cornered?
Thanks!

To get a rounded corner node you can use 2 approaches, each of them requires use of SKShapeNode.
First way is to use SKShapeNode and set its path to be a rounded rectangle like this:
SKShapeNode* tile = [SKShapeNode node];
[tile setPath:CGPathCreateWithRoundedRect(CGRectMake(-15, -15, 30, 30), 4, 4, nil)];
tile.strokeColor = tile.fillColor = [UIColor colorWithRed:0.0/255.0
green:128.0/255.0
blue:255.0/255.0
alpha:1.0];
The other one uses sprite node,crop node and SKShapeNode with rounded rectangle as crop nodes mask:
SKSpriteNode *tile = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithRed:0.0/255.0
green:128.0/255.0
blue:255.0/255.0
alpha:1.0] size:CGSizeMake(30, 30)];
SKCropNode* cropNode = [SKCropNode node];
SKShapeNode* mask = [SKShapeNode node];
[mask setPath:CGPathCreateWithRoundedRect(CGRectMake(-15, -15, 30, 30), 4, 4, nil)];
[mask setFillColor:[SKColor whiteColor]];
[cropNode setMaskNode:mask];
[cropNode addChild:tile];
If your tiles are one solid colour, i suggest you go with the first approach.

In Swift 3 you can create with:
let tile = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 30, height: 30), cornerRadius: 10)

Hope this helps:
SKSpriteNode *make_rounded_rectangle(UIColor *color, CGSize size, float radius)
{
UIGraphicsBeginImageContext(size);
[color setFill];
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
[path fill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SKTexture *texture = [SKTexture textureWithImage:image];
return [SKSpriteNode spriteNodeWithTexture:texture];
}

This is heavily inspired by the accepted answer, yet it uses a more readable way to create the SKShapeNode and also fixes the annoying pixel line around the crop. Seems like a minor detail, but it may cost someone a few minutes.
CGFloat cornerRadius = 15;
SKCropNode *cropNode = [SKCropNode node];
SKShapeNode *maskNode = [SKShapeNode shapeNodeWithRectOfSize:scoreNode.size cornerRadius:cornerRadius];
[maskNode setLineWidth:0.0];
[maskNode setFillColor:[UIColor whiteColor]];
[cropNode setMaskNode:maskNode];
[cropNode addChild:scoreNode];

It's really this simple.......
class YourSprite: SKSpriteNode {
func yourSetupFunction() {
texture = SKTexture( image: UIImage(named: "cat")!.circleMasked! )
There's nothing more to it.
You really can not use SKShapeNode.
It's just that simple. It's an insane performance hit using SKShapeNode, it's not the right solution, it's pointlessly difficult, and the purpose of SKShapeNode just has no relation to this problem.
Look at all them kitties!
The code for circleMasked is this simple:
(All projects that deal with images will need this anyway.)
extension UIImage {
var isPortrait: Bool { return size.height > size.width }
var isLandscape: Bool { return size.width > size.height }
var breadth: CGFloat { return min(size.width, size.height) }
var breadthSize: CGSize { return CGSize(width: breadth, height: breadth) }
var breadthRect: CGRect { return CGRect(origin: .zero, size: breadthSize) }
var circleMasked: UIImage? {
UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)
defer { UIGraphicsEndImageContext() }
guard let cgImage = cgImage?.cropping(to: CGRect(origin:
CGPoint(
x: isLandscape ? floor((size.width - size.height) / 2) : 0,
y: isPortrait ? floor((size.height - size.width) / 2) : 0),
size: breadthSize))
else { return nil }
UIBezierPath(ovalIn: breadthRect).addClip()
UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation)
.draw(in: breadthRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
// classic 'circleMasked' stackoverflow fragment
// courtesy Leo Dabius /a/29047372/294884
}
That's all there is to it.

Swift 4:
A good solution could be created before you should add your sprite to his parent, I've maded a little extension that could be corrected as you wish:
extension SKSpriteNode {
func addTo(parent:SKNode?, withRadius:CGFloat) {
guard parent != nil else { return }
guard withRadius>0.0 else {
parent!.addChild(self)
return
}
let radiusShape = SKShapeNode.init(rect: CGRect.init(origin: CGPoint.zero, size: size), cornerRadius: withRadius)
radiusShape.position = CGPoint.zero
radiusShape.lineWidth = 2.0
radiusShape.fillColor = UIColor.red
radiusShape.strokeColor = UIColor.red
radiusShape.zPosition = 2
radiusShape.position = CGPoint.zero
let cropNode = SKCropNode()
cropNode.position = self.position
cropNode.zPosition = 3
cropNode.addChild(self)
cropNode.maskNode = radiusShape
parent!.addChild(cropNode)
}
}
Usage:
let rootNode = SKSpriteNode(color: .white, size: self.size)
rootNode.alpha = 1.0
rootNode.anchorPoint = CGPoint.zero
rootNode.position = CGPoint.zero
rootNode.zPosition = 1
rootNode.name = "rootNode"
rootNode.addTo(parent: self, withRadius: 40.0)

from the class reference:
"An SKSpriteNode is a node that draws a textured image, a colored square, or a textured image blended with a color."
It seems the easiest way is to draw a block with rounded corners and then use one of these class methods:
spriteNodeWithImageNamed:
spriteNodeWithTexture:
spriteNodeWithTexture:size:

Here's a Swift 3 snippet based on the second solution of the accepted answer.
func createPlayerRoundedNode(){
let tile = SKSpriteNode(color: .white, size: CGSize(width: 30, height: 30))
tile.zPosition = 3
tile.name = "tile node"
let cropNode = SKCropNode()
cropNode.zPosition = 2
cropNode.name = "crop node"
let mask = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 30, height: 30), cornerRadius: 10)
mask.fillColor = SKColor.darkGray
mask.zPosition = 2
mask.name = "mask node"
cropNode.maskNode = mask
self.addChild(cropNode)
cropNode.addChild(tile)
}

Related

Add Border to circular UIImage

I have been looking at many stack overflow posts but none have them have been able to give me my desired solution. So far I have been able to get an image and convert it into a circle using AlamoFire. However, unfortunately alamo fire does not provide an option to add a border to a UIImage. I was wondering if anyone had a solution to my problem. Here is my code for making the image into a circle:
if let downloadedImage = UIImage(data: data!) {
let markerImage = downloadedImage
let markerImageSize = CGSize(width: 50, height: 50)
let markerImageFilter = AspectScaledToFillSizeCircleFilter(size: markerImageSize)
let finalMarkerImage = markerImageFilter.filter(markerImage)
marker.icon = finalMarkerImage
}
As you can see I am able to get a circle but not one with a border. So far I have tried many stack overflow post solutions to try and work with my AlamoFire solution. Here are some of the posts:
Making a UIImage to a circle form
Cut a UIImage into a circle Swift(iOS)
Here is what I currently have:
Here is what I want:
Any help would be much appreciated. Thanks!
I would suggest that you should apply the required appearance to the UIImageView that contains your UIImage, as follows:
imageView.layer.cornerRadius = imageView.frame.size.width / 2
imageView.layer.masksToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = UIColor.brown.cgColor
Update:
Since you are working with Google Maps (GMSMarker), you should create an UIImageView programmatically (apply the above code snippet to it) and add it to your marker as iconView, as follows:
marker.iconView = imageView
So, it should be similar to:
// of course the values of the width/height (size) is up to you
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
imageView.layer.cornerRadius = imageView.frame.size.width / 2
imageView.layer.masksToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = UIColor.white.cgColor
// set your image
imageView.image = ...
marker.iconView = imageView
This should create round image with white border…
func round(image: UIImage) -> UIImage {
let imageWidth = image.size.width
let imageHeight = image.size.height
let diameter = min(imageWidth, imageHeight)
let isLandscape = imageWidth > imageHeight
let xOffset = isLandscape ? (imageWidth - diameter) / 2 : 0
let yOffset = isLandscape ? 0 : (imageHeight - diameter) / 2
let imageSize = CGSize(width: diameter, height: diameter)
return UIGraphicsImageRenderer(size: imageSize).image { _ in
let ovalPath = UIBezierPath(ovalIn: CGRect(origin: .zero, size: imageSize))
ovalPath.addClip()
image.draw(at: CGPoint(x: -xOffset, y: -yOffset))
UIColor.white.setStroke()
ovalPath.lineWidth = diameter / 50
ovalPath.stroke()
}
}
Then
let roundImage = round(image: downloadedImage)
for people struggling with the obj-c version of #ashley answer. Same logic
+ (UIImage *)drawBorderToImage:(UIImage *)image withColor:(UIColor *)color andThickness:(CGFloat)thickness {
CGFloat diameter = MIN(image.size.width, image.size.height);
BOOL isLandscape = image.size.width > image.size.height;
CGFloat xOffset = isLandscape ? (image.size.width - diameter) / 2 : 0;
CGFloat yOffset = isLandscape ? 0 : (image.size.height - diameter) / 2;
CGSize imageSize = CGSizeMake(diameter, diameter);
UIGraphicsBeginImageContext(image.size);
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
[ovalPath addClip];
[image drawAtPoint:CGPointMake(-xOffset, -yOffset)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, color.CGColor);
ovalPath.lineWidth = thickness;
[ovalPath stroke];
UIImage *borderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return borderedImage;
}

Drop shadow in ios

How to drop an object shadow in iOS?
My object is UIImageView and i want to drop an elliptical shadow.Please refer image for reference.
Better you use another image for showing shadow. Use blur image or change alpha of the imageview.
Or if you want to do it programmatically, try it:
Obj c:
//create elliptical shadow for image through UIBezierPath
CGRect ovalRect = CGRectMake(0.0f, _imageView.frame.size.height + 10, _imageView.frame.size.width, 15);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:ovalRect];
//applying shadow to path
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 3.0;
_imageView.layer.shadowPath = path.CGPath;
Swift :
//create elliptical shdow forimage through UIBezierPath
var ovalRect = CGRectMake(0.0, imageView.frame.size.height + 10, imageView.frame.size.width, 15)
var path = UIBezierPath(ovalInRect: ovalRect)
//applying shadow to path
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowPath = path.CGPath
Output:
Taken from http://www.innofied.com/implementing-shadow-ios/ and also have a look to know more: UIView with rounded corners and drop shadow?
You can use CAShapeLayer like this:
Objective-C:
// init CAShapeLayer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
shadowLayer.frame = CGRectMake(CGRectGetMinX(_imageView.frame), CGRectGetMaxY(_imageView.frame), _imageView.frame.size.width, _imageView.frame.size.height);
shadowLayer.path = [UIBezierPath bezierPathWithOvalInRect:shadowLayer.bounds].CGPath;
shadowLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.02].CGColor;
shadowLayer.lineWidth = 0;
[self.view.layer addSublayer: shadowLayer];
Swift 3
let shadowLayer = CAShapeLayer()
shadowLayer.frame = CGRect(x: imageView.frame.minX, y: imageView.frame.maxY, width: imageView.frame.width, height: imageView.frame.height * 0.25)
shadowLayer.path = UIBezierPath(ovalIn: shadowLayer.bounds).cgPath
shadowLayer.fillColor = UIColor(white: 0, alpha: 0.02).cgColor
shadowLayer.lineWidth = 0
view.layer.addSublayer(shadowLayer)
Output:

Transparent UIButton title

How can I subtract the shape of the title text from the button background?
I created a custom UIButton class. Currently the code to add the border and text color looks simply like this
layer.borderWidth = 2.0
layer.borderColor = buttonColor.CGColor
layer.cornerRadius = (CGFloat(bounds.height) + paddingVertical * 2.0) / 2.0
setTitleColor(buttonColor, forState: UIControlState.Normal)
Any advice on how I can subtract the button title shape from background. Do I need to render all this as an image or is there a better alternative?
Extension for swift 4
extension UIButton{
func clearColorForTitle() {
let buttonSize = bounds.size
if let font = titleLabel?.font{
let attribs = [NSAttributedStringKey.font: font]
if let textSize = titleLabel?.text?.size(withAttributes: attribs){
UIGraphicsBeginImageContextWithOptions(buttonSize, false, UIScreen.main.scale)
if let ctx = UIGraphicsGetCurrentContext(){
ctx.setFillColor(UIColor.white.cgColor)
let center = CGPoint(x: buttonSize.width / 2 - textSize.width / 2, y: buttonSize.height / 2 - textSize.height / 2)
let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: buttonSize.width, height: buttonSize.height))
ctx.addPath(path.cgPath)
ctx.fillPath()
ctx.setBlendMode(.destinationOut)
titleLabel?.text?.draw(at: center, withAttributes: [NSAttributedStringKey.font: font])
if let viewImage = UIGraphicsGetImageFromCurrentImageContext(){
UIGraphicsEndImageContext()
let maskLayer = CALayer()
maskLayer.contents = ((viewImage.cgImage) as AnyObject)
maskLayer.frame = bounds
layer.mask = maskLayer
}
}
}
}
}
Usage :
button.clearColorForTitle()
I've written some code for you, my UIButton subclass called AKStencilButton (available on github https://github.com/purrrminator/AKStencilButton):
#import "AKStencilButton.h"
#import <QuartzCore/QuartzCore.h>
#interface AKStencilButton ()
{
UIColor * _buttonColor;
}
#end
#implementation AKStencilButton
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]){
[self setupDefaults];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]){
[self setupDefaults];
}
return self;
}
-(void)setupDefaults
{
self.backgroundColor = [UIColor redColor];
self.layer.cornerRadius = 4;
self.clipsToBounds = YES;
}
-(void)layoutSubviews
{
[super layoutSubviews];
[self refreshMask];
}
-(void)setTitleColor:(UIColor *)color forState:(UIControlState)state
{
[super setTitleColor:[UIColor clearColor] forState:state];
}
-(void)refreshMask
{
self.titleLabel.backgroundColor = [UIColor clearColor];
[self setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
NSString * text = self.titleLabel.text;
CGSize buttonSize = self.bounds.size;
UIFont * font = self.titleLabel.font;
NSDictionary* attribs = #{NSFontAttributeName: self.titleLabel.font};
CGSize textSize = [text sizeWithAttributes:attribs];
UIGraphicsBeginImageContextWithOptions(buttonSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGPoint center = CGPointMake(buttonSize.width/2-textSize.width/2, buttonSize.height/2-textSize.height/2);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
CGContextAddPath(ctx, path.CGPath);
CGContextFillPath(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
[text drawAtPoint:center withAttributes:#{NSFontAttributeName:font}];
UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CALayer *maskLayer = [CALayer layer];
maskLayer.contents = (__bridge id)(viewImage.CGImage);
maskLayer.frame = self.bounds;
self.layer.mask = maskLayer;
}
#end
it looks like this (sorry for the rainbow):
I added the following method to UIView to solve this:
-(void)maskSubviews:(NSArray*)subviews {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, self.bounds);
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
for (UIView *view in subviews) {
CGPoint origin = [view convertPoint:CGPointZero toView:self];
CGContextTranslateCTM(context, origin.x, origin.y);
[view.layer renderInContext:context];
CGContextTranslateCTM(context, -origin.x, -origin.y);
}
UIImage* mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.maskView = [[UIImageView alloc] initWithImage:mask];
}
Simply call it with the views you want masked out. For example:
[button maskSubviews: #[button.titleLabel, button.imageView]]
I converted #purrrminator's solution to Swift 3, just the main method.
func clearTextButton(button: UIButton, title: NSString, color: UIColor) {
button.backgroundColor = color
button.titleLabel?.backgroundColor = UIColor.clear()
button.setTitleColor(UIColor.clear(), for: .normal)
button.setTitle(title as String, for: [])
let buttonSize: CGSize = button.bounds.size
let font: UIFont = button.titleLabel!.font
let attribs: [String : AnyObject] = [NSFontAttributeName: button.titleLabel!.font]
let textSize: CGSize = title.size(attributes: attribs)
UIGraphicsBeginImageContextWithOptions(buttonSize, false, UIScreen.main().scale)
let ctx: CGContext = UIGraphicsGetCurrentContext()!
ctx.setFillColor(UIColor.white().cgColor)
let center: CGPoint = CGPoint(x: buttonSize.width / 2 - textSize.width / 2, y: buttonSize.height / 2 - textSize.height / 2)
let path: UIBezierPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: buttonSize.width, height: buttonSize.height))
ctx.addPath(path.cgPath)
ctx.fillPath()
ctx.setBlendMode(.destinationOut)
title.draw(at: center, withAttributes: [NSFontAttributeName: font])
let viewImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let maskLayer: CALayer = CALayer()
maskLayer.contents = ((viewImage.cgImage) as! AnyObject)
maskLayer.frame = button.bounds
button.layer.mask = maskLayer
}

Can I add a border to an SKSpriteNode, similar to UIView?

I'm interested if an SKSpriteNode can be made to imitate the behavior of a UIView where I can specify border and corner radius?
self.view.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.view.layer.borderWidth = 2;
self.view.layer.cornerRadius = 2;
self.view.layer.masksToBounds = YES;
The following is a simple, dropin extension for SKSpriteNode that will allow you to draw a border with a given color around your node.
import SpriteKit
extension SKSpriteNode {
func drawBorder(color: UIColor, width: CGFloat) {
let shapeNode = SKShapeNode(rect: frame)
shapeNode.fillColor = .clear
shapeNode.strokeColor = color
shapeNode.lineWidth = width
addChild(shapeNode)
}
}
Not natively. Though you can just add a SKShapeNode as child whose path you create with CGPathCreateWithRoundedRect.
extension SKSpriteNode {
func drawBorder(color: UIColor, width: CGFloat) {
for layer in self.children {
if layer.name == "border" {
layer.removeFromParent()
}
}
let imageSize = self.texture?.size()
let lineWidth = (imageSize!.width / self.size.width) * width
let shapeNode = SKShapeNode(rect: CGRect(x: -imageSize!.width/2, y: -imageSize!.height/2, width: imageSize!.width, height: imageSize!.height))
shapeNode.fillColor = .clear
shapeNode.strokeColor = color
shapeNode.lineWidth = lineWidth
shapeNode.name = "border"
shapeNode.zPosition = 1001
self.addChild(shapeNode)
self.zPosition = 1000
}
}

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
}

Resources