Last active
August 24, 2023 21:10
-
-
Save jakehawken/93ec9a107a72f1b58ee8dfd19ff3d3a3 to your computer and use it in GitHub Desktop.
Shadows or rounded corners. To that I say ¿Porque no los dos?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension UIView { | |
/// Adds the subview to a container with shadows on it and returns the container. | |
/// Primary use case is for a subview that clips to its bounds, and therefore can't | |
/// have a shadow on its own `layer`. The shadow will match the corner radius of | |
/// the contained view. | |
static func shadowContainerFor( | |
_ subview: UIView, | |
shadowColor: UIColor? = .darkGray, | |
shadowRadius: CGFloat = 2, | |
shadowOffset: CGSize = CGSize(width: 0, height: 2), | |
shadowOpacity: Float = 1 | |
) -> UIView { | |
ShadowBox( | |
view: subview, | |
shadowColor: shadowColor, | |
shadowRadius: shadowRadius, | |
shadowOffset: shadowOffset, | |
shadowOpacity: shadowOpacity | |
) | |
} | |
/// Adds the view to a container with shadows on it and returns the container. | |
/// Primary use case is for a view that clips to its bounds, and therefore can't | |
/// have a shadow on its own `layer`. The shadow will match the corner radius of | |
/// the contained view. | |
func inShadowBox( | |
shadowColor: UIColor? = .darkGray, | |
shadowRadius: CGFloat = 2, | |
shadowOffset: CGSize = CGSize(width: 0, height: 2), | |
shadowOpacity: Float = 1 | |
) -> UIView { | |
ShadowBox( | |
view: self, | |
shadowColor: shadowColor, | |
shadowRadius: shadowRadius, | |
shadowOffset: shadowOffset, | |
shadowOpacity: shadowOpacity | |
) | |
} | |
private class ShadowBox: UIView { | |
private let mainView: UIView | |
required init( | |
view: UIView, | |
shadowColor: UIColor?, | |
shadowRadius: CGFloat, | |
shadowOffset: CGSize, | |
shadowOpacity: Float | |
) { | |
self.containedView = view | |
super.init(frame: .zero) | |
addSubviewAndPinToAllSides(containedView) | |
self.addShadow( | |
shadowColor: shadowColor, | |
shadowRadius: shadowRadius, | |
shadowOffset: shadowOffset, | |
shadowOpacity: shadowOpacity, | |
shadowPath: UIBezierPath( | |
roundedRect: self.bounds, | |
cornerRadius: containedView.cornerRadius * shadowRadius | |
).cgPath | |
) | |
} | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
layer.shadowPath = UIBezierPath( | |
roundedRect: self.bounds, | |
cornerRadius: containedView.cornerRadius * layer.shadowRadius | |
).cgPath | |
} | |
required init?(coder: NSCoder) { | |
fatalError( | |
"init(coder:) prohibited. Please use required init " | |
+= "init(view:shadowColor:shadowRadius:shadowOffset:shadowOpacity:) instead." | |
) | |
} | |
} | |
private func addSubviewAndPinToAllSides(_ subview: UIView) { | |
subview.translatesAutoresizingMaskIntoConstraints = false | |
addSubview(subview) | |
NSLayoutConstraint.activate([ | |
subview.topAnchor.constraint(equalTo: topAnchor), | |
subview.leadingAnchor.constraint(equalTo: leadingAnchor), | |
subview.trailingAnchor.constraint(equalTo: trailingAnchor), | |
subview.bottomAnchor.constraint(equalTo: bottomAnchor), | |
]) | |
setNeedsLayout() | |
} | |
private func addShadow( | |
shadowColor: UIColor? = .darkGray, | |
shadowRadius: CGFloat = 2, | |
shadowOffset: CGSize = CGSize(width: 0, height: 2), | |
shadowOpacity: Float = 1, | |
shadowPath: CGPath? = nil | |
) { | |
if let shadowPath { | |
layer.shadowPath = shadowPath | |
} | |
layer.shadowColor = shadowColor?.cgColor | |
layer.shadowRadius = shadowRadius | |
layer.shadowOffset = shadowOffset | |
layer.shadowOpacity = shadowOpacity | |
clipsToBounds = false | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment