UIView shadow not showing in iphone 5s - swift

I have a UIView and want to add shadow to it. So I get a code given below, which is working good in all iphone simulators and devices, but doesn't show shadow in iphone 5s and simulator as well.
class ShadowView: UIView {
override var bounds: CGRect {
didSet {
setupShadow()
}
}
private func setupShadow() {
self.layer.cornerRadius = 2
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shadowRadius = 4
self.layer.shadowOpacity = 0.5
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2, height: 2)).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}

I hope this helps you!!
import UIKit
class ShadowView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
func setupView() {
self.layer.cornerRadius = 5.0
self.layer.shadowOpacity = 1.0
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: -1, height: 1)
self.layer.masksToBounds = false
self.layer.shadowRadius = 3.0
self.layer.shouldRasterize = true
}
}

Related

make the uibutton rounded, shadow and gradient

can someone tell me please how to make the button rounded, shadow and gradient
here I set the gradient and shadow to the button, but without rounding:
#IBOutlet weak var info: UIButton!
info.setTitle("INFO", for: .normal)
info.setTwoGradients(colorOne: Colors.OrangeGrad, colorTwo: Colors.OrangeGradSec)
info.setTitleColor(UIColor.black, for: .normal)
info.layer.shadowColor = UIColor.darkGray.cgColor
info.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
info.layer.shadowOpacity = 0.8
info.layer.shadowRadius = 2
view.addSubview(info)
I know that the shadow disappears because of the rounding and I found a way to fix it, ex:
final class CustomButton: UIButton {
private var shadowLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 5.0, height: 2.0)
shadowLayer.shadowOpacity = 0.7
shadowLayer.shadowRadius = 2
layer.insertSublayer(shadowLayer, at: 0)
}
}
but there is a line:
shadowLayer.fillColor = UIColor.white.cgColor
because of which I can't set the gradient
therefore, I cannot find a way by which all three conditions would be met
Output:
Usage
class ViewController: UIViewController {
#IBOutlet var btnGradient: CustomButton!
override func viewDidLoad() {
super.viewDidLoad()
btnGradient.gradientColors = [.red, .green]
btnGradient.setTitle("Gradient Button", for: .normal)
btnGradient.setTitleColor(.white, for: .normal)
}
}
Custom Class
Use this class as a reference to setup the attributes:
class CustomButton: UIButton {
var gradientColors : [UIColor] = [] {
didSet {
setupView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
let startPoint = CGPoint(x: 0, y: 0.5)
let endPoint = CGPoint(x: 1, y: 0.5)
var btnConfig = UIButton.Configuration.plain()
btnConfig.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: layer.frame.height / 2, bottom: 5, trailing: layer.frame.height / 2)
self.configuration = btnConfig
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
//Gradient
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = gradientColors.map { $0.cgColor }
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
gradientLayer.cornerRadius = layer.frame.height / 2
if let oldValue = layer.sublayers?[0] as? CAGradientLayer {
layer.replaceSublayer(oldValue, with: gradientLayer)
} else {
layer.insertSublayer(gradientLayer, below: nil)
}
//Shadow
layer.shadowColor = UIColor.darkGray.cgColor
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.frame.height / 2).cgPath
layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.shadowOpacity = 0.7
layer.shadowRadius = 2.0
}
}
You can use UIButton Extension or refer code
Pass colors in array with start & end point of gradient effect, you want to start & end. i.e. x=0, y=0 means TopLeft & x=1, y=1 means BottomRight
extension UIButton {
func setGradientLayer(colorsInOrder colors: [CGColor], startPoint sPoint: CGPoint = CGPoint(x: 0, y: 0.5), endPoint ePoint: CGPoint = CGPoint(x: 1, y: 0.5)) {
let gLayer = CAGradientLayer()
gLayer.frame = self.bounds
gLayer.colors = colors
gLayer.startPoint = sPoint
gLayer.endPoint = ePoint
gLayer.cornerRadius = 5
gLayer.shadowOpacity = 0.8
gLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.insertSublayer(gLayer, at: 0)
}
}

Swift - Custom button class shadow size scaling not working

I've wrote this custom button class
class RoundedButton: UIButton {
override func awakeFromNib() {
layer.cornerRadius = 5
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
layer.shadowOpacity = 0.2
layer.shadowRadius = 1.0
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 5).cgPath
layer.contents = center
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
}
}
On my iPhone X everything perfectly fits
But when I'm using a device with smaller screen, the shadow don't scale correctly according to the button size.
Is there a problem with the button class itself or is it a constraints problem? I can't find a solution.
You need to update the shadow on layoutSubviews (this should also resolve issues your code seemed to have with rotation).
class RoundedButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
updateShadow(on: self)
}
func updateShadow(on background: UIView) {
let layer = background.layer
layer.shadowPath = UIBezierPath(rect: background.bounds).cgPath
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 0.0)
layer.shadowRadius = 4
layer.shadowOpacity = 0.22
}
override func awakeFromNib() {
layer.cornerRadius = 5
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
layer.shadowOpacity = 0.2
layer.shadowRadius = 1.0
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 5).cgPath
layer.contents = center
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
}
}
Hope this helps you

CornerRadius and Shadow on a UIView using #IBInspectable Swift 4.2

Is it possible to have both CornerRaduis and a shadow on a UIView?
I set up a Custom class for a UIView which uses #IBInspectable to set a cornerRadius and a addShadow which can be true or false. When I set the cornerRadius the shadow doesn't display, if I take away the cornerRadius then it displays again. Thanks in advance!
Custom class:
import UIKit
class CustomUIView: UIView {
override func awakeFromNib() {
self.layer.masksToBounds = cornerRadius > 0
}
#IBInspectable var useDefaultRadius: Bool = true {
didSet {
self.layer.masksToBounds = cornerRadius > 0
}
}
#IBInspectable var cornerRadius: CGFloat {
set {
self.layer.cornerRadius = newValue
}
get {
if (useDefaultRadius) {
// Set default radius
self.layer.cornerRadius = 23
}
return self.layer.cornerRadius
}
}
#IBInspectable var addShadow:Bool = true{
didSet(newValue) {
if(newValue == true){
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: 2, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
print("trying to use shadow")
}
}
}
}
Rather than creating custom clas and changing class of uiview everytime, i prefer extending it.
I'm achieving this by extending UIView as mentioned below:
extension UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
#IBInspectable
var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
#IBInspectable
var borderColor: UIColor? {
get {
let color = UIColor.init(cgColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = shadowRadius
}
}
#IBInspectable
var shadowOffset : CGSize{
get{
return layer.shadowOffset
}set{
layer.shadowOffset = newValue
}
}
#IBInspectable
var shadowColor : UIColor{
get{
return UIColor.init(cgColor: layer.shadowColor!)
}
set {
layer.shadowColor = newValue.cgColor
}
}
#IBInspectable
var shadowOpacity : Float {
get{
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
}
And set properties in storyboard as:
That's it.
Hope this helps.
Set true masksToBounds in addShadow:Bool or you don't need to set masksToBounds in addShadow:Bool didSet method
#IBInspectable var addShadow:Bool = true{
didSet(newValue) {
if(newValue == true){
//self.layer.masksToBounds = false
self.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: 2, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
print("trying to use shadow")
}
}
}
You can follow: https://medium.com/bytes-of-bits/swift-tips-adding-rounded-corners-and-shadows-to-a-uiview-691f67b83e4a
https://spin.atomicobject.com/2017/07/18/swift-interface-builder/
Here's the trick, you have to Set true to masksToBounds then Set false to masksToBounds before adding shadow code.
you can check this method to make things clear:
func addShadow(color: UIColor, bottomOrTop: Bool = false, radius: CGFloat = 2, height: CGFloat = 3) {
// These below line makes the trick to draw shadow with corner radius
self.layer.masksToBounds = true
self.layer.masksToBounds = false
self.layer.shadowOffset = CGSize(width: height, height: bottomOrTop ? height : height * -1)
self.layer.shadowRadius = radius
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = 0.3
}

How to set shadow outline (solid) around UILabel text in Swift?

I want to set shadow outline (solid) around UILabel text.
I tried below code: -
class BottomToolBarLabel: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
layer.shadowColor = UIColor.red.cgColor
layer.shadowOffset = CGSize(width: 5.0, height: 5.0)
layer.shadowOpacity = 1.0
layer.shadowRadius = 0.0
}
}
But this is not working as I required. Please suggest. Thanks in advance.
I required output like: -
Try using this code :-
class BottomToolBarLabel : UILabel{
override func drawText(in rect: CGRect) {
let c = UIGraphicsGetCurrentContext();
c!.setLineWidth(10);
c!.setLineJoin(.round);
c!.setTextDrawingMode(.stroke);
self.textColor = UIColor.white
super.drawText(in: rect)
c!.setTextDrawingMode(.fill);
self.textColor = UIColor.black
self.shadowOffset = CGSize.init(width: 0, height: 0)
super.drawText(in: rect)
}
}

Adding rounded corner and drop shadow to UICollectionViewCell

So I already went through various posts on adding 2nd view for adding shadow, but I still cannot get it to work if I want to add it in UICollectionViewCell. I subclassed UICollectionViewCell, and here is my code where I add various UI elements to the cell's content view and adding shadow to the layer:
[self.contentView setBackgroundColor:[UIColor whiteColor]];
self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(0, 1);
self.layer.shadowRadius = 1.0;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 0.5;
[self.layer setShadowPath:[[UIBezierPath bezierPathWithRect:self.bounds] CGPath]];
I would like to know how to add rounded corner and shadow to UICollectionViewCell.
Neither of these solutions worked for me. If you place all your subviews into the UICollectionViewCell content view, which you probably are, you can set the shadow on the cell's layer and the border on the contentView's layer to achieve both results.
cell.contentView.layer.cornerRadius = 2.0f;
cell.contentView.layer.borderWidth = 1.0f;
cell.contentView.layer.borderColor = [UIColor clearColor].CGColor;
cell.contentView.layer.masksToBounds = YES;
cell.layer.shadowColor = [UIColor blackColor].CGColor;
cell.layer.shadowOffset = CGSizeMake(0, 2.0f);
cell.layer.shadowRadius = 2.0f;
cell.layer.shadowOpacity = 0.5f;
cell.layer.masksToBounds = NO;
cell.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:cell.contentView.layer.cornerRadius].CGPath;
Swift 3.0
self.contentView.layer.cornerRadius = 2.0
self.contentView.layer.borderWidth = 1.0
self.contentView.layer.borderColor = UIColor.clear.cgColor
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 2.0)
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 0.5
self.layer.masksToBounds = false
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
Swift 3 version:
cell.contentView.layer.cornerRadius = 10
cell.contentView.layer.borderWidth = 1.0
cell.contentView.layer.borderColor = UIColor.clear.cgColor
cell.contentView.layer.masksToBounds = true
cell.layer.shadowColor = UIColor.gray.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 2.0)
cell.layer.shadowRadius = 2.0
cell.layer.shadowOpacity = 1.0
cell.layer.masksToBounds = false
cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).cgPath
In case it helps: Here is the swift for rounding the corners:
cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true
with cell being a variable controlling the cell: often, you will use this in override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
Enjoy!
Here's my answer, close to the others, but I add a corner radius to the layer otherwise the corners won't fill in correctly. Also, this makes a nice little extension on UICollectionViewCell.
extension UICollectionViewCell {
func shadowDecorate() {
let radius: CGFloat = 10
contentView.layer.cornerRadius = radius
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
layer.cornerRadius = radius
}
}
You can call it in collectionView(_:cellForItemAt:) of the datasource once you dequeue your cell.
Here the Swift 4 solution, updated to round every corners and not only the top corners:
contentView.layer.cornerRadius = 6.0
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2.0)
layer.shadowRadius = 6.0
layer.shadowOpacity = 1.0
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: contentView.layer.cornerRadius).cgPath
layer.backgroundColor = UIColor.clear.cgColor
Set the layer attributes for the cell, not contentView.
CALayer * layer = [cell layer];
[layer setShadowOffset:CGSizeMake(0, 2)];
[layer setShadowRadius:1.0];
[layer setShadowColor:[UIColor redColor].CGColor] ;
[layer setShadowOpacity:0.5];
[layer setShadowPath:[[UIBezierPath bezierPathWithRect:cell.bounds] CGPath]];
SWIFT 4.2
One should add this in your custom cell or cellForItemAt:
If you are using the cellForItemAt: approach substitute self -> cell
self.layer.cornerRadius = 10
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 2.0, height: 4.0)
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 1.0
self.layer.masksToBounds = false
This will give you a cell with a rounded border and a drop shadow.
You simply need to (a) set cornerRadius and (b) set shadowPath to be a rounded rect with the same radius as cornerRadius:
self.layer.cornerRadius = 10;
self.layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius] CGPath];
Here's my take on the solution. It's similar to other answers, with one key difference. It doesn't create a path that's dependent on the bounds of the view. Anytime you create a path based on the bounds and provide it to the layer you can run into issues when it's resizing, and need to setup methods to update the path.
A simpler solution is to avoid using anything that is bounds dependent.
let radius: CGFloat = 10
self.contentView.layer.cornerRadius = radius
// Always mask the inside view
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 1.0)
self.layer.shadowRadius = 3.0
self.layer.shadowOpacity = 0.5
// Never mask the shadow as it falls outside the view
self.layer.masksToBounds = false
// Matching the contentView radius here will keep the shadow
// in sync with the contentView's rounded shape
self.layer.cornerRadius = radius
Now when ever the cells size change the view API will do all the work internally.
I had to make some slight changes for Swift:
cell.contentView.layer.cornerRadius = 2.0;
cell.contentView.layer.borderWidth = 1.0;
cell.contentView.layer.borderColor = UIColor.clearColor().CGColor;
cell.contentView.layer.masksToBounds = true;
cell.layer.shadowColor = UIColor.grayColor().CGColor;
cell.layer.shadowOffset = CGSizeMake(0, 2.0);
cell.layer.shadowRadius = 2.0;
cell.layer.shadowOpacity = 1.0;
cell.layer.masksToBounds = false;
cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).CGPath;
I use the following method to accomplish this kind of effect:
extension UICollectionViewCell {
/// Call this method from `prepareForReuse`, because the cell needs to be already rendered (and have a size) in order for this to work
func shadowDecorate(radius: CGFloat = 8,
shadowColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3),
shadowOffset: CGSize = CGSize(width: 0, height: 1.0),
shadowRadius: CGFloat = 3,
shadowOpacity: Float = 1) {
contentView.layer.cornerRadius = radius
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = shadowOffset
layer.shadowRadius = shadowRadius
layer.shadowOpacity = shadowOpacity
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
layer.cornerRadius = radius
}
}
The answer of Mike Sabatini works fine, if you configure the cell properties directly on the collectionView cellForItemAt, but if you try to set them in awakeFromNib() of a custom UICollectionViewCell subclass, you will end with a wrong bezierPath set on the devices that doesn't match the width and height previously set in your Storyboard (IB).
Solution for me was create a func inside the subclass of UICollectionViewCell and calling it from the cellForItemAt like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as? CustomCollectionViewCell{
cell.configure())
return cell
}
else {
return UICollectionViewCell()
}
}
And on the CustomCollectionViewCell.swift :
class CustomCollectionViewCell: UICollectionViewCell{
func configure() {
contentView.layer.cornerRadius = 20
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: contentView.layer.cornerRadius).cgPath}
}
This one worked for me
cell.contentView.layer.cornerRadius = 5.0
cell.contentView.layer.borderColor = UIColor.gray.withAlphaComponent(0.5).cgColor
cell.contentView.layer.borderWidth = 0.5
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.darkGray.cgColor
border.frame = CGRect(x: 0, y: cell.contentView.frame.size.height - width, width: cell.contentView.frame.size.width, height: cell.contentView.frame.size.height)
border.borderWidth = width
cell.contentView.layer.addSublayer(border)
cell.contentView.layer.masksToBounds = true
cell.contentView.clipsToBounds = true
You can set shadow color, radius and offset in UICollectionViewDataSource method while creating a UICollectionViewCell
cell.layer.shadowColor = UIColor.gray.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 2.0)
cell.layer.shadowRadius = 1.0
cell.layer.shadowOpacity = 0.5
cell.layer.masksToBounds = false
I found an important thing: the UICollectionViewCell must have the backgroundColor as clear color in order to make these above work.
Swift 5, Xcode 13, iOS 14
First config your collection as below:
self.collectionView.clipsToBounds = false
Then config your cell same as below:
override func awakeFromNib() {
super.awakeFromNib()
self.configView()
}
private func configView() {
self.clipsToBounds = false
self.backgroundColor = .systemBackground
self.layer.cornerRadius = 10
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0.0)
self.layer.shadowRadius = 10
self.layer.shadowOpacity = 0.2
}
Note to those two "clipToBounds = false" commands.
Just that.