Uitableviewcell: Rounded Corners and Shadow

UITableViewCell: rounded corners and shadow

This question comes at a good time! I literally JUST solved this same issue myself.

  1. Create a UIView (let's refer to it as mainBackground) inside your cell's Content View. This will contain all of your cell's content. Position it and apply necessary constraints in the Storyboard.
  2. Create another UIView. This one will be the one with the shadow (let's refer to it as shadowLayer). Position it exactly as you did mainBackground, but behind it, and apply the same constraints.
  3. 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)

Sample Image

How to make an indented UITableViewCell with rounded corners and single line borders

Using a typical UITableView, this was achieved:

Sample Image

by adding these lines in viewDidLoad():

    myTableView.layer.borderColor = myTableView.separatorColor?.cgColor
myTableView.layer.borderWidth = 1.0
myTableView.layer.cornerRadius = 6.0

Edit: Another approach, accommodating the use of frame override to give the table "insets".

Add a CAShapeLayer as a sub-layer to the cell. Set its path to a UIBezierPath that forms the correct edges and corners.

Top cell will have only left, top and right edges (no bottom), with top corners rounded.

Middle cells will have left, top and right edges (no bottom), with NO corners rounded.

Bottom cell will have all 4 edges, with bottom corners rounded.

A cell in a one-row table will have all 4 edges, with all 4 corners rounded.

Result:

Sample Image

Complete code (no IBOutlets needed):

enum RoundedTableViewCellType {
case first
case last
case single
case middle
}

class RoundedTableViewCell: UITableViewCell {

var theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = UIFont.systemFont(ofSize: 16.0, weight: .bold)
v.textColor = UIColor(red: 62.0 / 255.0, green: 43.0 / 255.0, blue: 191.0 / 255.0, alpha: 1.0)
return v
}()

private var borderLayer = CAShapeLayer()
private var myType: RoundedTableViewCellType = .middle

override var frame: CGRect {
get {
return super.frame
}
set {
let inset: CGFloat = 20
var frame = newValue
frame.origin.x += inset
frame.size.width -= 2 * inset
super.frame = frame
}
}

var borderColor: UIColor = .clear {
didSet {
borderLayer.strokeColor = borderColor.cgColor
}
}

var borderWidth: CGFloat = 0.0 {
didSet {
borderLayer.lineWidth = borderWidth
}
}

// need to re-set layer cornerRadius if radius is set *after* type (in VC's cellForRowAt)
var radius: CGFloat = 6.0 {
didSet {
type = myType
}
}

var type: RoundedTableViewCellType = .middle {
didSet {
myType = type
switch type {
case .first:
layer.cornerRadius = radius
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
case .last:
layer.cornerRadius = radius
layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
case .single:
layer.cornerRadius = radius
layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner, .layerMaxXMinYCorner]
case .middle:
layer.cornerRadius = 0
layer.maskedCorners = []
}
}
}

override func layoutSubviews() {
super.layoutSubviews()

let r = radius
var bPath = UIBezierPath()

let ptTopLeft = CGPoint(x: 0.0, y: 0.0)
let ptTopRight = CGPoint(x: bounds.width, y: 0.0)
let ptBotRight = CGPoint(x: bounds.width, y: bounds.height)
let ptBotLeft = CGPoint(x: 0.0, y: bounds.height)

switch type {
case .first:
// top cell, add left, top and right edges
// round top corners
bPath.move(to: ptBotLeft)
bPath.addLine(to: CGPoint(x: ptTopLeft.x, y: ptTopLeft.y + r))

bPath.addQuadCurve(to: CGPoint(x: ptTopLeft.x + r, y: ptTopLeft.y),
controlPoint: ptTopLeft)

bPath.addLine(to: CGPoint(x: ptTopRight.x - r, y: ptTopRight.y))

bPath.addQuadCurve(to: CGPoint(x: ptTopRight.x, y: ptTopRight.y + r),
controlPoint: ptTopRight)

bPath.addLine(to: CGPoint(x: ptBotRight.x, y: ptBotRight.y))

case .last:
// bottom cell, add all four edges
// round bottom corners
bPath = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.bottomLeft, .bottomRight],
cornerRadii: CGSize(width: r, height: r))

case .single:
// one-row table, add all four edges
// round all four corners
bPath = UIBezierPath(roundedRect: bounds, cornerRadius: r)

case .middle:
// middle cell, add left, top, right edges
// round NO corners
bPath.move(to: ptBotLeft)
bPath.addLine(to: ptTopLeft)
bPath.addLine(to: ptTopRight)
bPath.addLine(to: ptBotRight)

}

borderLayer.path = bPath.cgPath

}

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

func commonInit() -> Void {

contentView.addSubview(theLabel)
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0),
theLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16.0),
theLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0),
theLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16.0),
])

layer.addSublayer(borderLayer)

borderLayer.fillColor = UIColor.clear.cgColor

// default values
borderColor = UIColor(red: 220.0 / 255.0, green: 215.0 / 255.0, blue: 244.0 / 255.0, alpha: 1.0)
borderWidth = 1.0

}

}

class RoundedCornersInsetTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

var myTableView: UITableView = {
let v = UITableView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()

var theData = [1, 2, 3, 4]

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = UIColor(red: 242.0 / 255.0, green: 240.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0)

myTableView.dataSource = self
myTableView.delegate = self

myTableView.register(RoundedTableViewCell.self, forCellReuseIdentifier: "RoundedTableViewCell")

myTableView.backgroundColor = .clear
myTableView.separatorStyle = .none

myTableView.tableFooterView = UIView(frame: CGRect.zero)

view.addSubview(myTableView)

NSLayoutConstraint.activate([

// constrain top + 40-pts
myTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),

// constrain leading / trailing to 0.0
myTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0),
myTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0),

// change this as appropriate
myTableView.heightAnchor.constraint(equalToConstant: 400.0)

])
}

func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "RoundedTableViewCell", for: indexPath) as! RoundedTableViewCell
cell.theLabel.text = "Cell \(theData[indexPath.row])"
cell.accessoryType = .disclosureIndicator

if theData.count == 1 {

cell.type = .single

} else {

if indexPath.row == 0 {
cell.type = .first
} else if indexPath.row == theData.count - 1 {
cell.type = .last
} else {
cell.type = .middle
}

}

// configurable cell border properties
//cell.borderColor = UIColor(red: 220.0 / 255.0, green: 215.0 / 255.0, blue: 244.0 / 255.0, alpha: 1.0)
//cell.borderWidth = 2.0
//cell.radius = 16.0

return cell
}

}

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.

UITableViewCell how to add solid border with rounded corners

I'm not sure how to do it with CALayer(). I Always make a cell with 2 UIViews. 1 "background view" behind the other. This will also create a border. This will have the same result as far as I'm aware. Could this be what you're looking for?

class TableViewCell: UITableViewCell {

var viewBackground = UIView()
var viewBackgroundBorder = UIView()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .clear
viewBackground.backgroundColor = UIColor.white
viewBackgroundBorder.backgroundColor = UIColor.red
viewBackgroundBorder.layer.cornerRadius = 10
viewBackground.layer.cornerRadius = 9

addSubview(viewBackgroundBorder)
addSubview(viewBackground)

viewBackgroundBorder.translatesAutoresizingMaskIntoConstraints = false
viewBackground.translatesAutoresizingMaskIntoConstraints = false

let constraints = [
viewBackgroundBorder.topAnchor.constraint(equalTo: topAnchor, constant: 0),
viewBackgroundBorder.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
viewBackgroundBorder.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
viewBackgroundBorder.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0),

viewBackground.topAnchor.constraint(equalTo: topAnchor, constant: 2),
viewBackground.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 2),
viewBackground.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -2),
viewBackground.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -2)]

NSLayoutConstraint.activate(constraints)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

How to convert tableview cell to rounded corner with shadow in iOS 9.0 or later

If you use normal table view cell

cell.contentView.layer.cornerRadius = 7.0f;
cell.contentView.layer.masksToBounds = YES;

If you use custom table view cell

cell.layer.cornerRadius = 7.0f;
cell.layer.masksToBounds = YES;

clear background color of UITableView in storyboard. Then you will be able to see the change.

For Cell Row Height

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44; //It default size.If you want to change to other size you can change.
}


Related Topics



Leave a reply



Submit