I have a login screen as below. Around each text field I have added a view and for that view I want to display a drop shadow. I kind of achieved what I was trying but this thing is not working for iPhone Plus (6+,8+) devices.
You can see the difference below.
iPhone 8+:-
iPhone 8:-
Here is my code
extension UIView {
func addShadow() {
layer.cornerRadius = 8
layer.masksToBounds = true
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: self.bounds,cornerRadius:8).cgPath
}
}
How I can fix this properly?
Since the views may be resized you should update your shadowPath after resizing because it has a fixed size. Unfortunately this can't be done in an extension, because you need to overwrite layoutSubview(). But you may call addShadow() from viewDidLayoutSubviews() from your view controller again for each text field.
You may also modify your extension to only update the path:
extension UIView {
func addShadow() {
layer.cornerRadius = 8
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
updateShadow()
}
func updateShadow() {
layer.shadowPath = UIBezierPath(roundedRect: self.bounds,cornerRadius:8).cgPath
}
}
With this you should call updateShadow() from viewDidLayoutSubviews() for each view with a shadow.
If you use a custom subclass for your text fields you may put the updateShadow() call into layoutSubviews(). So you need not to call it from the view controller.
You are building the view on iPhone 8 on storyboard. So when you run it on iPhone 8+/ 6+, view gets resized but shadow does not get updated.
Put layoutIfNeeded() before adding shadowPath to layer:
Updated code will look like:
func addShadow() {
layer.cornerRadius = 8
layer.masksToBounds = true
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layoutIfNeeded()
layer.shadowPath = UIBezierPath(roundedRect: self.bounds,cornerRadius: 8).cgPath
}
I'm sharing my approach that worked for me. I have used #clemens extension which is:
extension UIView {
func addShadow() {
layer.cornerRadius = 8
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
updateShadow()
}
func updateShadow() {
layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: 8).cgPath
}
}
However, it didn't work until this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Async is the key in this situation :)
DispatchQueue.main.async { [weak self] in
self?.someButton.addShadowNice()
self?.someButton.updateShadow()
}
}
Cheers.
Related
Before asking this question, I found so many answers at stack overflow but none worked for my case.
Requirement:
I tried:
private func setShadow() {
contentView.layer.masksToBounds = false
contentView.layer.shadowOffset = CGSize(width: 0, height: 1.5)
contentView.layer.shadowRadius = 0.5
contentView.layer.shadowOpacity = 0.5
layer.shadowColor = UIColor.lightGray.cgColor
}
I also play around with offset, radius, opacity; studied about each property, but none combination results in desired output. Any suggestions please.
You can achieve the effect by static layer.
contentView.layer.masksToBounds = false
let shadowLayer = CALayer()
shadowLayer.frame = contentView.bounds
shadowLayer.shadowOffset = CGSize(width: 0, height: 1.5);
shadowLayer.shadowRadius = 0.5
shadowLayer.shadowOpacity = 0.5;
shadowLayer.shadowColor = UIColor.lightGray.cgColor
contentView.layer.addSublayer(shadowLayer)
Please remember to input it in layoutSubviews if you use autolayout .
override func layoutSubviews() {
super.layoutSubviews()
shadowLayer.frame = contentView.bounds
}
override func awakeFromNib() {
super.awakeFromNib()
makeRoundedCorners()
cellBackGroundView.layer.shadowColor = UIColor.black.cgColor
cellBackGroundView.layer.shadowOffset = CGSize(width: 10, height: 10)
cellBackGroundView.layer.shadowOpacity = 1
cellBackGroundView.layer.shadowRadius = 4
cellBackGroundView.clipsToBounds = true
cellBackGroundView.layer.masksToBounds = true
}
I am trying above code but shadow is not showing, not getting what is the issue.
It is because you are applying rounded corner and shadow both to a single view.
You should apply the rounded corner to the first view, and shadow to another view which is the superview of the first view.
For example, If cellBackGroundView is the subView of cell. then You should apply the rounded corner to cellBackGroundView and the shadow to cell(Which is the superView of cellBackGroundView).
addShadowToView(view: cell, value: 3)
addCornerToView(view: cell.cellBackGroundView, value: 8)
Let me share my functions with you,
For Shadow,
func addShadowToView (view : UIView, value: CGFloat) {
view.layer.shadowColor = UIColor.lightGray.cgColor
view.layer.shadowOffset = CGSize(width: value, height: value)
view.layer.shadowOpacity = 1.0
view.layer.shadowRadius = 2
view.clipsToBounds = false
}
For Rounded Corner,
func addCornerToView (view : UIView, value: CGFloat) {
view.layer.cornerRadius = value
view.clipsToBounds = true
}
you can remove clipsToBounds = true attribute
self.shadowColor = color.cgColor
self.shadowOffset = CGSize(width: 0.0, height: 1.4)
self.shadowOpacity = opacity
self.shadowRadius = 0.0
self.masksToBounds = false
Here is what i use for shadows:
extension UIView {
func addShadow(color: UIColor, shadowRadius: CGFloat, opacity: Float) {
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = .zero
layer.shadowRadius = shadowRadius
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
}}
Usage:
view.addShadow(color: UIColor.black, shadowRadius: 4, opacity: 1)
You can try this one for collectionViewshadow
class CollectionViewCornerRound: UICollectionView {
var viewCornerRadius: CGFloat = 10 {
didSet {
layoutSubviews()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.shadowRadius = 2
layer.shadowOpacity = 0.3
layer.cornerRadius = viewCornerRadius
layer.masksToBounds = true
layer.shadowOffset = .zero
clipsToBounds = true
}
}
am adding a border to my view like this:
func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height, width: self.frame.size.width, height: width)
self.layer.addSublayer(border)
}
but strangley this works fine on Iphone 7 but in iphone 7 plus .. i will get this result:
as you can see there's a space at the beginning of the view's border.. why?
same thing is happening with view's shadow .. am doing it like this:
func dropShadow(scale: Bool = true) {
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize.zero
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
and getting space at the end of the view on iphone 7 plus
how to solve it?
it means your UI Is not updated , call in viewdidAppear or force to update the UI in mainthread on viewdidload using
DispatchQueue.main.async {
//call your addBottomBorderWithColor
}
Try calling the addBottomBorderWithColor in:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
The UI gets updated according to the constraints after ViewDidLoad.
Here's what I need to do to my two buttons.
and here's what I got right now.
What I'm doing:
- Set these up in IB inside a stackView.
- Add masks
- Add borders with width and color
- Add shadow.
The borders are being added but being cut-off as well by the masks.
Codes:
public func addCornerRadiusToCorners(
_ corners: UIRectCorner,
cornerRadius: CGFloat,
borderColor: UIColor,
borderWidth: CGFloat) {
// self.layer.masksToBounds = true
// self.clipsToBounds = true
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.cgColor
let size = CGSize(width: cornerRadius, height: cornerRadius)
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size)
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
public func addDefaultShadow() {
let shadowPath = UIBezierPath(rect: self.bounds)
self.layer.masksToBounds = false
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shadowOpacity = 0.5
self.layer.shadowPath = shadowPath.cgPath
}
Any idea how to achieve the result in the first photo?
EDIT: the border is being cut, the result was just got from the UI Debugger of Xcode. Sorry.
Add corner radius to you view on viewDidAppear
#IBOutlet weak var segmentView: UIView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
segmentView.layer.cornerRadius = segmentView.frame.size.height/2;
segmentView.layer.borderWidth = 1.0;
segmentView.clipsToBounds = true
segmentView.layer.borderColor = UIColor.lightGray.cgColor
segmentView.layer.shadowColor = UIColor.black.cgColor
segmentView.layer.shadowOpacity = 0.2
segmentView.layer.shadowRadius = 10.0
segmentView.layer.shadowOffset = CGSize(width: 1, height: 1)
segmentView.layer.masksToBounds = false
}
Sample Output:
you forgot to clip ur view.
self.clipsToBounds = true
you need to set maskstobounds to the view layer
segmentView.layer.masksToBounds = true
I want to achieve the following UI
And here is my code for it
class RoundTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
customize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customize()
}
func customize() {
apply(shadow: true)
}
func apply(shadow: Bool) {
if shadow {
borderStyle = .none
layer.cornerRadius = bounds.height / 2
layer.borderWidth = 1.0
layer.borderColor = UIColor.lightGray.cgColor
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 5.0)
layer.shadowRadius = 2
layer.masksToBounds = false
layer.shadowOpacity = 1.0
} else {
}
}
}
And here is the issue detail,
Case 1: For the above code I get a textField without any border or shadow,
Case 2: If I comment the first two lines, I get the shadow effect with the border is not rounded, and the border gets to default rounded border.
borderStyle = .none
layer.cornerRadius = bounds.height / 2
How could I resolve this issue ?
I can't reproduce the case 1 you encounter so I will post my code that seems achieve the UI you want.
class RoundTextField: UITextField {
override func layoutSubviews() {
super.layoutSubviews()
borderStyle = .none
layer.cornerRadius = bounds.height / 2
layer.borderWidth = 1.0
layer.borderColor = UIColor.init(colorLiteralRed: 241/256, green: 241/256, blue: 241/256, alpha: 1).cgColor
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2
layer.masksToBounds = false
layer.shadowOpacity = 1.0
// set backgroundColor in order to cover the shadow inside the bounds
layer.backgroundColor = UIColor.white.cgColor
}
}
This is the screenshot of my simulator:
You should set layer.masksToBounds to true.
Try calling these method from LayoutSubViews instead.
override func layoutSubviews() {
super.layoutSubviews()
// Call here
}