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 make an indented UITableViewCell with rounded corners and single line borders
Using a typical UITableView
, this was achieved:
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:
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
What Is Preventing My Conversion from String to Int When Decoding Using Swift 4's Codable
How to Return Value from Async Block in Swift
How to Properly Use Queryorderedbyvalue
What Is the Correct Date.Format for Mmm Dd, Yyyy Hh:Mm:Ss A? and How Convert to Dd-Mm-Yyyy Hh:Ii
What Is the Use of "Static" Keyword If "Let" Keyword Used to Define Constants/Immutables in Swift
Does Swift Implement Tail Call Optimization? and in Mutual Recursion Case
Swift Struct Type Recursive Value
How to Clear the Terminal Screen in Swift
What Is the Swift Syntax " .Bar" Called
Swift Date(Byadding:To:) Returns Nil for Trivial Calculation in Repl
Initialize Class-Instance and Access Variables in Swift
Wkwebview Does Not Load Links to Pdfs
Alamofire Asynchronous Completionhandler For Json Request
How String Comparison Happens in Swift
Why Is Manually Setup Root View Controller Showing Black Screen