Why am I unable to animate my UIView's shadow out? - ios

I want to animate out a shadow on my UIView, so that it starts visible, and by the end of the animation is invisible. I can do the opposite fine, but if I try to fade it out, the animation never occurs, in fact I never see a lick of shadow.
My code:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let foo = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
foo.backgroundColor = UIColor.greenColor()
applyShadowToView(foo)
view.addSubview(foo)
foo.layer.shadowOpacity = 1.0
let shadowAnimation = CABasicAnimation(keyPath: "shadowOpacity")
shadowAnimation.fromValue = 1.0
shadowAnimation.toValue = 0.0
shadowAnimation.duration = 0.5
shadowAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
foo.layer.addAnimation(shadowAnimation, forKey: "shadowOpacity")
foo.layer.shadowOpacity = 0.0
}
private func applyShadowToView(view: UIView) {
view.clipsToBounds = false
view.layer.shadowColor = UIColor(white: 0.4, alpha: 1.0).CGColor
view.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
view.layer.shadowRadius = 5.0
view.layer.shadowOpacity = 0.0
view.layer.shadowPath = UIBezierPath(rect: view.bounds).CGPath
view.layer.shouldRasterize = true
view.layer.rasterizationScale = UIScreen.mainScreen().scale
}
What am I doing wrong here exactly?

Related

Border Layer animation not Scaling from Center

I am struggling with the strange issue like when I am adding scale transformation to any layer, it doesn't animated from the center. It does from origin.
func addSquareBorderLayer() {
borderLayer = CAShapeLayer()
let rect = CGRect(x: spotlight!.frame.origin.x, y: spotlight!.frame.origin.y, width: spotlight!.frame.width, height: spotlight!.frame.height)
borderLayer?.path = UIBezierPath(roundedRect: rect, cornerRadius: spotlight!.cornerRadius).cgPath
borderLayer?.strokeColor = UIColor(red: 91/255, green: 186/255, blue: 162/255, alpha: 1).cgColor //ColorRGB(91, 186, 162)
borderLayer?.lineWidth = spotlight!.cornerRadius
borderLayer?.fillColor = UIColor.clear.cgColor
layer.addSublayer(borderLayer!)
animatePulsatingLayer(pulsatingLayer: layer)
}
private func animatePulsatingLayer(pulsatingLayer : CALayer) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.duration = 0.5
animation.repeatCount = .greatestFiniteMagnitude
animation.autoreverses = true
animation.fromValue = 1
animation.toValue = 1.05
pulsatingLayer.add(animation, forKey: "pulsing")
}
Can anybody please help?
Refernce to what's happening: https://www.dropbox.com/s/0hqdrrv310du7vz/mo.mov?dl=0
It's a bit tricky. You need to set the frame for the layer as the rect you are setting currently for path. And set path origin to zero so that the circle(or any shape) is drawn at the same position as frame's origin. This will allow the animation to be applied at the anchorPoint (0.5, 0.5).
Here is the complete example,
var borderLayer: CAShapeLayer!
func addSquareBorderLayer() {
let size: CGFloat = 100
borderLayer = CAShapeLayer()
let rect = CGRect(origin: .zero, size: CGSize(width: size, height: size))
borderLayer?.path = UIBezierPath(roundedRect: rect, cornerRadius: 50).cgPath
borderLayer?.strokeColor = UIColor(red: 91/255, green: 186/255, blue: 162/255, alpha: 1).cgColor
borderLayer?.lineWidth = 20
borderLayer?.fillColor = UIColor.clear.cgColor
borderLayer?.frame = CGRect(origin: CGPoint(x: 100, y: 200), size: rect.size)
self.view.layer.addSublayer(borderLayer!)
animatePulsatingLayer(pulsatingLayer: borderLayer!)
}
private func animatePulsatingLayer(pulsatingLayer : CALayer) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.duration = 0.3
animation.repeatCount = .greatestFiniteMagnitude
animation.autoreverses = true
animation.fromValue = 1
animation.toValue = 1.2
pulsatingLayer.add(animation, forKey: "pulsing")
}
Result

How do I add layer border with blurred colour?

I am using the following code:
bottomBorder.backgroundColor = UIColor.viewShadowGray().cgColor
bottomBorder.frame = CGRect(x: 0, y: view.frame.size.height - 1, width: view.frame.size.width, height: 1)
In extended UIColor file:
class func viewShadowGray() -> UIColor
{
return UIColor(red: 177.0/255.0, green: 177.0/255.0, blue: 179.0/255.0, alpha: 0.7)
}
changing the alpha value only makes the colour light, doesn't make it look blurred.
and getting :
I am expecting to obtain something as this:
How do I proceed rectifying this here?
edited , since you tried the shadow opacity thing in your new updated question i suggest using the implemented shadow properties for UIlayer
view.layer.masksToBounds = NO
view.layer.shadowOffset = CGSizeMake(-15, 20)
view.layer.shadowRadius = 5
view.layer.shadowOpacity = 0.5
view.layer.shadowColor = UIColor.black.cgColor
i would also recommend reading this article it would help you with that big time here
and check out this solution
extension UIView {
// OUTPUT 1
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
// OUTPUT 2
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offSet
layer.shadowRadius = radius
layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
original answer here
using them like this
view.layer.dropShadow()

My indicator is blank [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I use a custom indicator but when i call the subclass indicator in my viewdidload my view controller is blank but when i run it in a playground i can see it in the side window. Here is the code of the indicator. Theres no error but my indicator is not showing. Thats my problem. I would appreciate it if someone could tell me why.
import UIKit
class MaterialLoadingIndicator: UIView {
let MinStrokeLength: CGFloat = 0.05
let MaxStrokeLength: CGFloat = 0.7
let circleShapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
initShapeLayer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func initShapeLayer() {
circleShapeLayer.actions = ["strokeEnd" : NSNull(),
"strokeStart" : NSNull(),
"transform" : NSNull(),
"strokeColor" : NSNull()]
circleShapeLayer.backgroundColor = UIColor.clear.cgColor
circleShapeLayer.strokeColor = UIColor.blue.cgColor
circleShapeLayer.fillColor = UIColor.clear.cgColor
circleShapeLayer.lineWidth = 5
circleShapeLayer.lineCap = kCALineCapRound
circleShapeLayer.strokeStart = 0
circleShapeLayer.strokeEnd = MinStrokeLength
let center = CGPoint(x: bounds.width*0.5, y: bounds.height*0.5)
circleShapeLayer.frame = bounds
circleShapeLayer.path = UIBezierPath(arcCenter: center,
radius: center.x,
startAngle: 0,
endAngle: CGFloat(M_PI*2),
clockwise: true).cgPath
layer.addSublayer(circleShapeLayer)
}
func startAnimating() {
if layer.animation(forKey: "rotation") == nil {
startColorAnimation()
startStrokeAnimation()
startRotatingAnimation()
}
}
private func startColorAnimation() {
let color = CAKeyframeAnimation(keyPath: "strokeColor")
color.duration = 10.0
color.values = [UIColor(hex: 0x4285F4, alpha: 1.0).cgColor,
UIColor(hex: 0xDE3E35, alpha: 1.0).cgColor,
UIColor(hex: 0xF7C223, alpha: 1.0).cgColor,
UIColor(hex: 0x1B9A59, alpha: 1.0).cgColor,
UIColor(hex: 0x4285F4, alpha: 1.0).cgColor]
color.calculationMode = kCAAnimationPaced
color.repeatCount = Float.infinity
circleShapeLayer.add(color, forKey: "color")
}
private func startRotatingAnimation() {
let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = M_PI*2
rotation.duration = 2.2
rotation.isCumulative = true
rotation.isAdditive = true
rotation.repeatCount = Float.infinity
layer.add(rotation, forKey: "rotation")
}
private func startStrokeAnimation() {
let easeInOutSineTimingFunc = CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1.0)
let progress: CGFloat = MaxStrokeLength
let endFromValue: CGFloat = circleShapeLayer.strokeEnd
let endToValue: CGFloat = endFromValue + progress
let strokeEnd = CABasicAnimation(keyPath: "strokeEnd")
strokeEnd.fromValue = endFromValue
strokeEnd.toValue = endToValue
strokeEnd.duration = 0.5
strokeEnd.fillMode = kCAFillModeForwards
strokeEnd.timingFunction = easeInOutSineTimingFunc
strokeEnd.beginTime = 0.1
strokeEnd.isRemovedOnCompletion = false
let startFromValue: CGFloat = circleShapeLayer.strokeStart
let startToValue: CGFloat = fabs(endToValue - MinStrokeLength)
let strokeStart = CABasicAnimation(keyPath: "strokeStart")
strokeStart.fromValue = startFromValue
strokeStart.toValue = startToValue
strokeStart.duration = 0.4
strokeStart.fillMode = kCAFillModeForwards
strokeStart.timingFunction = easeInOutSineTimingFunc
strokeStart.beginTime = strokeEnd.beginTime + strokeEnd.duration + 0.2
strokeStart.isRemovedOnCompletion = false
let pathAnim = CAAnimationGroup()
pathAnim.animations = [strokeEnd, strokeStart]
pathAnim.duration = strokeStart.beginTime + strokeStart.duration
pathAnim.fillMode = kCAFillModeForwards
pathAnim.isRemovedOnCompletion = false
CATransaction.begin()
CATransaction.setCompletionBlock {
if self.circleShapeLayer.animation(forKey: "stroke") != nil {
self.circleShapeLayer.transform = CATransform3DRotate(self.circleShapeLayer.transform, CGFloat(M_PI*2) * progress, 0, 0, 1)
self.circleShapeLayer.removeAnimation(forKey: "stroke")
self.startStrokeAnimation()
}
}
circleShapeLayer.add(pathAnim, forKey: "stroke")
CATransaction.commit()
}
func stopAnimating() {
circleShapeLayer.removeAllAnimations()
layer.removeAllAnimations()
circleShapeLayer.transform = CATransform3DIdentity
layer.transform = CATransform3DIdentity
}
}
extension UIColor {
convenience init(hex: UInt, alpha: CGFloat) {
self.init(
red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
green: CGFloat((hex & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(hex & 0x0000FF) / 255.0,
alpha: CGFloat(alpha)
)
}
}
And here is the code of my view controller in the viewdidload
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
let indicator = MaterialLoadingIndicator(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
indicator.center = CGPoint(x: 320*0.5, y: 568*0.5)
view.addSubview(indicator)
indicator.startAnimating()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The view holding the indicator is just floating around, feeling lost, feeling unhappy for not belonging to, not being added to someone. :)
EDIT :
Okay John, now that we are stuck, let us add the indicator to someone.
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
let indicator = MaterialLoadingIndicator(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
indicator.center = CGPoint(x: 320*0.5, y: 568*0.5)
view.addSubview(indicator)
indicator.startAnimating()
self.view.addSubview(view) // John, this is what was missing
}

Swift - Color fill animation

I'm relatively new to ios animations and I believe there's something wrong with the approach I took to animate UIView.
I will start with a UI screenshot to picture my problem more precisely:
There is a tableView cell with two labels and colorful filled circle
Anytime I introduce new value to the cell, I'd like to animate this left-most bulb so it looks like it's getting filled with red color.
This is the implementation od BadgeView, which is basically the aforementioned leftmost filled circle
class BadgeView: UIView {
var coeff:CGFloat = 0.5
override func drawRect(rect: CGRect) {
let topRect:CGRect = CGRectMake(0, 0, rect.size.width, rect.size.height*(1.0 - self.coeff))
UIColor(red: 249.0/255.0, green: 163.0/255.0, blue: 123.0/255.0, alpha: 1.0).setFill()
UIRectFill(topRect)
let bottomRect:CGRect = CGRectMake(0, rect.size.height*(1-coeff), rect.size.width, rect.size.height*coeff)
UIColor(red: 252.0/255.0, green: 95.0/255.0, blue: 95.0/255.0, alpha: 1.0).setFill()
UIRectFill(bottomRect)
self.layer.cornerRadius = self.frame.height/2.0
self.layer.masksToBounds = true
}
}
This is the way I achieve uneven fill - I introduced coefficient which I modify in viewController.
Inside my cellForRowAtIndexPath method I try to animate this shape using custom button with callback
let btn:MGSwipeButton = MGSwipeButton(title: "", icon: img, backgroundColor: nil, insets: ins, callback: {
(sender: MGSwipeTableCell!) -> Bool in
print("Convenience callback for swipe buttons!")
UIView.animateWithDuration(4.0, animations:{ () -> Void in
cell.pageBadgeView.coeff = 1.0
let frame:CGRect = cell.pageBadgeView.frame
cell.pageBadgeView.drawRect(frame)
})
return true
})
But it does nothing but prints to console
: CGContextSetFillColorWithColor: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Although I'd love to know the right answer and approach, it would be great to know, for education purpose, why this code doesn't work.
Thanks in advance
The error part of the problem seems to be this part of the code:
cell.pageBadgeView.drawRect(frame)
From the Apple docs on UIView drawRect:
This method is called when a view is first displayed or when an event occurs that invalidates a visible part of the view. You should never call this method directly yourself. To invalidate part of your view, and thus cause that portion to be redrawn, call the setNeedsDisplay or setNeedsDisplayInRect: method instead.
So if you'd change your code to:
cell.pageBadgeView.setNeedsDisplay()
You'll get rid of the error and see the badgeView filled correctly. However this won't animate it, since drawRect isn't animatable by default.
The easiest workaround to your problem would be for BadgeView to have an internal view for the fill color. I'd refactor the BadgeView as so:
class BadgeView: UIView {
private let fillView = UIView(frame: .zero)
private var coeff:CGFloat = 0.5 {
didSet {
// Make sure the fillView frame is updated everytime the coeff changes
updateFillViewFrame()
}
}
// Only needed if view isn't created in xib or storyboard
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// Only needed if view isn't created in xib or storyboard
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
override func awakeFromNib() {
setupView()
}
private func setupView() {
// Setup the layer
layer.cornerRadius = bounds.height/2.0
layer.masksToBounds = true
// Setup the unfilled backgroundColor
backgroundColor = UIColor(red: 249.0/255.0, green: 163.0/255.0, blue: 123.0/255.0, alpha: 1.0)
// Setup filledView backgroundColor and add it as a subview
fillView.backgroundColor = UIColor(red: 252.0/255.0, green: 95.0/255.0, blue: 95.0/255.0, alpha: 1.0)
addSubview(fillView)
// Update fillView frame in case coeff already has a value
updateFillViewFrame()
}
private func updateFillViewFrame() {
fillView.frame = CGRect(x: 0, y: bounds.height*(1-coeff), width: bounds.width, height: bounds.height*coeff)
}
// Setter function to set the coeff animated. If setting it not animated isn't necessary at all, consider removing this func and animate updateFillViewFrame() in coeff didSet
func setCoeff(coeff: CGFloat, animated: Bool) {
if animated {
UIView.animate(withDuration: 4.0, animations:{ () -> Void in
self.coeff = coeff
})
} else {
self.coeff = coeff
}
}
}
In your button callback you'll just have to do:
cell.pageBadgeView.setCoeff(1.0, animated: true)
try this playground code
import UIKit
import CoreGraphics
var str = "Hello, playground"
class BadgeView: UIView {
var coeff:CGFloat = 0.5
func drawCircleInView(){
// Set up the shape of the circle
let size:CGSize = self.bounds.size;
let layer = CALayer();
layer.frame = self.bounds;
layer.backgroundColor = UIColor.blue().cgColor
let initialRect:CGRect = CGRect.init(x: 0, y: size.height, width: size.width, height: 0)
let finalRect:CGRect = CGRect.init(x: 0, y: size.height/2, width: size.width, height: size.height/2)
let sublayer = CALayer()
sublayer.frame = initialRect
sublayer.backgroundColor = UIColor.orange().cgColor
sublayer.opacity = 0.5
let mask:CAShapeLayer = CAShapeLayer()
mask.frame = self.bounds
mask.path = UIBezierPath(ovalIn: self.bounds).cgPath
mask.fillColor = UIColor.black().cgColor
mask.strokeColor = UIColor.yellow().cgColor
layer.addSublayer(sublayer)
layer.mask = mask
self.layer.addSublayer(layer)
let boundsAnim:CABasicAnimation = CABasicAnimation(keyPath: "bounds")
boundsAnim.toValue = NSValue.init(cgRect:finalRect)
let anim = CAAnimationGroup()
anim.animations = [boundsAnim]
anim.isRemovedOnCompletion = false
anim.duration = 3
anim.fillMode = kCAFillModeForwards
sublayer.add(anim, forKey: nil)
}
}
var badgeView:BadgeView = BadgeView(frame:CGRect.init(x: 50, y: 50, width: 50, height: 50))
var window:UIWindow = UIWindow(frame: CGRect.init(x: 0, y: 0, width: 200, height: 200))
window.backgroundColor = UIColor.red()
badgeView.backgroundColor = UIColor.green()
window.becomeKey()
window.makeKeyAndVisible()
window.addSubview(badgeView)
badgeView.drawCircleInView()
More Modification to Above code , anchor point code was missing in above code
```
var str = "Hello, playground"
class BadgeView: UIView {
var coeff:CGFloat = 0.7
func drawCircleInView(){
// Set up the shape of the circle
let size:CGSize = self.bounds.size;
let layerBackGround = CALayer();
layerBackGround.frame = self.bounds;
layerBackGround.backgroundColor = UIColor.blue.cgColor
self.layer.addSublayer(layerBackGround)
let initialRect:CGRect = CGRect.init(x: 0, y: size.height , width: size.width, height: 0)
let finalRect:CGRect = CGRect.init(x: 0, y: 0, width: size.width, height: size.height)
let sublayer = CALayer()
//sublayer.bounds = initialRect
sublayer.frame = initialRect
sublayer.anchorPoint = CGPoint(x: 0.5, y: 1)
sublayer.backgroundColor = UIColor.orange.cgColor
sublayer.opacity = 1
let mask:CAShapeLayer = CAShapeLayer()
mask.frame = self.bounds
mask.path = UIBezierPath(ovalIn: self.bounds).cgPath
mask.fillColor = UIColor.black.cgColor
mask.strokeColor = UIColor.yellow.cgColor
layerBackGround.addSublayer(sublayer)
layerBackGround.mask = mask
self.layer.addSublayer(layerBackGround)
let boundsAnim:CABasicAnimation = CABasicAnimation(keyPath: "bounds")
boundsAnim.toValue = NSValue.init(cgRect:finalRect)
let anim = CAAnimationGroup()
anim.animations = [boundsAnim]
anim.isRemovedOnCompletion = false
anim.duration = 1
anim.fillMode = kCAFillModeForwards
sublayer.add(anim, forKey: nil)
}

iOS add / remove shadow from a view

I do not understand how to remove a shadow that was added to a view.
I add to my view in initWithFrame a shadow in this way:
self.layer.borderWidth = 2;
self.layer.borderColor = [UIColor clearColor].CGColor;
self.backgroundColor = [UIColor greenColor];
[self.layer setCornerRadius:8.0f];
CALayer *layer = self.layer;
layer.shadowOffset = CGSizeMake(2, 2);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.cornerRadius = 8.0f;
layer.shadowRadius = 3.0f;
layer.shadowOpacity = 0.80f;
layer.shadowPath = [[UIBezierPath bezierPathWithRect:layer.bounds] CGPath];
After in the execution of the app I want to remove the shadow from this view. I've tried using:
layer.hidden = YES;
or
self.layer.hidden = YES;
but this hides the view completely, not just the added shadow.
Is there a way to retrieve the added shadow from a view and then hide it?
Thanks!
I guess you could use the shadowOpacity property of your CALayer.
So this should work:
self.layer.shadowOpacity = 0;
See the CALayer's shadowOpacity documentation page
And to show your shadow use:
self.layer.shadowOpacity = 1.0;
Sorry, not sure the correct way, but have you tried changing the properties of the layer shadow? For example, one of these;
layer.shadowOffset = CGSizeMake(0, 0);
layer.shadowColor = [[UIColor clearColor] CGColor];
layer.cornerRadius = 0.0f;
layer.shadowRadius = 0.0f;
layer.shadowOpacity = 0.00f;
Swift 4.2
I am using this in my code for labels and navigation bar.
extension UIView {
func shadow(_ height: Int = 5) {
self.layer.masksToBounds = false
self.layer.shadowRadius = 4
self.layer.shadowOpacity = 1
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 0 , height: height)
}
func removeShadow() {
self.layer.shadowOffset = CGSize(width: 0 , height: 0)
self.layer.shadowColor = UIColor.clear.cgColor
self.layer.cornerRadius = 0.0
self.layer.shadowRadius = 0.0
self.layer.shadowOpacity = 0.0
}
}
I tried the other answers, the only thing that worked for me was to toggle layer.masksToBounds to true/false
lazy var myView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.layer.cornerRadius = 5
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 3
view.layer.shadowOffset = .zero
view.layer.shadowRadius = 5
return view
}()
func showShadow() {
myView.layer.masksToBounds = false
}
func hideShadow() {
myView.layer.masksToBounds = true
}
the "layer" that you are trying to make hidden is the layer of the object that you are having a shadow to it's not a visible aspect.. only the objects with in the layer... it's rather confusing to conceptualize anyways, the only way to remove the shadow is to undo what you originally did, which was suggested above, there is no defined property that you can just toggle a bool and make the shadow go away
Swift 5.+
My solution was to add a shadowBackgroundView, which has a removable shadowLayer. In this way I could easyly remove the layer without resetting the shadow properties.
class ViewController: UIViewController {
private let shadowBackgroundView: UIView = {
let view = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
view.layer.masksToBounds = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(shadowBackgroundView)
// the view you want to add the shadow
let dummyView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
dummyView.backgroundColor = .red
shadowBackgroundView.addSubview(dummyView)
let addShadowButton = UIButton(frame: CGRect(x: 100, y: 300, width: 140, height: 50))
addShadowButton.backgroundColor = .blue
addShadowButton.setTitle("Add Shadow", for: .normal)
addShadowButton.addTarget(self, action: #selector(addShadow), for: .touchUpInside)
let removeShadowButton = UIButton(frame: CGRect(x: 100, y: 450, width: 140, height: 50))
removeShadowButton.backgroundColor = .blue
removeShadowButton.setTitle("Remove Shadow", for: .normal)
removeShadowButton.addTarget(self, action: #selector(removeShadow), for: .touchUpInside)
view.addSubview(addShadowButton)
view.addSubview(removeShadowButton)
}
#objc
func addShadow() {
let shadowLayer = CALayer()
shadowLayer.name = "ShadowLayer"
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOpacity = 1
shadowLayer.shadowOffset = .zero
shadowLayer.shadowRadius = 10
shadowLayer.shadowPath = UIBezierPath(rect: shadowBackgroundView.bounds).cgPath
// Otherwise the shadow will appear above the dummyView
shadowLayer.zPosition = -1
shadowBackgroundView.layer.addSublayer(shadowLayer)
}
#objc
func removeShadow() {
// Alternatively, you could also create the shadowLayer as a property, so you could call shadowLayer.removeFromSuperLayer()
shadowBackgroundView.layer.sublayers?.first { $0.name == "ShadowLayer" }?.removeFromSuperlayer()
}
}
As a Note, for the UITableViewCell, you wouldnt need to add a shadowBackgroundView, but you could add the shadowLayer directly to cell.view.layer, which serves as the backgroundView for the cell.contentView.
Swift 4.2
Just to add to the other answers, you could change the opacity through animation:
#IBAction func btnDidTap(_ sender: UICustomGestureRecognizer) {
guard let view = sender.view else {return}
if sender.state == .began {
UIView.animate(withDuration: 0.3,
animations: {
view.layer.shadowOpacity = 0
})
} else if sender.state == .ended {
UIView.animate(withDuration: 0.3,
animations: {
view.layer.shadowOpacity = 1
})
}
}

Resources