The shadow in my cell is not working at all.
This is one of the view that I want to add shadow but it's not working. I added this code inside my custom cell class.
super.layoutSubviews()
UIview1.layer.cornerRadius = 7
UIview1.layer.borderWidth = 1.0
UIview1.layer.borderColor = HexColor.hexStringToUIColor(hex: "FA2537").cgColor
UIview1.layer.masksToBounds = true
UIview1.layer.shadowColor = HexColor.hexStringToUIColor(hex: "01A4B7").cgColor
UIview1.layer.shadowOpacity = 0.5
UIview1.layer.shadowOffset = CGSize.zero
UIview1.layer.shadowRadius = 5
}
For showing shadow on a view, you need to set its layer's masksToBounds property false.
or you can try this.
You can make a method like this and can use:
extension UIView {
func setShadowWith(color: UIColor = UIColor.black, shadowOpacity: Float = 0.2, radius: Float = 1.0, shadowOffSet: CGSize = CGSize(width: 0, height: 1)) {
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = shadowOpacity
self.layer.shadowOffset = shadowOffSet
self.layer.shadowRadius = CGFloat(radius)
}
}
and can use function like:
yourContainerView.setShadowWith()
Here parameters used in functions are taking default values. you can change accordingly.
Set maskToBounds to false instead of true:
UIview1.layer.masksToBounds = false
Related
I have collection view (If the process is easier on a table view I can change it to that).
I need to display the index path of the Cell just before displaying the collection view cell.
I have been trying to figure out how to work on this, here are the things I worked out (Nothing has been a good solution).
Firstly, I tried using a Collection view cell with an added Label (For numbering) and UIView (For showing the content next to it). But the shadow code is not working for the UIView on the collection view cell.
#objc extension CALayer {
func applySketchShadow(
color: UIColor = .black,
alpha: Float = 0.5,
x: CGFloat = 0,
y: CGFloat = 2,
blur: CGFloat = 4,
spread: CGFloat = 0)
{
shadowColor = color.cgColor
shadowOpacity = alpha
shadowOffset = CGSize(width: x, height: y)
shadowRadius = blur / 2.0
if spread == 0 {
shadowPath = nil
} else {
let dx = -spread
let rect = bounds.insetBy(dx: dx, dy: dx)
shadowPath = UIBezierPath(rect: rect).cgPath
}
}
}
#objc extension UIView{
func applyShadowToView(){
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.clear.cgColor
self.layer.masksToBounds = true
self.layer.masksToBounds = false
self.layer.applySketchShadow(color: UIColor.black, alpha: 0.09, x: 3, y: 2, blur: 50, spread: 4)
}}
For some weird reasons, my shadow view is not showing up. I tried various codes from online just to be sure, nothing works out.
Other things I thought off are having a header view to show the number of the Collection view, but header view needs to be as wide as Frame so this is not an option and
the other thing is having 2 collection view cells, even cells showing the Number and odd cells showing content.
But the problem with this is I want to add rearranging functionality later on and this method will not work when I want to do it.
I'm having a funny issue working on my first tvOS application (actually my first iOS application in general since years).
What I want to achieve is to have a custom zoom and shadow for the cells of my UICollectionView when they are selected.
The weird thing is I'm able to see the zoom effect and the shadow for the cells, but when these are at both sides of the collection, the shadow is not rendered.
Let me show you the issue with few images.
This is the correct result I'd like to achieve and it works fine for "internal" cells: bigger shadow + zoom (scale).
This is the wrong result I'm getting for "external" cells: zoom (scale) is ok, but the shadow is not updated. This is also valid for the other "external" cell on the right side of the collection.
Further funny thing is, if I do not scale the cells, then the shadow is correctly update:
This is the code of my custom UICollectionViewCell:
import UIKit
class MyCloudCollectionViewCell: UICollectionViewCell {
var selectTrans: UIFocusAnimationCoordinator?
var scale : CGFloat = 0.0
override func layoutSubviews() {
super.layoutSubviews()
self.clipsToBounds = false
self.layer.masksToBounds = false
self.layer.shadowOpacity = 0.20;
self.layer.shadowRadius = 4.0;
self.layer.shadowOffset = CGSize(width: 1, height: 6);
self.scale = 1.0
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
coordinator.addCoordinatedAnimations({
super.didUpdateFocus(in: context, with: coordinator)
if self.isFocused {
self.layer.shadowOpacity = 0.25;
self.layer.shadowRadius = 4.0;
self.layer.shadowOffset = CGSize(width: 1, height: 18);
self.scale = 1.19
let transform = CGAffineTransform(scaleX: self.scale, y: self.scale)
self.layer.setAffineTransform(transform)
} else {
self.layer.shadowOpacity = 0.20;
self.layer.shadowRadius = 4.0;
self.layer.shadowOffset = CGSize(width: 1, height: 6);
self.scale = 1.0
let transform = CGAffineTransform(scaleX: self.scale, y: self.scale)
self.layer.setAffineTransform(transform)
}
}, completion: nil)
}
}
Collection also have these settings:
self.collectionView?.clipsToBounds = false
self.collectionView?.layer.masksToBounds = false
The problem is that the base view cell is not the right place to perform that kind of operation.
Try to use a custom container view instead of the base view cell.
I am using the code below to make the shadow for my ImageView
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:self.avatarImageView.bounds];
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
self.avatarImageView.layer.shadowOpacity = 0.8f;
self.avatarImageView.layer.shadowPath = shadowPath.CGPath;
It will drop a shadow in the right and bottom like this image.
Now I want to make my ImageView also have a shadow in top and left.
What should I change in code?
Is possible to make the view contains shadow in top,right,bottom,left by config in code only or I need to create other layout view for shadow? Any help would be great appreciated.
Here is what I want to achieve
Update
Thank #Dipen Panchasara for give a simple solution. Follow #Dipen Panchasara (with the shadow color is black) I will have the shadow image like this
Only following code will do the job for your requirement, You don't need to create UIBezierPath for shadow path.
// *** Set masks bounds to NO to display shadow visible ***
self.avatarImageView.layer.masksToBounds = NO;
// *** Set light gray color as shown in sample ***
self.avatarImageView.layer.shadowColor = [UIColor lightGrayColor].CGColor;
// *** *** Use following to add Shadow top, left ***
self.avatarImageView.layer.shadowOffset = CGSizeMake(-5.0f, -5.0f);
// *** Use following to add Shadow bottom, right ***
//self.avatarImageView.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
// *** Use following to add Shadow top, left, bottom, right ***
// avatarImageView.layer.shadowOffset = CGSizeZero;
// avatarImageView.layer.shadowRadius = 5.0f;
// *** Set shadowOpacity to full (1) ***
self.avatarImageView.layer.shadowOpacity = 1.0f;
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
Little less code for swift 3:
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.7
view.layer.shadowOffset = CGSize.zero
view.layer.shadowRadius = 4
view.layer.shadowPath = UIBezierPath(rect: planView.bounds).cgPath
Without using UIBezierPath, CGSize.zero is the key here
yourView.layer.masksToBounds = false
yourView?.layer.shadowColor = UIColor.red.cgColor
yourView?.layer.shadowOffset = CGSize.zero
yourView?.layer.shadowOpacity = 0.5
yourView?.layer.shadowRadius = 4
The best solution for shadow with a rounded corner on the same view and no need to do clipsToBounds or maskToBounds
func addShadow(cornerRadius: CGFloat, maskedCorners: CACornerMask, color: UIColor, offset: CGSize, opacity: Float, shadowRadius: CGFloat) {
self.layer.cornerRadius = cornerRadius
self.layer.maskedCorners = maskedCorners
self.layer.shadowColor = color.cgColor
self.layer.shadowOffset = offset
self.layer.shadowOpacity = opacity
self.layer.shadowRadius = shadowRadius
}
For UIView and Adding shadow, remember to set background color to the UIView.
If the background color is clearColor, no shadow appears.
If you are still not getting proper shadow, the problem might be the place you added the code.
You should call this in viewDidLayoutSubviews when you use UIBezierPath. If you call in ViewDidLoad, you may get wrong result since the views layout process might be unfinished.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//Shadow code here
}
//If you’ve tried this before, you know exactly what happens. The corners will be rounded, but the shadow will be missing. If you set masksToBounds to false, the shadow will appear, but the corners will not be rounded.
//to get Shadow with corner radius
Add super view for container view with clear color and apply shadow for super view ,Apply corner radius for container View. try it.
//view to apply shadow and corner radius
containerView.layer.cornerRadius = 3
containerView.clipsToBounds = true
//superview of container View for to apply shadow
shadowView.layer.shadowOpacity = 0.1
shadowView.layer.shadowRadius = 2.0
shadowView.layer.masksToBounds = false
shadowView.layer.shadowOffset = CGSize.zero
shadowView.layer.shadowColor = UIColor.Black.cgColor
shadowView.layer.shadowPath = UIBezierPath(roundedRect:containerView.bounds, cornerRadius: containerView.layer.cornerRadius).cgPath
shadowView.layer.shouldRasterize = true
CGRectInset(self.avatarImageView.bounds, -10.0, -10.0)
**in swift 4**
yourView.clipsToBounds = true
yourView.layer.cornerRadius = 20
yourView.layer.shadowPath = UIBezierPath(roundedRect: self.yourView.bounds,
cornerRadius: self.DeletConversation.layer.cornerRadius).cgPath
yourView.layer.shadowColor = UIColor(hexString: "color")?.cgColor
DeletConversation.layer.shadowOpacity = 1
DeletConversation.layer.shadowOffset = CGSize(width: 0, height: 1.0)
DeletConversation.layer.shadowRadius = 1
DeletConversation.layer.masksToBounds = false
There is a very detailed explanation about this here: https://www.hackingwithswift.com/example-code/uikit/how-to-add-a-shadow-to-a-uiview.
If someone is straggling with no top shadow in a collection view then this may help:
I know that this may be obvious for some people, but if you have a CollectionView with header and cell, make sure that you have space between the header and the cell, otherwise, the top shadow of the cell will be blocked by the header.
To add space, just use the insetsForSectionAt section.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
}
I am trying to add a drop shadow to views that are layered on top of one another, the views collapse allowing content in other views to be seen, in this vein i want to keep view.clipsToBounds ON so that when the views collapse their content is clipped.
This seems to have made it difficult for me to add a drop shadow to the layers as when i turn clipsToBounds ON the shadows are clipped also.
I have been trying to manipulate view.frame and view.bounds in order to add a drop shadow to the frame but allow the bounds to be large enough to encompass it, however I have had no luck with this.
Here is the code I am using to add a Shadow (this only works with clipsToBounds OFF as shown)
view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;
Here is a screenshot of the shadow being applied to the top lightest grey layer. Hopefully this gives an idea of how my content will overlap if clipsToBounds is OFF.
How can I add a shadow to my UIView and keep my content clipped?
Edit: Just wanted to add that I have also played around with using background images with shadows on, which does work well, however I would still like to know the best coded solution for this.
Try this:
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;
First of all: The UIBezierPath used as shadowPath is crucial. If you don't use it, you might not notice a difference at first, but the keen eye will observe a certain lag occurring during events like rotating the device and/or similar. It's an important performance tweak.
Regarding your issue specifically: The important line is view.layer.masksToBounds = NO. It disables the clipping of the view's layer's sublayers that extend further than the view's bounds.
For those wondering what the difference between masksToBounds (on the layer) and the view's own clipToBounds property is: There isn't really any. Toggling one will have an effect on the other. Just a different level of abstraction.
Swift 2.2:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowOffset = CGSizeMake(0.0, 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.CGPath
}
Swift 3:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.cgPath
}
Wasabii's answer in Swift 2.3:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath
And in Swift 3/4/5:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath
Put this code in layoutSubviews() if you're using AutoLayout.
In SwiftUI, this is all much easier:
Color.yellow // or whatever your view
.shadow(radius: 3)
.frame(width: 200, height: 100)
The trick is defining the masksToBounds property of your view's layer properly:
view.layer.masksToBounds = NO;
and it should work.
(Source)
You can create an extension for UIView to access these values in the design editor
extension UIView{
#IBInspectable var shadowOffset: CGSize{
get{
return self.layer.shadowOffset
}
set{
self.layer.shadowOffset = newValue
}
}
#IBInspectable var shadowColor: UIColor{
get{
return UIColor(cgColor: self.layer.shadowColor!)
}
set{
self.layer.shadowColor = newValue.cgColor
}
}
#IBInspectable var shadowRadius: CGFloat{
get{
return self.layer.shadowRadius
}
set{
self.layer.shadowRadius = newValue
}
}
#IBInspectable var shadowOpacity: Float{
get{
return self.layer.shadowOpacity
}
set{
self.layer.shadowOpacity = newValue
}
}
}
You can set shadow to your view from storyboard also
On viewWillLayoutSubviews:
override func viewWillLayoutSubviews() {
sampleView.layer.masksToBounds = false
sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
sampleView.layer.shadowOpacity = 1.0
}
Using Extension of UIView:
extension UIView {
func addDropShadowToView(targetView:UIView? ){
targetView!.layer.masksToBounds = false
targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
targetView!.layer.shadowOpacity = 1.0
}
}
Usage:
sampleView.addDropShadowToView(sampleView)
So yes, you should prefer the shadowPath property for performance, but also:
From the header file of CALayer.shadowPath
Specifying the path explicitly using this property will usually
* improve rendering performance, as will sharing the same path
* reference across multiple layers
A lesser known trick is sharing the same reference across multiple layers. Of course they have to use the same shape, but this is common with table/collection view cells.
I don't know why it gets faster if you share instances, i'm guessing it caches the rendering of the shadow and can reuse it for other instances in the view. I wonder if this is even faster with
With the following snippet, I'm adding a drop shadow effect to one my UIView. Which works pretty well. But as soon as I set the view's masksToBounds property to YES. The drop shadow effect isn't rendered any more.
self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;
Do you have any ideas on this?
Because shadow is an effect done outside the View, and that masksToBounds set to YES will tell the UIView not to draw anything that is outside itself.
If you want a roundedCorner view with shadow I suggest you do it with 2 views:
UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];
view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];
It's iOS 6 now, things might have changed. TheSquad's answer don't work for me until I managed to add one more line view2.layer.masksToBounds = NO;, otherwise shadow doesn't show. Although documentation says masksToBounds is NO by default, my code shows the opposite.
Here is how I make a rounded corner button with shadow, which is among the most commonly used code snippet in my app.
button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;
view.layer.masksToBounds = NO; // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath =
[UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
[view addSubview:button];
EDIT
If views need to be animated or scrolled, masksToBounds = YES tax performance significantly, which means animation will probably get stuttered. To get rounded corner and shadow AND smooth animation or scrolling, use following code instead:
button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;
[view addSubview:button];
Swift 3.0 version with StoryBoard
The same idea with #TheSquad. Create a new view under the actual view and add shadow to the lower view.
1. Create a view under the actual view
Drag a UIView to StoryBoard with same constraint as your target view. Check clip to bound for the target view. Also make sure the new view is listed before the target view so that the target view will cover the new view.
2. Now link the new view to your code add add shadow on it
This is just a sample. You can do whatever way you want here
shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3
shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true
This is the Swift 3 and IBDesignable version of the answer posted by #TheSquad.
I used the same concept while making changes in the storyboard file. First I moved my targetView (the one which requires corner radius and shadow) inside a new containerView. Then I added the following lines of code (Reference: https://stackoverflow.com/a/35372901/419192) to add some IBDesignable attributes for UIView Class:
#IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
#IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.cgColor
}
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
else {
return nil
}
}
}
/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
#IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (0, -3). Animatable. */
#IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
#IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
/* The corner radius of the view. */
#IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
}
get {
return layer.cornerRadius
}
}
After adding this code, I went back to the storyboard and on selecting my containerView I could now find a new set of attributes in the attributes inspector:
Other than adding values for these attributes as per my choice, I also added a corner radius to my targetView and set the masksToBounds property as true.
I hope this helps :)
I also had drastic performance issues with shadows and rounded corners. Instead of using the shadowPath part, I used the following lines which perfectly solved the performance hit:
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;
Here is one of the solutions:
#IBOutlet private weak var blockView: UIView! {
didSet {
blockView.backgroundColor = UIColor.white
blockView.layer.shadowColor = UIColor.black.cgColor
blockView.layer.shadowOpacity = 0.5
blockView.layer.shadowOffset = CGSize.zero
blockView.layer.cornerRadius = 10
}
}
#IBOutlet private weak var imageView: UIImageView! {
didSet {
imageView.layer.cornerRadius = 10
imageView.layer.masksToBounds = true
imageView.layer.shouldRasterize = true
}
}