I am having issue with constraints. I am using UIBezierPath and auto layout. I have done almost everything given here and still the issue is not rectified and that is why I am posting this question. I am using the following function for rounding certain corner of my view (which I have made using storyboard)
func addShadowAndCorner(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
let shadowLayer = CAShapeLayer()
let size = CGSize(width: cornerRadius, height: cornerRadius)
let cgPath = UIBezierPath(roundedRect: self.curvedView.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
shadowLayer.path = cgPath //2
shadowLayer.fillColor = fillColor.cgColor //3
shadowLayer.shadowColor = shadowColor.cgColor //4
shadowLayer.shadowPath = cgPath
shadowLayer.shadowOffset = offSet //5
shadowLayer.shadowOpacity = opacity
shadowLayer.shadowRadius = shadowRadius
self.curvedView.layer.addSublayer(shadowLayer)
}
Then I am calling this function as below
func configureView() {
self.view.backgroundColor = UIColor(named: "appBackgroundColor")
curvedView.backgroundColor = .clear
self.addShadowAndCorner(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)
}
I have called the above function inside "override func viewDidLayoutSubviews()". But I am not getting expected behaviours for constraints on different phone sizes
The code works well when running on iPhone 11 simulator, the screenshot is shared below.
But when running the same code on iPhone SE simulator it is not working as expected. The screenshot is shared below for iPhone SE
The constraints for curvedView in storyboard is.
Please help me here. Thanks in advance
CodePudding user response:
You should apply the shadow code in didLayoutSubviews. At that point your views know their frame sizes. Make sure you call super too.
Be careful not to re-add the shadow sublayer every time by just calling your addShadowAndCorner function from didLayoutSubviews.
CodePudding user response:
You'll have the most reliable (and flexible) results by creating a custom UIView
subclass.
Here's a quick example:
class MyCustomView: UIView {
// properties with default values
var shadowColor: UIColor = .darkGray
var offset: CGSize = .zero
var opacity: Float = 1.0
var shadowRadius: CGFloat = 0.0
var cornerRadius: CGFloat = 0.0
var corners: UIRectCorner = []
var fillColor: UIColor = .white
let shadowLayer = CAShapeLayer()
convenience init(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
self.init(frame: .zero)
self.shadowColor = shadowColor
self.offset = offSet
self.opacity = opacity
self.shadowRadius = shadowRadius
self.cornerRadius = cornerRadius
self.corners = corners
self.fillColor = fillColor
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
layer.addSublayer(shadowLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
let size = CGSize(width: cornerRadius, height: cornerRadius)
let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
shadowLayer.path = cgPath //2
shadowLayer.fillColor = fillColor.cgColor //3
shadowLayer.shadowColor = shadowColor.cgColor //4
shadowLayer.shadowPath = cgPath
shadowLayer.shadowOffset = offset //5
shadowLayer.shadowOpacity = opacity
shadowLayer.shadowRadius = shadowRadius
}
func configureView(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
self.shadowColor = shadowColor
self.offset = offSet
self.opacity = opacity
self.shadowRadius = shadowRadius
self.cornerRadius = cornerRadius
self.corners = corners
self.fillColor = fillColor
setNeedsLayout()
}
}
You can then add a UIView
in Storyboard, assign its Custom Class to MyCustomView
, connect it to an @IBOutlet
, and then in viewDidLoad()
:
class MyTestVC: UIViewController {
@IBOutlet var curvedView: MyCustomView!
override func viewDidLoad() {
super.viewDidLoad()
curvedView.backgroundColor = .clear
curvedView.configureView(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)
}
}
Now, it will auto-update itself when its frame changes... such as on different devices or on device rotation:
You can create it via code like this:
let curvedView = MyCustomView(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)
and, with very little effort, you can make it @IBDesignable
and configure your properties as @IBInspectable
... then you can visually see the result when you lay it out in Storyboard.
The only tricky property is the Corners, because there is no direct @IBInspectable
option for UIRectCorner
... so we can use Bool properties for each corner:
@IBDesignable
class MyCustomView: UIView {
// properties with default values
@IBInspectable var shadowColor: UIColor = .darkGray
@IBInspectable var offset: CGSize = .zero
@IBInspectable var opacity: Float = 1.0
@IBInspectable var shadowRadius: CGFloat = 0.0
@IBInspectable var cornerRadius: CGFloat = 0.0
@IBInspectable var topLeft: Bool = false
@IBInspectable var topRight: Bool = false
@IBInspectable var bottomLeft: Bool = false
@IBInspectable var bottomRight: Bool = false
@IBInspectable var fillColor: UIColor = .white
private let shadowLayer = CAShapeLayer()
convenience init(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
self.init(frame: .zero)
self.shadowColor = shadowColor
self.offset = offSet
self.opacity = opacity
self.shadowRadius = shadowRadius
self.cornerRadius = cornerRadius
self.fillColor = fillColor
self.topLeft = corners.contains(.topLeft)
self.topRight = corners.contains(.topRight)
self.bottomLeft = corners.contains(.bottomLeft)
self.bottomRight = corners.contains(.bottomRight)
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layer.addSublayer(shadowLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
backgroundColor = .clear
var corners: UIRectCorner = UIRectCorner()
if self.topLeft { corners.insert(.topLeft) }
if self.topRight { corners.insert(.topRight) }
if self.bottomLeft { corners.insert(.bottomLeft) }
if self.bottomRight { corners.insert(.bottomRight) }
let size = CGSize(width: cornerRadius, height: cornerRadius)
let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
shadowLayer.path = cgPath
shadowLayer.fillColor = self.fillColor.cgColor
shadowLayer.shadowColor = self.shadowColor.cgColor
shadowLayer.shadowPath = cgPath
shadowLayer.shadowOffset = self.offset
shadowLayer.shadowOpacity = self.opacity
shadowLayer.shadowRadius = self.shadowRadius
}
public func configureView(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
self.shadowColor = shadowColor
self.offset = offSet
self.opacity = opacity
self.shadowRadius = shadowRadius
self.cornerRadius = cornerRadius
self.fillColor = fillColor
self.topLeft = corners.contains(.topLeft)
self.topRight = corners.contains(.topRight)
self.bottomLeft = corners.contains(.bottomLeft)
self.bottomRight = corners.contains(.bottomRight)
setNeedsLayout()
}
}
Now, we get this in Storyboard: