I'm attempting to create a method to configure buttons in an app. My method performs as expected, but the shadows don't draw. It does not crash.
I've created a UIButton extension with the following method:
func configureWhiteText(withBackgroundColor buttonColor: UIColor?) {
// basic configuration
tintColor = UIColor.white
setTitleColor(UIColor.white, for: .normal)
backgroundColor = buttonColor
// layer stuff
layer.drawsAsynchronously = true
layer.shouldRasterize = true
layer.cornerRadius = 10.0
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 5.0, height: 5.0)
layer.shadowRadius = 5.0
}
I've tried it within a UITableViewController in viewDidLoad and 'viewWillAppear` without luck:
myButton.configureWhiteText(withBackgroundColor: UIColor.blue)
The buttons are in grouped static cells of UITableViewController. Everything works within the configuration method except the shadow-oriented lines of code. layer.cornerRadius rounds the corner as expected, but none of the other layer-oriented lines draw a shadow.
I've verified layer.masksToBounds = false. I also tried commenting out layer.drawsAsynchronously and layer.shouldRasterize to no avail.
Any further ideas/suggestions are appreciated.
You left out the setting that actually turns on the shadow — the shadowOpacity. Add this line:
layer.shadowOpacity = 0.5
Badda bing badda boom.
You need to set the shadow path, assuming it is in an uibutton extension
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: 20).CGPath
For someone, like me, who might still face the same issue even if layer.shadowOpacity is set to a non-zero value. If your button background color is UIColor.clear, you should change it to other colors, e.g. white color.
Related
In figma, there's an effect you can apply to a view called a layer blur.
I cannot figure out how to replicate this sort of effect as its own view in Swift.
For example, say I want to add a view that goes under a button with a layer effect as so:
I have tried exporting the blur view from figma and using it as an image in code, but it just ends up like this:
These are the settings for the layer blur view I want to implement:
Any and all help would be greatly appreciated.
You can add drop shadow to a button this way.
Add an extension of UIView Like this.
extension UIView{
func dropShadow() {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.16
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3
}
}
Add drop shadow to your button by adding this line.
yourButton.dropShadow()
enter image description hereYou can add a shadow to your Button(View) for that purpose
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowRadius = 20
view.layer.shadowOpacity = 1
view.layer.shadowOffset = .zero
shadowOpacity: sets the transparency, where 0 will be without shadow, and 1 is the strongest.
shadowRadius: sets how wide the shadow is.
shadowOffset: sets the distance between the view and shadow.
I am using IGListKit with my UICollectionView. For now the CollectionView is pretty simple, as it only have one cell per section. That cell contains an inner horizontal UICollectionView as an image slideshow.
As I need some shadowing around my entire sections, I am using Decoration Views, and apply it a border shadow: layer.shadowPath
I noticed something weird, the shadow's opacity changes upon the picture currently displayed in the slideshow. If the picture (or a portion of the picture) is bright, you can see the shadow opacity changing.
I don't know if it is something I can fix.
You can clearly see that if I take a screenshot while swiping pictures in the slideshow, the shadow on the top is darker on one side.
Code for decoration view:
class FeedItemBackgroundShadowView: UICollectionReusableView {
// MARK: Initialization
... Constructors calling setup
// MARK: Setup
override func layoutSubviews() {
super.layoutSubviews()
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: Constants.cornerRadius).cgPath
}
func setup() {
self.layer.cornerRadius = 12.0
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.5)
self.layer.shadowRadius = 12.0
self.layer.shadowOpacity = 0.35
}
Rest of the code a simply UICollectionViewCells that embed a UICollectionView
Are you certain it isn't an optical illusion?
I am trying to have the shadow surround a View, but for some reason, the shadow only appears on the top and left side of the View, below is my code and view image. Can someone tell me what I did wrong and how should I change my code, so that the shadow will also appear on the right and bottom? Thank you.
override func awakeFromNib() {
layer.shadowOpacity = 1
layer.shadowRadius = 10
layer.shadowOffset = CGSize(width:1, height: 1)
layer.shadowColor = UIColor.black.cgColor
layer.shadowPath = CGPath(rect: bounds, transform: nil)
}
the view
I have created a tableviewcell in XIB and used it in tableview using registering the class. I am using autolayout here.
But the problem is when i loading the tableview in viewcontroller the shadow of the view inside the cell is not setting correctly. it exceeds its bounds as i shown in picture with red box.
But when is scrolling the tableview then the shadow of the view is shown correctly as expected.
The code used in cellForRowAtIndexPath is shown below:
let layer = cell.cellContectView.layer
layer.shadowOffset = CGSizeMake(1.0, 1.0)
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.2
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(rect: layer.bounds).CGPath
Please note: the blurred text is not an issue. Its hidden for security reason
You can try to remove the code for the layer from the cellForRowAtIndexPath and put it in your cell class in:
override func prepareForReuse() {
let layer = self.contentView.layer
layer.shadowOffset = CGSizeZero
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.2
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(rect: layer.bounds).CGPath
}
Adding a shadow inside UITableViewCell using QuartzCore code - can decrease scroll performance dramatically.
Better to create shadow graphic asset and add it as UIImageView.
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