Multiple shadows under UIView iOS Swift - ios

I am trying to make a UIButton with rounded corners that has 2 colored shadows. Why is the red (and at this point also the blue "shadow" layer covering the button? How to get the shadows below the button canvas). I thought it was helping to insert sublayers instead of just adding them.
I have made a playground illustrating the issue
import UIKit
import PlaygroundSupport
This is the button I'm trying to implement
class PrimaryButton: UIButton {
required init(text: String = "Test 1", hasShadow: Bool = true) {
super.init(frame: .zero)
setTitle(text, for: .normal)
backgroundColor = UIColor.blue
layer.cornerRadius = 48 / 2
layer.masksToBounds = false
if hasShadow {
insertShadow()
}
}
fileprivate func insertShadow() {
let layer2 = CALayer(layer: layer), layer3 = CALayer(layer: layer)
layer2.applySketchShadow(color: UIColor.red, alpha: 0.5, x: 0, y: 15, blur: 35, spread: -10)
layer3.applySketchShadow(color: UIColor.blue, alpha: 0.5, x: 0, y: 10, blur: 21, spread: -9)
layer.insertSublayer(layer2, at: 0)
layer.insertSublayer(layer3, at: 0)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
layer.sublayers?.forEach { (sublayer) in
sublayer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
}
This is an extension that helps adding the shadow from Sketch specification:
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
}
masksToBounds = false
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let button = PrimaryButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 48)
view.addSubview(button)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

It seems legit to me. layer1 & layer2 are sublayers of the button layer.
You could add a third layer that will serve as a background. Here is an example based on your code:
class PrimaryButton: UIButton {
let layer1 = CALayer(), layer2 = CALayer(), layer3 = CALayer()
override func layoutSubviews() {
super.layoutSubviews()
layer1.backgroundColor = UIColor.blue.cgColor
layer1.cornerRadius = 48 / 2
[layer1, layer2, layer3].forEach {
$0.masksToBounds = false
$0.frame = layer.bounds
layer.insertSublayer($0, at: 0)
}
layer2.applySketchShadow(color: UIColor.red, alpha: 0.5, x: 0, y: 15, blur: 35, spread: -10)
layer3.applySketchShadow(color: UIColor.blue, alpha: 0.5, x: 0, y: 10, blur: 21, spread: -9)
}
}
Note that I put most of the code inside layoutSubviews because most of your methods use the actual bounds of the button.

Change your insertions to:
layer.insertSublayer(layer2, at: 1)
layer.insertSublayer(layer3, at: 2)
That should do it.

Another way is to add double buttons without change your class.
let button = PrimaryButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 48)
button.backgroundColor = UIColor.clear
view.addSubview(button)
self.view = view
let button1 = PrimaryButton()
button1.frame = CGRect(x: 0, y: 0, width: 200, height: 48)
button.addSubview(button1)
button1.layer.sublayers?.forEach{$0.removeFromSuperlayer()}

Related

How to skip an area (cutout) when drawing with core graphics

I want to draw some items but leave a true alpha transparency cutout for a circular area. What I want to achieve:
Yellow is example background to show bleed through.
The cutout width is actually wider than the arc stroke, so they don't fully intersect. I need true cutout because I a saving to an image with transparency.
I thought maybe I could use setBlendMode() but I believe that would only work if I wanted my cutout to be exactly the same width as the arc. But there is the gist of how I was trying to go about it:
A Swift workbook follows. Any tips on achieving this are greatly appreciated.
import Foundation
import UIKit
var dimen: CGFloat = 200.0;
var strokeWidth: CGFloat = 20.0;
var cutoutWidth: CGFloat = 30.0;
class DonutView : UIView
{
override func draw(_ rect: CGRect)
{
// cutout
let cutoutColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
cutoutColor.setFill()
let cutoutPath = UIBezierPath(ovalIn: CGRect(x: dimen-cutoutWidth, y: dimen/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))
cutoutPath.fill()
// let context = UIGraphicsGetCurrentContext()!
// context.setBlendMode(.sourceOut)
let ringOffset = cutoutWidth/2;
let circleWidth = dimen - ringOffset*2;
// ring
let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))
let ringColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
ringColor.setStroke()
ringPath.lineWidth = strokeWidth
ringPath.stroke()
// arc
let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
let arcPath = UIBezierPath()
arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)
let arcColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
arcColor.setStroke()
arcPath.lineWidth = strokeWidth
arcPath.stroke()
}
}
var view = DonutView(frame: CGRect.init(x: 0, y: 0, width: dimen, height: dimen))
view.backgroundColor = UIColor.yellow
// View these elements
view
(Edit: I should have stated this initially: this is to ultimately create a UIImage for WatchKit)
With help from How to clear circle in CGContext in iOS
import Foundation
import UIKit
import PlaygroundSupport
var dimen: CGFloat = 200.0;
var strokeWidth: CGFloat = 20.0;
var cutoutWidth: CGFloat = 30.0;
class DonutView : UIImageView
{
override init(frame: CGRect) {
super.init(frame: frame)
UIGraphicsBeginImageContextWithOptions(CGSize(width: dimen, height: dimen), false, 1)
let context = UIGraphicsGetCurrentContext()!
let ringOffset = cutoutWidth/2;
let circleWidth = dimen - ringOffset*2;
// ring
let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))
let ringColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
ringColor.setStroke()
ringPath.lineWidth = strokeWidth
ringPath.stroke()
// arc
let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
let arcPath = UIBezierPath()
arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)
let arcColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
arcColor.setStroke()
arcPath.lineWidth = strokeWidth
arcPath.stroke()
// Cutout circle
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.clear)
context.addEllipse(in: CGRect(x: dimen-cutoutWidth, y: dimen/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))
context.drawPath(using: .fill)
context.setBlendMode(.normal)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
var view = DonutView(frame: CGRect.init(x: 0, y: 0, width: dimen, height: dimen))
view.backgroundColor = UIColor.yellow
// View these elements
view
You can do this by using another CAShapeLayer as a mask.
The portion(s) of the mask layer that are alpha = 1.0 will be fully transparent.
So...
If we make the Arc Layer a sublayer of the Ring Layer, we can then apply the Cutout Layer as a mask, resulting in:
Here is source for a Playground page:
class MyDonutView : UIView
{
let ringLayer = CAShapeLayer()
let arcLayer = CAShapeLayer()
let cutoutLayer = CAShapeLayer()
var strokeWidth: CGFloat = 20.0;
var cutoutWidth: CGFloat = 30.0;
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// add arcLayer as a sublayer of ringLayer
ringLayer.addSublayer(arcLayer)
// add ringLayer as a sublayer of self.layer
layer.addSublayer(ringLayer)
// ring layer stroke is black at 0.3 alpha, fill is clear
ringLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.3).cgColor
ringLayer.fillColor = UIColor.clear.cgColor
ringLayer.lineWidth = strokeWidth
// arc layer stroke is black at 0.6 alpha, fill is clear
arcLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.6).cgColor
arcLayer.lineWidth = strokeWidth
arcLayer.fillColor = UIColor.clear.cgColor
// cutout layer stroke is black (although we're using Zero line width
// fill is black
cutoutLayer.strokeColor = UIColor.red.cgColor
cutoutLayer.lineWidth = 0
cutoutLayer.fillColor = UIColor.red.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
// define the "padding" around the ring
let ringOffset = cutoutWidth / 2.0
// define the diameter of the ring
let circleWidth = bounds.size.width - cutoutWidth;
// ring path
let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))
// arc path
let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
let arcPath = UIBezierPath()
arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)
// set ring layer path
ringLayer.path = ringPath.cgPath
// set arc layer path
arcLayer.path = arcPath.cgPath
// create a rect path the full size of bounds of self
let fullPath = UIBezierPath(rect: bounds)
// create a cutout path (the small circle to cut-out of the ring/arc)
let cutoutPath = UIBezierPath(ovalIn: CGRect(x: bounds.size.width-cutoutWidth, y: bounds.size.width/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))
// append the cutout path to the full rect path
fullPath.append(cutoutPath)
// even-odd winding rule
cutoutLayer.fillRule = CAShapeLayerFillRule.evenOdd
// set cutout layer path
cutoutLayer.path = fullPath.cgPath
// use cutout layer to mask ring layer
ringLayer.mask = cutoutLayer
}
}
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// instantiate a MyDonutView
let myDonutView = MyDonutView()
// we can set the stroke and cutout widths here
myDonutView.strokeWidth = 20.0
myDonutView.cutoutWidth = 30.0
// we're using auto-layout
myDonutView.translatesAutoresizingMaskIntoConstraints = false
// background color yellow to see the frame
//myDonutView.backgroundColor = .yellow
// otherwise, it should be clear
myDonutView.backgroundColor = .clear
// add as subview
view.addSubview(myDonutView)
// constrain centerX and centerY
// width = 200, height = width
NSLayoutConstraint.activate([
myDonutView.widthAnchor.constraint(equalToConstant: 200.0),
myDonutView.heightAnchor.constraint(equalTo: myDonutView.widthAnchor),
myDonutView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myDonutView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
let vc = TestViewController()
PlaygroundPage.current.liveView = vc

How to draw multiple horizontally circles in rectangle (UIButton or UIControl) Swift iOS

How to draw about three circle in horizontally area with main and ring color in rectangle. I need to create custom button with this circles, something like this:
Is there any good way to do this?
We can design such kind of views with UIStackView in very ease manner.
Take a stackView, set its alignment to center, axis to horizontal and distribution to fill. Create a UILabel/UIButton/UIImageView or even UIView and add rounded radius and border to it. Finally, add those views to the main stackView.
Try this.
override func viewDidLoad() {
super.viewDidLoad()
//Setup stackView
let myStackView = UIStackView()
myStackView.axis = .horizontal
myStackView.alignment = .center
myStackView.distribution = .fillEqually
myStackView.spacing = 8
view.addSubview(myStackView)
//Setup circles
let circle_1 = circleLabel()
let circle_2 = circleLabel()
let circle_3 = circleLabel()
myStackView.addArrangedSubview(circle_1)
myStackView.addArrangedSubview(circle_2)
myStackView.addArrangedSubview(circle_3)
myStackView.translatesAutoresizingMaskIntoConstraints = false
myStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0).isActive = true
myStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0).isActive = true
}
func circleLabel() -> UILabel {
let label = UILabel()
label.backgroundColor = UIColor.red
label.layer.cornerRadius = 12.5
label.layer.masksToBounds = true
label.layer.borderColor = UIColor.orange.cgColor
label.layer.borderWidth = 3.0
label.widthAnchor.constraint(equalToConstant: 25.0).isActive = true
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
return label
}
To make a Single Circle like that, you need to make use of UIBezierPath and CAShapeLayer .
let outerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(50), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let outerCircleShapeLayer = CAShapeLayer()
outerCircleShapeLayer.path = outerCirclePath.cgPath
outerCircleShapeLayer.fillColor = UIColor.white.cgColor
outerCircleShapeLayer.lineWidth = 3.0
view.layer.addSublayer(outerCircleShapeLayer)
// Drawing the inner circle
let innerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(40), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let innerCircleShapeLayer = CAShapeLayer()
innerCircleShapeLayer.path = innerCirclePath.cgPath
innerCircleShapeLayer.fillColor = UIColor.blue.cgColor
view.layer.addSublayer(innerCircleShapeLayer)
I have attached an image below for the Playground version of it .
Just play around with arcCenter and radius values and you will get the desired output
My team helped me and here is solution to create this with dynamically changing state of circles (with different stroke and fill colors):
import UIKit
#IBDesignable
class CirclesButton: UIControl {
#IBInspectable
var firstCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var secondCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var thirdCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
// get context
guard let context = UIGraphicsGetCurrentContext() else { return }
// make configurations
context.setLineWidth(1.0);
context.setStrokeColor(UIColor.white.cgColor)
context.setFillColor(red: 0.0, green: 0.58, blue: 1.0, alpha: 1.0)
// find view center
let dotSize:CGFloat = 11.0
let viewCenter = CGPoint(x: rect.midX, y: rect.midY)
// find personal dot rect
var dotRect = CGRect(x: viewCenter.x - dotSize / 2.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if secondCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find global notes rect
dotRect = CGRect(x: viewCenter.x - dotSize * 1.5 - 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if firstCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find music rect
dotRect = CGRect(x: viewCenter.x + dotSize / 2.0 + 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if thirdCircle {
context.setFillColor(red: 0.0, green: 1.0, blue: 0.04, alpha: 1.0)
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
}
}
It will looks like: CirclesButton
Сode:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let buttonSize: CGFloat = 80
let firstButton = CustomButton(position: CGPoint(x: 0, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(firstButton)
let secondButton = CustomButton(position: CGPoint(x: firstButton.frame.maxX, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(secondButton)
let thirdButton = CustomButton(position: CGPoint(x: secondButton.frame.maxX, y: 0), size: buttonSize, color: .green)
self.view.addSubview(thirdButton)
}
}
class CustomButton: UIButton {
init(position: CGPoint, size: CGFloat, color: UIColor) {
super.init(frame: CGRect(x: position.x, y: position.y, width: size, height: size))
self.backgroundColor = color
self.layer.cornerRadius = size / 2
self.clipsToBounds = true
self.layer.borderWidth = 4.0 // make it what ever you want
self.layer.borderColor = UIColor.white.cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
You can handle button tapped like:
override func viewDidLoad() {
super.viewDidLoad()
firstButton.addTarget(self, action: #selector(handleFirstButton), for: .touchUpInside)
}
#objc func handleFirstButton(sender: UIButton) {
print("first button tapped")
}
Best and Universal Solution for **Button or Label creation (Fully Dynamic)**
var x = 10
var y = 5
var buttonHeight = 40
var buttonWidth = 40
for i in 0..<3 {
let roundButton = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight))
roundButton.setTitle("Butt\(i)", for: .normal)
roundButton.layer.cornerRadius = roundButton.bounds.size.height/2
yourButtonBackView.addSubview(roundButton)
x = x + buttonWidth + 10
if x >= Int(yourButtonBackView.frame.width - 30) {
y = y + buttonHeight + 10
x = 10
}
}

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)
}

Custom progress view

I want to do a custom progress view for my iOS app, with 2 dots. Here is my code:
import UIKit
#IBDesignable
class StepProgressView: UIView {
#IBInspectable var progress: Float = 0
var progressColor = UIColor.blackColor()
var bgColor = UIColor.whiteColor()
override func layoutSubviews() {
self.backgroundColor = UIColor.clearColor()
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
let height = frame.height-8
let circle1 = UIView(frame: CGRect(x: frame.width*(1/3), y: 0, width: frame.height, height: frame.height))
circle1.backgroundColor = bgColor
circle1.layer.cornerRadius = frame.height/2
addSubview(circle1)
let circle2 = UIView(frame: CGRect(x: frame.width*(2/3), y: 0, width: frame.height, height: frame.height))
circle2.backgroundColor = bgColor
circle2.layer.cornerRadius = frame.height/2
addSubview(circle2)
let bgView = UIView(frame: CGRect(x: height/2, y: 4, width: frame.width-height/2, height: height))
bgView.backgroundColor = bgColor
bgView.layer.cornerRadius = height/2
addSubview(bgView)
let progressView = UIView(frame: CGRect(x: 0, y: 4, width: frame.width*CGFloat(progress), height: height))
progressView.backgroundColor = progressColor
progressView.layer.cornerRadius = height/2
addSubview(progressView)
}
}
The result:
However, as you can see, the circles aren't "filled" when the progression pass over one of them, and I don't know how to do that. I could create another view but I don't know how to handle the corner radius.
Can you help me ?
Thanks

Swift - Problems with corner radius and drop shadow

I'm trying to create a button with rounded corners and a drop shadow. No matter how I switch up, the button will not display correctly. I've tried masksToBounds = false and masksToBounds = true, but either the corner radius works and the shadow does not or the shadow works and the corner radius doesn't clip the corners of the button.
import UIKit
import QuartzCore
#IBDesignable
class Button : UIButton
{
#IBInspectable var masksToBounds: Bool = false {didSet{updateLayerProperties()}}
#IBInspectable var cornerRadius : CGFloat = 0 {didSet{updateLayerProperties()}}
#IBInspectable var borderWidth : CGFloat = 0 {didSet{updateLayerProperties()}}
#IBInspectable var borderColor : UIColor = UIColor.clearColor() {didSet{updateLayerProperties()}}
#IBInspectable var shadowColor : UIColor = UIColor.clearColor() {didSet{updateLayerProperties()}}
#IBInspectable var shadowOpacity: CGFloat = 0 {didSet{updateLayerProperties()}}
#IBInspectable var shadowRadius : CGFloat = 0 {didSet{updateLayerProperties()}}
#IBInspectable var shadowOffset : CGSize = CGSizeMake(0, 0) {didSet{updateLayerProperties()}}
override func drawRect(rect: CGRect)
{
updateLayerProperties()
}
func updateLayerProperties()
{
self.layer.masksToBounds = masksToBounds
self.layer.cornerRadius = cornerRadius
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.CGColor
self.layer.shadowColor = shadowColor.CGColor
self.layer.shadowOpacity = CFloat(shadowOpacity)
self.layer.shadowRadius = shadowRadius
self.layer.shadowOffset = shadowOffset
}
}
The following Swift 5 / iOS 12 code shows how to set a subclass of UIButton that allows to create instances with rounded corners and shadow around it:
import UIKit
final class CustomButton: UIButton {
private var shadowLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
shadowLayer.shadowOpacity = 0.8
shadowLayer.shadowRadius = 2
layer.insertSublayer(shadowLayer, at: 0)
//layer.insertSublayer(shadowLayer, below: nil) // also works
}
}
}
According to your needs, you may add a UIButton in your Storyboard and set its class to CustomButton or you may create an instance of CustomButton programmatically. The following UIViewController implementation shows how to create and use a CustomButton instance programmatically:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = CustomButton(type: .system)
button.setTitle("Button", for: .normal)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let widthConstraint = button.widthAnchor.constraint(equalToConstant: 100)
let heightConstraint = button.heightAnchor.constraint(equalToConstant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
}
The previous code produces the image below in the iPhone simulator:
My custom button with some shadow and rounded corners, I use it directly within the Storyboard with no need to touch it programmatically.
Swift 4
class RoundedButtonWithShadow: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
self.layer.masksToBounds = false
self.layer.cornerRadius = self.frame.height/2
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
self.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
self.layer.shadowOpacity = 0.5
self.layer.shadowRadius = 1.0
}
}
To expand on Imanou's post, it's possible to programmatically add the shadow layer in the custom button class
#IBDesignable class CustomButton: UIButton {
var shadowAdded: Bool = false
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = cornerRadius > 0
}
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
if shadowAdded { return }
shadowAdded = true
let shadowLayer = UIView(frame: self.frame)
shadowLayer.backgroundColor = UIColor.clearColor()
shadowLayer.layer.shadowColor = UIColor.darkGrayColor().CGColor
shadowLayer.layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: self.cornerRadius).CGPath
shadowLayer.layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
shadowLayer.layer.shadowOpacity = 0.5
shadowLayer.layer.shadowRadius = 1
shadowLayer.layer.masksToBounds = true
shadowLayer.clipsToBounds = false
self.superview?.addSubview(shadowLayer)
self.superview?.bringSubviewToFront(self)
}
}
An alternative way to get more usable and consistent button.
Swift 2:
func getImageWithColor(color: UIColor, size: CGSize, cornerRadius:CGFloat) -> UIImage {
let rect = CGRectMake(0, 0, size.width, size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 1)
UIBezierPath(
roundedRect: rect,
cornerRadius: cornerRadius
).addClip()
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
let button = UIButton(type: .Custom)
button.frame = CGRectMake(20, 20, 200, 50)
button.setTitle("My Button", forState: UIControlState.Normal)
button.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
self.addSubview(button)
let image = getImageWithColor(UIColor.whiteColor(), size: button.frame.size, cornerRadius: 5)
button.setBackgroundImage(image, forState: UIControlState.Normal)
button.layer.shadowRadius = 5
button.layer.shadowColor = UIColor.blackColor().CGColor
button.layer.shadowOpacity = 0.5
button.layer.shadowOffset = CGSizeMake(0, 1)
button.layer.masksToBounds = false
Swift 3:
func getImageWithColor(_ color: UIColor, size: CGSize, cornerRadius:CGFloat) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
let button = UIButton(type: .custom)
button.frame = CGRect(x:20, y:20, width:200, height:50)
button.setTitle("My Button", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
self.addSubview(button)
if let image = getImageWithColor(UIColor.white, size: button.frame.size, cornerRadius: 5) {
button.setBackgroundImage(image, for: .normal)
}
button.layer.shadowRadius = 5
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOpacity = 0.5
button.layer.shadowOffset = CGSize(width:0, height:1)
button.layer.masksToBounds = false
Swift 5 &
No need of "UIBezierPath"
view.layer.cornerRadius = 15
view.clipsToBounds = true
view.layer.masksToBounds = false
view.layer.shadowRadius = 7
view.layer.shadowOpacity = 0.6
view.layer.shadowOffset = CGSize(width: 0, height: 5)
view.layer.shadowColor = UIColor.red.cgColor
Refactored this to support any view. Subclass your view from this and it should have rounded corners. If you add something like a UIVisualEffectView as a subview to this view you likely need to use the same rounded corners on that UIVisualEffectView or it won't have rounded corners.
/// Inspiration: https://stackoverflow.com/a/25475536/129202
class ViewWithRoundedcornersAndShadow: UIView {
private var theShadowLayer: CAShapeLayer?
override func layoutSubviews() {
super.layoutSubviews()
if self.theShadowLayer == nil {
let rounding = CGFloat.init(22.0)
let shadowLayer = CAShapeLayer.init()
self.theShadowLayer = shadowLayer
shadowLayer.path = UIBezierPath.init(roundedRect: bounds, cornerRadius: rounding).cgPath
shadowLayer.fillColor = UIColor.clear.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowRadius = CGFloat.init(3.0)
shadowLayer.shadowOpacity = Float.init(0.2)
shadowLayer.shadowOffset = CGSize.init(width: 0.0, height: 4.0)
self.layer.insertSublayer(shadowLayer, at: 0)
}
}
}
Exact solution for 2020 syntax
import UIKit
class ColorAndShadowButton: UIButton {
override init(frame: CGRect) { super.init(frame: frame), common() }
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder), common() }
private func common() {
// UIButton is tricky: you MUST set the clear bg in bringup; NOT in layout
backgroundColor = .clear
clipsToBounds = false
layer.insertSublayer(colorAndShadow, below: layer)
}
lazy var colorAndShadow: CAShapeLayer = {
let s = CAShapeLayer()
// set your button color HERE (NOT on storyboard)
s.fillColor = UIColor.black.cgColor
// now set your shadow color/values
s.shadowColor = UIColor.red.cgColor
s.shadowOffset = CGSize(width: 0, height: 10)
s.shadowOpacity = 1
s.shadowRadius = 10
// now add the shadow
layer.insertSublayer(s, at: 0)
return s
}()
override func layoutSubviews() {
super.layoutSubviews()
// you MUST layout these two EVERY layout cycle:
colorAndShadow.frame = bounds
colorAndShadow.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath
}
}
Note that the very old top answer here is correct but has a critical error
Note that UIButton is unfortunately quite different from UIView in iOS.
Due to a strange behavior in iOS, you must set the background color (which of course must be clear in this case) in initialization, not in layout. You could just set it clear in storyboard (but you usually click it to be some solid color simply so you can see it when working in storyboard.)
In general combos of shadows/rounding are a real pain in iOS. Similar solutions:
https://stackoverflow.com/a/57465440/294884 - image + rounded + shadows
https://stackoverflow.com/a/41553784/294884 - two-corner problem
https://stackoverflow.com/a/59092828/294884 - "shadows + hole" or "glowbox" problem
https://stackoverflow.com/a/57400842/294884 - the "border AND gap" problem
https://stackoverflow.com/a/57514286/294884 - basic "adding" beziers
To improve PiterPan's answer and show a real shadow (not just a background with no blur) with a circular button in Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
myButton.layer.masksToBounds = false
myButton.layer.cornerRadius = myButton.frame.height/2
myButton.clipsToBounds = true
}
override func viewDidLayoutSubviews() {
addShadowForRoundedButton(view: self.view, button: myButton, opacity: 0.5)
}
func addShadowForRoundedButton(view: UIView, button: UIButton, opacity: Float = 1) {
let shadowView = UIView()
shadowView.backgroundColor = UIColor.black
shadowView.layer.opacity = opacity
shadowView.layer.shadowRadius = 5
shadowView.layer.shadowOpacity = 0.35
shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)
shadowView.layer.cornerRadius = button.bounds.size.width / 2
shadowView.frame = CGRect(origin: CGPoint(x: button.frame.origin.x, y: button.frame.origin.y), size: CGSize(width: button.bounds.width, height: button.bounds.height))
self.view.addSubview(shadowView)
view.bringSubview(toFront: button)
}
Corner Radius with Shadow
Short and simple way !!!!!
extension CALayer {
func applyCornerRadiusShadow(
color: UIColor = .black,
alpha: Float = 0.5,
x: CGFloat = 0,
y: CGFloat = 2,
blur: CGFloat = 4,
spread: CGFloat = 0,
cornerRadiusValue: CGFloat = 0)
{
cornerRadius = cornerRadiusValue
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
}
}
Use of code
btn.layer.applyCornerRadiusShadow(color: .black,
alpha: 0.38,
x: 0, y: 3,
blur: 10,
spread: 0,
cornerRadiusValue: 24)
No need maskToBound
Please verify clipsToBounds is false.
OUTPUT
Extension to drop shadow and corner radius
extension UIView {
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, shadowRadius: CGFloat = 1, scale: Bool = true, cornerRadius: CGFloat) {
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = color.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = offSet
shadowLayer.shadowOpacity = opacity
shadowLayer.shadowRadius = shadowRadius
layer.insertSublayer(shadowLayer, at: 0)
}
}
Here is the solution that will work!
extension UIView {
func applyShadowWithCornerRadius(color:UIColor, opacity:Float, radius: CGFloat, edge:AIEdge, shadowSpace:CGFloat) {
var sizeOffset:CGSize = CGSize.zero
switch edge {
case .Top:
sizeOffset = CGSize(width: 0, height: -shadowSpace)
case .Left:
sizeOffset = CGSize(width: -shadowSpace, height: 0)
case .Bottom:
sizeOffset = CGSize(width: 0, height: shadowSpace)
case .Right:
sizeOffset = CGSize(width: shadowSpace, height: 0)
case .Top_Left:
sizeOffset = CGSize(width: -shadowSpace, height: -shadowSpace)
case .Top_Right:
sizeOffset = CGSize(width: shadowSpace, height: -shadowSpace)
case .Bottom_Left:
sizeOffset = CGSize(width: -shadowSpace, height: shadowSpace)
case .Bottom_Right:
sizeOffset = CGSize(width: shadowSpace, height: shadowSpace)
case .All:
sizeOffset = CGSize(width: 0, height: 0)
case .None:
sizeOffset = CGSize.zero
}
self.layer.cornerRadius = self.frame.size.height / 2
self.layer.masksToBounds = true;
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = sizeOffset
self.layer.shadowRadius = radius
self.layer.masksToBounds = false
self.layer.shadowPath = UIBezierPath(roundedRect:self.bounds, cornerRadius:self.layer.cornerRadius).cgPath
}
}
enum AIEdge:Int {
case
Top,
Left,
Bottom,
Right,
Top_Left,
Top_Right,
Bottom_Left,
Bottom_Right,
All,
None
}
Finally, to apply shadow with corner radius call as per below:
viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)
Result Image
UPDATE: If you don't see the expected output then try calling the extension method from Main Thread, that will work for sure!
DispatchQueue.main.async {
viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)
}
UIButton Extension
Many people have proposed using a custom class of UIButton which is totally fine. Just in case you want an extension, like me, here's one. Written in Swift 5.
extension UIButton {
/// Adds a shadow to the button, with a corner radius
/// - Parameters:
/// - corner: The corner radius to apply to the shadow and button
/// - color: The color of the shaodw
/// - opacity: The opacity of the shadow
/// - offset: The offset of the shadow
/// - radius: The radius of the shadow
func addShadow(corner: CGFloat = 20, color: UIColor = .black, opacity: Float = 0.3, offset: CGSize = CGSize(width: 0, height: 5), radius: CGFloat = 5) {
let shadowLayer = CAShapeLayer()
layer.cornerRadius = corner
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: corner).cgPath
shadowLayer.fillColor = UIColor.clear.cgColor
shadowLayer.shadowColor = color.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = offset
shadowLayer.shadowOpacity = opacity
shadowLayer.shadowRadius = radius
layer.insertSublayer(shadowLayer, at: 0)
}
}
If somebody need add shadows to rounded buttons in Swift 3.0, here is a good method to do it.
func addShadowForRoundedButton(view: UIView, button: UIButton, shadowColor: UIColor, shadowOffset: CGSize, opacity: Float = 1) {
let shadowView = UIView()
shadowView.backgroundColor = shadowColor
shadowView.layer.opacity = opacity
shadowView.layer.cornerRadius = button.bounds.size.width / 2
shadowView.frame = CGRect(origin: CGPoint(x: button.frame.origin.x + shadowOffset.width, y: button.frame.origin.y + shadowOffset.height), size: CGSize(width: button.bouds.width, height: button.bounds.height))
self.view.addSubview(shadowView)
view.bringSubview(toFront: button)
}
Use this method in func viewDidLayoutSubviews() as bellow:
override func viewDidLayoutSubviews() {
addShadowForRoundedButton(view: self.view, button: button, shadowColor: .black, shadowOffset: CGSize(width: 2, height: 2), opacity: 0.5)
}
The effect of this method is:
You can create a protocol and conform it to you UIView, UIButton, Cell or whatever you want like that:
protocol RoundedShadowable: class {
var shadowLayer: CAShapeLayer? { get set }
var layer: CALayer { get }
var bounds: CGRect { get }
}
​
extension RoundedShadowable {
func applyShadowOnce(withCornerRadius cornerRadius: CGFloat, andFillColor fillColor: UIColor) {
if self.shadowLayer == nil {
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
shadowLayer.fillColor = fillColor.cgColor
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 0.0, height: 2.0)
shadowLayer.shadowOpacity = 0.2
shadowLayer.shadowRadius = 3
self.layer.insertSublayer(shadowLayer, at: 0)
self.shadowLayer = shadowLayer
}
}
}
​
class RoundShadowView: UIView, RoundedShadowable {
var shadowLayer: CAShapeLayer?
private let cornerRadius: CGFloat
private let fillColor: UIColor
init(cornerRadius: CGFloat, fillColor: UIColor) {
self.cornerRadius = cornerRadius
self.fillColor = fillColor
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.applyShadowOnce(withCornerRadius: self.cornerRadius, andFillColor: self.fillColor)
}
}
​
class RoundShadowButton: UIButton, RoundedShadowable {
var shadowLayer: CAShapeLayer?
private let cornerRadius: CGFloat
private let fillColor: UIColor
init(cornerRadius: CGFloat, fillColor: UIColor) {
self.cornerRadius = cornerRadius
self.fillColor = fillColor
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.applyShadowOnce(withCornerRadius: self.cornerRadius, andFillColor: self.fillColor)
}
}

Resources