Reputation: 2533
Is there any way to add spacing between UITableViewCell
?
I have created a table and each cell only contain an image. The image is assigned to the cell like this:
cell.imageView.image = [myImages objectAtIndex:indexPath.row];
but this make the image enlarged and fit into the whole cell, and there are no spacing between the images.
Or lets say in this way, the height of image are e.g. 50, and I want to add 20 spacing between the images. Is there any way to accomplish this?
Upvotes: 251
Views: 401577
Reputation: 9
TableViewCell ->
override open var frame: CGRect {
get {
return super.frame
}
set {
var frame = newValue
frame.size.height -= 2
super.frame = frame
}
}
Upvotes: 0
Reputation: 131
Just adding to the pool of answers with what worked for me.
I’ve added a view (purple view) within the TableViewCell that I use as my cell’s content view. And constrain the purple view to have padding on top and bottom, or however you’d like, but I think this way creates a little more flexibility.
Upvotes: 1
Reputation: 890
indexPath.section
, instead of row
heightForHeader
and return your desired spacingUpvotes: 13
Reputation: 121
Read this after reading other people answers
I'd like to warn everyone who wants to use the solution like adding headers that will serve the purpose of spacing. If you do this, you will not be able to animate cells insertions, deletions, etc.. For example, you may get this kind of error if you use that method
Invalid update: invalid number of sections. The number of sections contained in the table view after the update (6) must be equal to the number of sections contained in the table view before the update (5), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
In case you need to animate insertions and deletions of rows I would go with adding this space in the cells itself. If you are concern about highlighting, then you can override method
func setHighlighted(_ highlighted: Bool, animated: Bool)
and set the highlighting yourself
Upvotes: 4
Reputation: 11
here is the actual solution
use sections instead of rows because every row has 1 section so you can give space to the header and the footer of the section here is the code just paste it in to your viewcontroller class where you create your tableview outlet
if you want top spacing just use heightForHeaderInSection if you want bottom spacing just use heightForFooterInSection as shown below
just copy and paste it
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let v = UIView()
v.backgroundColor = UIColor.clear
return v
}
func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
Upvotes: -3
Reputation: 1859
I override this function is subclass of UITableViewCell, and it works OK for me
override func layoutSubviews() {
super.layoutSubviews()
//set the values for top,left,bottom,right margins
let margins = UIEdgeInsets(top: 5, left: 8, bottom: 5, right: 8)
contentView.frame = contentView.frame.inset(by: margins)
contentView.layer.cornerRadius = 8
}
Upvotes: 19
Reputation: 199
Change the number of rows in section to 1 You have changed number of sections instead number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
Here you put spacing between rows
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
Upvotes: 8
Reputation: 71
You can simply use constraint in code like this :
class viewCell : UITableViewCell
{
@IBOutlet weak var container: UIView!
func setShape() {
self.container.backgroundColor = .blue
self.container.layer.cornerRadius = 20
container.translatesAutoresizingMaskIntoConstraints = false
self.container.widthAnchor.constraint(equalTo:contentView.widthAnchor , constant: -40).isActive = true
self.container.heightAnchor.constraint(equalTo: contentView.heightAnchor,constant: -20).isActive = true
self.container.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
self.container.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
}
it's important to add subview (container) and put other elements in it.
Upvotes: 1
Reputation: 143
Using a bunch of different sections is not needed. The other answers use frame insets and CGRect and layers and... BLAH. Not good; use auto layout and a custom UITableViewCell. In that UITableViewCell, instead of sub viewing your content inside the contentView, make a new containerView (a UIView), subview the container view inside the contentView, then subview all your views inside the container view.
To make the spacing now, simply edit the layout margins of the container view, like so:
class CustomTableViewCell: UITableViewCell {
let containerView = UIView()
let imageView = UIImageView()
required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder)}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
containerView.translatesAutoResizingMaskIntoConstraints = false
imageView.translatesAutoResizingMaskIntoConstraints = false
contentView.addSubview(containerView)
containerView.addSubview(imageView)
contentView.layoutMargins = UIEdgeInsets(top: 15, left: 3, bottom: 15, right: 3)
containerView.layoutMargins = UIEdgeInsets(top: 15, left: 17, bottom: 15, right: 17) // It isn't really necessary unless you've got an extremely complex table view cell. Otherwise, you could just write e.g. containerView.topAnchor
let cg = contentView.layoutMarginsGuide
let lg = containerView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: cg.topAnchor),
containerView.leadingAnchor.constraint(equalTo: cg.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: cg.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: cg.bottomAnchor),
imageView.topAnchor.constraint(equalTo: lg.topAnchor),
imageView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
imageView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
}
Upvotes: 2
Reputation: 129
If you don't want to change the section and row number of your table view (like I did), here's what you do:
1) Add an ImageView to the bottom of your table cell view.
2) Make it the same colour as the background colour of the table view.
I've done this in my application and it works perfectly. Cheers! :D
Upvotes: 2
Reputation: 358
Using the headers as spacing would work fine I guess if you don't want to use any headers. Otherwise, probably not the best idea. What I'm thinking is create a custom cell view.
Examples:
In the custom cell, make a background view with constraints so that it doesn't fill the entire cell, give it some padding.
Then, make the tableview background invisible and remove the separators:
// Make the background invisible
tableView.backgroundView = UIView()
tableView.backgroundColor = .clear
// Remove the separators
tableview.separatorStyle = .none
Upvotes: 2
Reputation: 8568
My easy solution using Swift :
// Inside UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
}
Upvotes: 237
Reputation: 268
I solved it like this way in Swift 4.
I create a extension of UITableViewCell and include this code:
override open var frame: CGRect {
get {
return super.frame
}
set (newFrame) {
var frame = newFrame
frame.origin.y += 10
frame.origin.x += 10
frame.size.height -= 15
frame.size.width -= 2 * 10
super.frame = frame
}
}
override open func awakeFromNib() {
super.awakeFromNib()
layer.cornerRadius = 15
layer.masksToBounds = false
}
I hope it helps you.
Upvotes: 12
Reputation: 2449
I was having trouble getting this to work alongside background colours and accessory views in the cell. Ended up having to:
1) Set the cells background view property with a UIView set with a background colour.
let view = UIView()
view.backgroundColor = UIColor.white
self.backgroundView = view
2) Re-position this view in layoutSubviews to add the idea of spacing
override func layoutSubviews() {
super.layoutSubviews()
backgroundView?.frame = backgroundView?.frame.inset(by: UIEdgeInsets(top: 2, left: 0, bottom: 0, right: 0)) ?? CGRect.zero
}
Upvotes: 1
Reputation: 2851
I was in the same boat. At first I tried switching to sections, but in my case it ended up being more of a headache than I originally thought, so I've been looking for an alternative. To keep using rows (and not mess with how you access your model data), here's what worked for me just by using a mask:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
let verticalPadding: CGFloat = 8
let maskLayer = CALayer()
maskLayer.cornerRadius = 10 //if you want round edges
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.frame = CGRect(x: cell.bounds.origin.x, y: cell.bounds.origin.y, width: cell.bounds.width, height: cell.bounds.height).insetBy(dx: 0, dy: verticalPadding/2)
cell.layer.mask = maskLayer
}
All you have left to do is make the cell's height bigger by the same value as your desired verticalPadding
, and then modify your inner layout so that any views that had spacing to the edges of the cell have that same spacing increased by verticalPadding/2
. Minor downside: you get verticalPadding/2
padding on both the top and bottom of the tableView, but you can quickly fix this by setting tableView.contentInset.bottom = -verticalPadding/2
and tableView.contentInset.top = -verticalPadding/2
. Hope this helps somebody!
Upvotes: 28
Reputation: 174
Based on Husam's answer: Using the cell layer instead of content view allows for adding a border around the entire cell and the accessory if need. This method requires careful adjustment of the bottom constraints of the cell as well as those insets otherwise the view will not proper.
@implementation TableViewCell
- (void)awakeFromNib {
...
}
- (void) layoutSubviews {
[super layoutSubviews];
CGRect newFrame = UIEdgeInsetsInsetRect(self.layer.frame, UIEdgeInsetsMake(4, 0, 4, 0));
self.layer.frame = newFrame;
}
@end
Upvotes: 4
Reputation: 1232
This article helped, it's pretty much what the other answers said but summarize and concise
https://medium.com/@andersongusmao/left-and-right-margins-on-uitableviewcell-595f0ba5f5e6
In it, he only applies them to left and right sides but the UIEdgeInsetsMake
init allows to add padding to all four points.
func UIEdgeInsetsMake(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> UIEdgeInsets
Description
Creates an edge inset for a button or view. An inset is a margin around a rectangle. Positive values represent margins closer to the center of the rectangle, while negative values represent margins further from the center.Parameters
top: The inset at the top of an object.
left: The inset on the left of an object
bottom: The inset on the bottom of an object.
right: The inset on the right of an object.Returns
An inset for a button or view
Note that UIEdgeInsets can also be used to achieve the same.
Xcode 9.3/Swift 4
Upvotes: 2
Reputation: 21
Check out my solution on GitHub with subclassing of UITableView
and using runtime features of Objective-C.
It basically uses Apple's private data structure UITableViewRowData
that I got searching private runtime header of UITableView
:
and here's desired private class that contains everything you need to layout your cells' spacings however you want without setting it in cells' classes:
Upvotes: 1
Reputation: 5063
Example in swift 3..
view controller code is bellow like
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
var arraytable = [[String:Any]]()
override func viewDidLoad() {
super.viewDidLoad()
arraytable = [
["title":"About Us","detail":"RA-InfoTech Ltd -A Joint Venture IT Company formed by Bank Asia Ltd"],
["title":"Contact","detail":"Bengal Center (4th & 6th Floor), 28, Topkhana Road, Dhaka - 1000, Bangladesh"]
]
tableView.delegate = self
tableView.dataSource = self
//For Auto Resize Table View Cell;
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableViewAutomaticDimension
//Detault Background clear
tableView.backgroundColor = UIColor.clear
}
func numberOfSections(in tableView: UITableView) -> Int { return arraytable.count }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// Set the spacing between sections
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
// Make the background color show through
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! as! CustomCell
cell.tv_title.text = arraytable[indexPath.section]["title"] as! String?
cell.tv_details.text = arraytable[indexPath.section]["detail"] as! String?
//label height dynamically increase
cell.tv_details.numberOfLines = 0
//For bottom border to tv_title;
let frame = cell.tv_title.frame
let bottomLayer = CALayer()
bottomLayer.frame = CGRect(x: 0, y: frame.height - 1, width: frame.width, height: 1)
bottomLayer.backgroundColor = UIColor.black.cgColor
cell.tv_title.layer.addSublayer(bottomLayer)
//borderColor,borderWidth, cornerRadius
cell.backgroundColor = UIColor.lightGray
cell.layer.borderColor = UIColor.red.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
cell.clipsToBounds = true
return cell
}
}
Download full source to Github : link
Upvotes: 6
Reputation: 63
Use UITableViewDelegate, heightForRowAtIndexPath
and return the height of row.
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100.0f ;
}
Upvotes: -4
Reputation: 512616
Updated for Swift 3
This answer is somewhat more general than the original question for the sake of future viewers. It is a supplemental example to the basic UITableView example for Swift.
Overview
The basic idea is to create a new section (rather than a new row) for each array item. The sections can then be spaced using the section header height.
How to do it
Set up your project as described in UITableView example for Swift. (That is, add a UITableView
and hook up the tableView
outlet to the View Controller).
In the Interface Builder, change the main view background color to light blue and the UITableView
background color to clear.
Replace the ViewController.swift code with the following.
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// These strings will be the data for the table view cells
let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
let cellReuseIdentifier = "cell"
let cellSpacingHeight: CGFloat = 5
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// These tasks can also be done in IB if you prefer.
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
tableView.delegate = self
tableView.dataSource = self
}
// MARK: - Table View delegate methods
func numberOfSections(in tableView: UITableView) -> Int {
return self.animals.count
}
// There is just one row in every section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// Set the spacing between sections
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return cellSpacingHeight
}
// Make the background color show through
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// note that indexPath.section is used rather than indexPath.row
cell.textLabel?.text = self.animals[indexPath.section]
// add border and color
cell.backgroundColor = UIColor.white
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
cell.clipsToBounds = true
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// note that indexPath.section is used rather than indexPath.row
print("You tapped cell number \(indexPath.section).")
}
}
Note that indexPath.section
is used rather than indexPath.row
in order to get the proper values for the array elements and tap positions.
How did you get the extra padding/space on the right and left?
I got it the same way you add spacing to any view. I used auto layout constraints. Just use the pin tool in the Interface Builder to add spacing for the leading and trailing constraints.
Upvotes: 195
Reputation: 165
Yes you can increase or decrease the spacing(padding) between two cell by creating one base view on content view in cell.Set clear colour for content view background and you can adjust the height of the base view to create space between cells.
Upvotes: 4
Reputation: 9975
I think this is the cleanest solution:
class MyTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
layoutMargins = UIEdgeInsetsMake(8, 0, 8, 0)
}
}
Upvotes: 2
Reputation: 553
I think the most straight forward solution if your just looking for a little space and probably least expensive would be to simply set the cell border color to your tables background color then set the border width to get desired result!
cell.layer.borderColor = blueColor.CGColor
cell.layer.borderWidth = 3
Upvotes: 12
Reputation: 8802
My situation was i used custom UIView to viewForHeader in section also heightForHeader in section return constant height say 40, issue was when there is no data all header views were touched to each other. so i wanted to space between the section in absent of data so i fixed by just changing "tableview style" plane to "Group".and it worked for me.
Upvotes: 1
Reputation: 1673
The way I achieve adding spacing between cells is to make numberOfSections = "Your array count" and make each section contains only one row. And then define headerView and its height.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return yourArry.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return cellSpacingHeight;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *v = [UIView new];
[v setBackgroundColor:[UIColor clearColor]];
return v;
}
Upvotes: 143
Reputation: 11284
I needed to do the same concept of having UITableCells have a "space" between them. Since you can't literally add space between cells you can fake it by manipulating the UITableView's cell height and then adding a UIView to the contentView of your cell. Here is a screen shot of a prototype I did in another test project when I was simulating this:
Here is some code (Note: there are lots of hard coded values for demonstration purposes)
First, I needed to set the heightForRowAtIndexPath
to allow for different heights on the UITableViewCell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text = [self.newsArray objectAtIndex:[indexPath row]];
if ([text isEqual:@"December 2012"])
{
return 25.0;
}
return 80.0;
}
Next, I want to manipulate the look and feel of the UITableViewCells so I do that in the willDisplayCell:(NewsUITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
method.
- (void)tableView:(UITableView *)tableView willDisplayCell:(NewsUITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (cell.IsMonth)
{
UIImageView *av = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 20, 20)];
av.backgroundColor = [UIColor clearColor];
av.opaque = NO;
av.image = [UIImage imageNamed:@"month-bar-bkgd.png"];
UILabel *monthTextLabel = [[UILabel alloc] init];
CGFloat font = 11.0f;
monthTextLabel.font = [BVFont HelveticaNeue:&font];
cell.backgroundView = av;
cell.textLabel.font = [BVFont HelveticaNeue:&font];
cell.textLabel.textColor = [BVFont WebGrey];
}
if (indexPath.row != 0)
{
cell.contentView.backgroundColor = [UIColor clearColor];
UIView *whiteRoundedCornerView = [[UIView alloc] initWithFrame:CGRectMake(10,10,300,70)];
whiteRoundedCornerView.backgroundColor = [UIColor whiteColor];
whiteRoundedCornerView.layer.masksToBounds = NO;
whiteRoundedCornerView.layer.cornerRadius = 3.0;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(-1, 1);
whiteRoundedCornerView.layer.shadowOpacity = 0.5;
[cell.contentView addSubview:whiteRoundedCornerView];
[cell.contentView sendSubviewToBack:whiteRoundedCornerView];
}
}
Note that I made my whiteRoundedCornerView height 70.0 and that's what causes the simulated space because the cell's height is actually 80.0 but my contentView is 70.0 which gives it the appearance.
There might be other ways of accomplishing this even better but it's just how I found how to do it. I hope it can help someone else.
Upvotes: 45
Reputation: 100748
If you are not using section headers (or footers) already, you can use them to add arbitrary spacing to table cells. Instead of having one section with n rows, create a table with n sections with one row each.
Implement the tableView:heightForHeaderInSection:
method to control the spacing.
You may also want to implement tableView:viewForHeaderInSection:
to control what the spacing looks like.
Upvotes: 8