UITableViewCell: rounded corners and shadow
This question comes at a good time! I literally JUST solved this same issue myself.
- Create a
UIView
(let's refer to it asmainBackground
) inside your cell's Content View. This will contain all of your cell's content. Position it and apply necessary constraints in the Storyboard. - Create another
UIView
. This one will be the one with the shadow (let's refer to it asshadowLayer
). Position it exactly as you didmainBackground
, but behind it, and apply the same constraints. Now you should be able to set the rounded corners and the shadows as follows:
cell.mainBackground.layer.cornerRadius = 8
cell.mainBackground.layer.masksToBounds = true
cell.shadowLayer.layer.masksToBounds = false
cell.shadowLayer.layer.shadowOffset = CGSizeMake(0, 0)
cell.shadowLayer.layer.shadowColor = UIColor.blackColor().CGColor
cell.shadowLayer.layer.shadowOpacity = 0.23
cell.shadowLayer.layer.shadowRadius = 4
However, the problem here is: calculating the shadow for every single cell is a slow task. You'll notice some serious lag when you scroll through your table. The best way to fix this is to define a UIBezierPath
for the shadow, then rasterize it. So you may want to do this:
cell.shadowLayer.layer.shadowPath = UIBezierPath(roundedRect: cell.shadowLayer.bounds, byRoundingCorners: .AllCorners, cornerRadii: CGSize(width: 8, height: 8)).CGPath
cell.shadowLayer.layer.shouldRasterize = true
cell.shadowLayer.layer.rasterizationScale = UIScreen.mainScreen().scale
But this creates a new problem! The shape of the UIBezierPath
depends on shadowLayer
's bounds, but the bounds are not properly set by the time cellForRowAtIndexPath
is called. So, you need to adjust the shadowPath
based on shadowLayer
's bounds. The best way to do this is to subclass UIView
, and add a property observer to the bounds property. Then set all the properties for the shadow in didSet
. Remember to change the class of your shadowLayer
in the storyboard to match your new subclass.
class ShadowView: UIView {
override var bounds: CGRect {
didSet {
setupShadow()
}
}
private func setupShadow() {
self.layer.cornerRadius = 8
self.layer.shadowOffset = CGSize(width: 0, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowOpacity = 0.3
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 8)).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
TableView rounded corners and shadow
You can add your table view to a container view and add drop shadow to that container view:
let containerView:UIView = UIView(frame:CGRect(x: 10, y: 100, width: 300, height: 400))
self.tableView = UITableView(frame: containerView.bounds), style: .Plain)
containerView.backgroundColor = UIColor.clearColor()
containerView.layer.shadowColor = UIColor.darkGrayColor().CGColor
containerView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
containerView.layer.shadowOpacity = 1.0
containerView.layer.shadowRadius = 2
// This is for rounded corners
self.tableView.layer.cornerRadius = 10
self.tableView.layer.masksToBounds = true
self.view.addSubview(containerView)
containerView.addSubview(self.tableView)
Edit
Swift 3.0:
let containerView:UIView = UIView(frame:CGRect(x: 10, y: 100, width: 300, height: 400))
self.tableView = UITableView(frame: containerView.bounds, style: .plain)
containerView.backgroundColor = UIColor.clear
containerView.layer.shadowColor = UIColor.darkGray.cgColor
containerView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
containerView.layer.shadowOpacity = 1.0
containerView.layer.shadowRadius = 2
self.tableView.layer.cornerRadius = 10
self.tableView.layer.masksToBounds = true
self.view.addSubview(containerView)
containerView.addSubview(self.tableView)
How to style tableview sections with shadow, padding and rounded corners?
It could be achieved by simply adding uitableview as a cell of uitableview and add the shadow and border to each cell.
Grouped table view with shadow
I messed around with this for ages, and eventually altered some Objc code to have your desired result.
I never had the will to make it work for single cells too. Always a few bugs. However for two or more cells, use this extension on the cell itself and not the contentView
.
Inside cells layoutSubviews
backgroundView = UIView(frame: contentView.frame)
backgroundView?.clipsToBounds = false
backgroundView?.backgroundColor = .clear
addShadowToCellInTableView(lastIndex: lastIndex, atIndexPath: indexPath)
public extension UITableViewCell {
/** adds a drop shadow to the background view of the (grouped) cell */
func addShadowToCellInTableView(lastIndex: Int, atIndexPath indexPath: IndexPath!) {
let isFirstRow: Bool = indexPath.row == 0
let isLastRow: Bool = (indexPath.row == lastIndex - 1)
guard let backgroundView = self.backgroundView else { return }
let backBounds = backgroundView.bounds
// the shadow rect determines the area in which the shadow gets drawn
var shadowRect: CGRect = backBounds.insetBy(dx: 0, dy: -10)
if isFirstRow {
shadowRect.origin.y += 10
} else if isLastRow {
shadowRect.size.height -= 10
}
// the mask rect ensures that the shadow doesn't bleed into other table cells
var maskRect: CGRect = backBounds.insetBy(dx: -20, dy: 0)
if isFirstRow {
maskRect.origin.y -= 10
maskRect.size.height += 10
} else if isLastRow {
maskRect.size.height += 10
}
// now configure the background view layer with the shadow
let layer: CALayer = backgroundView.layer
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 4
layer.shadowOpacity = 0.23
layer.shadowPath = UIBezierPath(rect: shadowRect).cgPath
layer.masksToBounds = false
// and finally add the shadow mask
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(rect: maskRect).cgPath
layer.mask = maskLayer
}
func addShadowToSingleCell() {
layer.shadowOpacity = 0.23
layer.shadowRadius = 4
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowColor = UIColor.black.cgColor
layer.shadowPath = UIBezierPath(roundedRect: contentView.frame,
cornerRadius: contentView.layer.cornerRadius).cgPath
}
}
Shadow does not work with round corner on top Tableview
Here is the method that will allow you to make the UIView
round with shadow:
extension UIView {
func addShadow(cornerRadius: CGFloat, maskedCorners: CACornerMask, color: UIColor, offset: CGSize, opacity: Float, shadowRadius: CGFloat) {
self.layer.cornerRadius = cornerRadius
self.layer.maskedCorners = maskedCorners
self.layer.shadowColor = color.cgColor
self.layer.shadowOffset = offset
self.layer.shadowOpacity = opacity
self.layer.shadowRadius = shadowRadius
}
}
Use:
For rounding all the corners:
<your_view_object>.addShadow(cornerRadius: 10.0, maskedCorners: [.layerMaxXMaxYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMinYCorner], color: AppColors.themeBlack.withAlphaComponent(0.6), offset: CGSize.zero, opacity: 0.4, shadowRadius: 4.0)
For rounding only top corners:
<your_view_object>.addShadow(cornerRadius: 10.0, maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner], color: AppColors.themeBlack.withAlphaComponent(0.6), offset: CGSize.zero, opacity: 0.4, shadowRadius: 4.0)
For rounding only bottom corners:
<your_view_object>.addShadow(cornerRadius: 10.0, maskedCorners: [.layerMaxXMaxYCorner, .layerMinXMaxYCorner], color: AppColors.themeBlack.withAlphaComponent(0.6), offset: CGSize.zero, opacity: 0.4, shadowRadius: 4.0)
For making the shadow as per you requirement change the others parameters.
Related Topics
iOS Avplayer Trigger Streaming Is Out of Buffer
What Happens with Constraints When a View Is Removed
Swift - Segmented Control - Switch Multiple Views
Popping and Pushing View Controllers in Same Action
Wait for Async Task to Finish Completion Block Before Returning in App Delegate
Presentviewcontroller and Displaying Navigation Bar
Color in Storyboard Not Matching Uicolor
How to Build .Ipa for React Native
Change the Color of iOS Navigation Bar
How to Detect Taps on Mkpolylines/Overlays Like Maps.App
iOS Certificate Pinning with Swift and Nsurlsession
Sprite Kit Create an Iadbanner
How to Use Git Properly with Xcode
Simplified Screen Capture: Record Video of Only What Appears Within the Layers of a Uiview