Swift - Creating Shadow with 2 Different Colours for Imageview

Swift - Creating shadow with 2 different colours for imageView

To get different color shadows - one going up-left and one going down-right - on a UIImageView, one approach would be:

  • Subclass UIView
  • Give it 3 CALayer sublayers

    • Shadow 1 layer
    • Shadow 2 layer
    • Image layer

This also makes it easy to add rounded corners.

Here is a sample class. It has @IBInspectable properties to set the image, corner radius, shadow colors and shadow offsets. It is also marked @IBDesignable so you can see how it looks while designing in Storyboard / Interface Builder:

@IBDesignable
class DoubleShadowRoundedImageView: UIView {
@IBInspectable var image: UIImage? = nil {
didSet {
imageLayer.contents = image?.cgImage
}
}

@IBInspectable var cornerRadius: CGFloat = 0.0

@IBInspectable var shad1X: CGFloat = 0.0
@IBInspectable var shad1Y: CGFloat = 0.0

@IBInspectable var shad2X: CGFloat = 0.0
@IBInspectable var shad2Y: CGFloat = 0.0

@IBInspectable var shad1Color: UIColor = UIColor.blue
@IBInspectable var shad2Color: UIColor = UIColor.red

var imageLayer: CALayer = CALayer()
var shadowLayer1: CALayer = CALayer()
var shadowLayer2: CALayer = CALayer()

var shape: UIBezierPath {
return UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
}

var shapeAsPath: CGPath {
return shape.cgPath
}

var shapeAsMask: CAShapeLayer {
let s = CAShapeLayer()
s.path = shapeAsPath
return s
}

override func layoutSubviews() {
super.layoutSubviews()

clipsToBounds = false
backgroundColor = .clear

self.layer.addSublayer(shadowLayer1)
self.layer.addSublayer(shadowLayer2)
self.layer.addSublayer(imageLayer)

imageLayer.frame = bounds

imageLayer.mask = shapeAsMask

shadowLayer1.frame = bounds
shadowLayer2.frame = bounds

shadowLayer1.shadowPath = (image == nil) ? nil : shapeAsPath
shadowLayer1.shadowOpacity = 0.80

shadowLayer2.shadowPath = (image == nil) ? nil : shapeAsPath
shadowLayer2.shadowOpacity = 0.80

shadowLayer1.shadowColor = shad1Color.cgColor
shadowLayer2.shadowColor = shad2Color.cgColor

shadowLayer1.shadowOffset = CGSize(width: shad1X, height: shad1Y)
shadowLayer2.shadowOffset = CGSize(width: shad2X, height: shad2Y)

}
}

You would probably want to change some of the default values, and you might want to add some additional properties (such as shadow opacity).

Example results:

Sample Image

Multi-colored Shadow Swift

Thanks to @Josh Homann for pointing me in the right direction, but the answer was actually much more complex.

  1. The first (bottom) UIView in the hierarchy should have a plain white background and be pinned to the edges of the screen.

  2. The next view should be a UIImageView with an alpha of 0.75.

  3. Then you should add a UIVisualEffectView with the same dimensions as the first view.

  4. Finally, add another UIImageView that is the same size as the first UIImageView.

Your IB hierarchy should look similar to this:

IB Hierarchy

The final result is this:

Final Result

Creating a shadow for a UIImageView that has rounded corners?

If you set clipsToBounds to true, this will round the corners but prevent the shadow from appearing. In order to resolve this, you can create two views. The container view should have the shadow, and its subview should have the rounded corners.

The container view has clipsToBounds set to false, and has the shadow properties applied. If you want the shadow to be rounded as well, use the UIBezierPath constructor that takes in a roundedRect and cornerRadius.

let outerView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
outerView.clipsToBounds = false
outerView.layer.shadowColor = UIColor.black.cgColor
outerView.layer.shadowOpacity = 1
outerView.layer.shadowOffset = CGSize.zero
outerView.layer.shadowRadius = 10
outerView.layer.shadowPath = UIBezierPath(roundedRect: outerView.bounds, cornerRadius: 10).cgPath

Next, set the image view (or any other type of UIView) to be the same size of the container view, set clipsToBounds to true, and give it a cornerRadius.

let myImage = UIImageView(frame: outerView.bounds)
myImage.clipsToBounds = true
myImage.layer.cornerRadius = 10

Finally, remember to make the image view a subview of the container view.

outerView.addSubview(myImage)

The result should look something like this:

Sample Image

Shadow effects on ImageView in iOS

Perhaps, adding a shadow to the layer seems to work. You may want to try something like this:

//  perspectiveView.transform = CGAffineTransformMakeRotation(-50.0f);   
perspectiveView.layer.shadowOffset = CGSizeMake(10, 10);
perspectiveView.layer.shadowRadius = 5.0;
perspectiveView.layer.shadowOpacity = 0.6;

perspectiveView.layer.masksToBounds = NO;

You will need to playaround with these values to match your requirements. Hope this helps.

Here is my output:

Sample Image

Gradient / shadows in UIImageView like Flipboard

You could use CAGradientLayer:

  1. Add QuartzCore.framework to your project. (See Linking to Library or Framework).

  2. Import the QuartzCore header:

    #import <QuartzCore/QuartzCore.h>
  3. Add a CAGradientLayer to the view in question. For example, for a simple gradient from gray to white:

    - (void)addGradientToView:(UIView *)view
    {
    CAGradientLayer *gradient = [CAGradientLayer layer];
    gradient.frame = view.bounds;
    gradient.colors = @[(id)[[UIColor lightGrayColor] CGColor],
    (id)[[UIColor whiteColor] CGColor]];
    [view.layer insertSublayer:gradient atIndex:0];
    }

    Or from one shade of gray, to another, and back:

    - (void)addGradientToView:(UIView *)view
    {
    CAGradientLayer *gradient = [CAGradientLayer layer];
    gradient.frame = view.bounds;
    gradient.colors = @[(id)[[UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0] CGColor],
    (id)[[UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0] CGColor],
    (id)[[UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0] CGColor]];
    [view.layer insertSublayer:gradient atIndex:0];
    }

Just change the colors in your array of colors to alter the gradient to achieve the desired effect. You may want to put the image view in a container view and apply the gradient to the container view, not the image view. Furthermore, presumably obvious, but this assumes that the images you are using in your UIImageView employ transparent backgrounds. If they don't, you'll have to alter them either (a) build the gradient right into the image; or (b) replace the background of the image with a transparency so the above CAGradientLayer effect can be seen.

How do I cast two shadows from an UIImageView with a SF Symbol as the image?

Sample Image

Fixed the shadow problem. I was able to generate double shadows by adding 3 layers on my UIView. The furthest behind (the first sublayer of the view's layer) is the darker shadow, the one on top of that is the lighter shadow and on top of that I have one with the normal color.

I'm 'drawing' the images on the layers by applying a shadow path & color.

The image I'm drawing is coming from a method like listed below, but it's not being generated from a SFSymbols. I would like to generate it during runtime so I end up with a simple method of using symbols to place Neumorphic elements in our apps. The manual generation of these BezierPaths isn't really pleasant.

static var shieldFill: UIBezierPath = {
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 58.94, y: 115.53))
bezierPath.addCurve(to: CGPoint(x: 62.84, y: 114.4), controlPoint1: CGPoint(x: 60.06, y: 115.53), controlPoint2: CGPoint(x: 61.57, y: 115.04))
bezierPath.addCurve(to: CGPoint(x: 101.76, y: 74.46), controlPoint1: CGPoint(x: 91.5, y: 100.49), controlPoint2: CGPoint(x: 101.76, y: 91.89))
bezierPath.addLine(to: CGPoint(x: 101.76, y: 39.94))
bezierPath.addCurve(to: CGPoint(x: 92.04, y: 25.05), controlPoint1: CGPoint(x: 101.76, y: 31.84), controlPoint2: CGPoint(x: 99.41, y: 28.12))
bezierPath.addCurve(to: CGPoint(x: 67.63, y: 16.85), controlPoint1: CGPoint(x: 87.89, y: 23.29), controlPoint2: CGPoint(x: 71.29, y: 17.97))
bezierPath.addCurve(to: CGPoint(x: 58.94, y: 15.53), controlPoint1: CGPoint(x: 64.94, y: 16.11), controlPoint2: CGPoint(x: 61.52, y: 15.53))
bezierPath.addCurve(to: CGPoint(x: 50.24, y: 16.85), controlPoint1: CGPoint(x: 56.35, y: 15.53), controlPoint2: CGPoint(x: 52.93, y: 16.11))
bezierPath.addCurve(to: CGPoint(x: 25.83, y: 25.05), controlPoint1: CGPoint(x: 46.53, y: 17.97), controlPoint2: CGPoint(x: 29.93, y: 23.29))
bezierPath.addCurve(to: CGPoint(x: 16.11, y: 39.94), controlPoint1: CGPoint(x: 18.46, y: 28.12), controlPoint2: CGPoint(x: 16.11, y: 31.84))
bezierPath.addLine(to: CGPoint(x: 16.11, y: 74.46))
bezierPath.addCurve(to: CGPoint(x: 55.03, y: 114.4), controlPoint1: CGPoint(x: 16.11, y: 91.89), controlPoint2: CGPoint(x: 26.46, y: 100.29))
bezierPath.addCurve(to: CGPoint(x: 58.94, y: 115.53), controlPoint1: CGPoint(x: 56.3, y: 115.04), controlPoint2: CGPoint(x: 57.76, y: 115.53))
bezierPath.close()

return bezierPath
}()

How can I add shadow to a circle UIImageView or UIView?

Use the CALayer's shadowPath property and add a UIBezierPath with rounded rect

self.pic.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.pic.frame cornerRadius:50.0].CGPath;

EDIT

For a square-ish image view this technique does not work directly because, as you said, the image view goes back to square. Reason: You set clipsToBounds = NO to show the shadow which removes the clipping for corner radius, where imageView is subview of container.

Workaround:

Add your imageview in a container view and then apply the layer shadow to this container. Following is the code I tried.

[self.imageView.layer setCornerRadius:60.0];
[self.imageView.layer setMasksToBounds:YES];
self.imageView.clipsToBounds = YES;

self.container.backgroundColor = [UIColor clearColor];
self.container.layer.shadowColor = [UIColor blackColor].CGColor;
self.container.layer.shadowOffset = CGSizeMake(5,15);
self.container.layer.shadowOpacity = 0.5;
self.container.layer.shadowRadius = 2.0;
self.container.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.container.bounds cornerRadius:100.0].CGPath;

The resultant effect is as shown in the screenshot,

Sample Image

IOS: How to make a shadow for UIView on 4 side (top,right,bottom and left)

Like this:

float shadowSize = 10.0f;
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(self.avatarImageView.frame.origin.x - shadowSize / 2,
self.avatarImageView.frame.origin.y - shadowSize / 2,
self.avatarImageView.frame.size.width + shadowSize,
self.avatarImageView.frame.size.height + shadowSize)];
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.avatarImageView.layer.shadowOpacity = 0.8f;
self.avatarImageView.layer.shadowPath = shadowPath.CGPath;

Swift 3 version:

    let shadowSize : CGFloat = 5.0
let shadowPath = UIBezierPath(rect: CGRect(x: -shadowSize / 2,
y: -shadowSize / 2,
width: self.avatarImageView.frame.size.width + shadowSize,
height: self.avatarImageView.frame.size.height + shadowSize))
self.avatarImageView.layer.masksToBounds = false
self.avatarImageView.layer.shadowColor = UIColor.black.cgColor
self.avatarImageView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.avatarImageView.layer.shadowOpacity = 0.5
self.avatarImageView.layer.shadowPath = shadowPath.cgPath

How to use Quartz 2D to add drop shadow to an UIImage or UIImageView?

imageView.layer.shadowColor = [UIColor blackColor].CGColor;
imageView.layer.shadowOffset = CGSizeMake(0, 1);
imageView.layer.shadowOpacity = 1;
imageView.layer.shadowRadius = 1.0;

Don't forget to #import <QuartzCore/QuartzCore.h> in your implementation.

EDIT:

Adding in @Luke's comment:

Just a little gotcha that might save some other people some time make sure you have not set layer.masksToBounds to YES on your view otherwise the shadow will not appear.



Related Topics



Leave a reply



Submit