29

I am creating a ios app in swift and want to add spacing between cells like Facebook (pic bellow).

I am using a custom nib for the posts. I know to use UITableViewController. I figure I would use a separator style but it does not achieve the effect. I goggled around for hours and can't find a single tutorial in swift that makes sense! could some one explain how they did it in there app using swift? thanks!

enter image description here

Now2407
  • 469
  • 1
  • 5
  • 11
  • 1
    Have a wrapper view inside the cell's content view. Inset that with, like, 10px for all edges. Then put your content inside that view. Or use collection view. – yusuke024 Feb 19 '15 at 04:58
  • 1
    easy way is make your nib view height 10px more... with clear background – Sunny Shah Feb 19 '15 at 07:08
  • Possible duplicate of [How to add spacing between UITableViewCell](https://stackoverflow.com/questions/6216839/how-to-add-spacing-between-uitableviewcell) – AechoLiu Mar 21 '18 at 03:05

10 Answers10

35

This is my solution with result: (based on Jorge Casariego's answer)

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomApplicationCell

    cell.contentView.backgroundColor = UIColor.clear

    let whiteRoundedView : UIView = UIView(frame: CGRectMake(10, 8, self.view.frame.size.width - 20, 149))

    whiteRoundedView.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [1.0, 1.0, 1.0, 0.8])
    whiteRoundedView.layer.masksToBounds = false
    whiteRoundedView.layer.cornerRadius = 2.0
    whiteRoundedView.layer.shadowOffset = CGSizeMake(-1, 1)
    whiteRoundedView.layer.shadowOpacity = 0.2

    cell.contentView.addSubview(whiteRoundedView)
    cell.contentView.sendSubviewToBack(whiteRoundedView)

    return cell
}

table row height: 165 point

header section height, footer section height: 10 point

and result

enter image description here

Edit For Swift 3 Syntax:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomApplicationCell

    cell.contentView.backgroundColor = UIColor.clear

    let whiteRoundedView : UIView = UIView(frame: CGRect(x: 10, y: 8, width: self.view.frame.size.width - 20, height: 120))

    whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 1.0, 0.9])
    whiteRoundedView.layer.masksToBounds = false
    whiteRoundedView.layer.cornerRadius = 2.0
    whiteRoundedView.layer.shadowOffset = CGSize(width: -1, height: 1)
    whiteRoundedView.layer.shadowOpacity = 0.2

    cell.contentView.addSubview(whiteRoundedView)
    cell.contentView.sendSubview(toBack: whiteRoundedView)

    return cell
}

Simplest way for creating material card view:

github.com/SwiftCardView

Create CardView.swift

@IBDesignable
class CardView: UIView {

    @IBInspectable var cornerRadius: CGFloat = 2

    @IBInspectable var shadowOffsetWidth: Int = 0
    @IBInspectable var shadowOffsetHeight: Int = 3
    @IBInspectable var shadowColor: UIColor? = .black
    @IBInspectable var shadowOpacity: Float = 0.5

    override func layoutSubviews() {
        layer.cornerRadius = cornerRadius
        let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)

        layer.masksToBounds = false
        layer.shadowColor = shadowColor?.cgColor
        layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
        layer.shadowOpacity = shadowOpacity
        layer.shadowPath = shadowPath.cgPath
    }
}

Now just add CardView class to your UIView.

ibrahimyilmaz
  • 2,317
  • 1
  • 24
  • 28
27

With Swift 2 you can do spacing between UITableViewCells in this way:

In your TableViewController copy this:

    // In this case I returning 140.0. You can change this value depending of your cell
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 140.0
    }

    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

        cell.contentView.backgroundColor = UIColor.clearColor()

        let whiteRoundedView : UIView = UIView(frame: CGRectMake(0, 10, self.view.frame.size.width, 120))

        whiteRoundedView.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [1.0, 1.0, 1.0, 1.0])
        whiteRoundedView.layer.masksToBounds = false
        whiteRoundedView.layer.cornerRadius = 2.0
        whiteRoundedView.layer.shadowOffset = CGSizeMake(-1, 1)
        whiteRoundedView.layer.shadowOpacity = 0.2

        cell.contentView.addSubview(whiteRoundedView)
        cell.contentView.sendSubviewToBack(whiteRoundedView)
    }

This is the result:

enter image description here

Jorge Casariego
  • 21,948
  • 6
  • 90
  • 97
  • cell.contentView.addSubview(whiteRoundedView) and cell.contentView.sendSubviewToBack(whiteRoundedView) are called each time you display cell. You do not remove them and spam shadows and views this way – Vivienne Fosh Jun 04 '16 at 20:59
14

I spent at least an hour researching the topic. Finally, I came up win an idea to use a transparent border:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "faqCell", for: indexPath)

    // ...

    cell.layer.borderWidth = CGFloat(TABLE_CELLSPACING)
    cell.layer.borderColor = tableView.backgroundColor?.cgColor

    return cell
}

This works perfectly in Swift 3/Xcode 8.

Image: This is how it looks like on my side

Nick Ivanov
  • 754
  • 7
  • 7
  • few hours and finally found you.. T-H-A-N-K Y-O-U – deevee Jan 22 '19 at 15:41
  • It's the most convenient solution I've found so far. However it's interesting that it doesn't work for clear color option. Does anyone know the reason of it? – Onur Tuna Feb 13 '19 at 08:07
  • It's a good thought, but it ended up clipping the text for me, so I couldn't use it. – Dymas May 01 '19 at 21:19
6

code for Swift 4

override var frame: CGRect {
        get {
            return super.frame
        }
        set (newFrame) {
            var frame =  newFrame
            frame.origin.y += 4
            frame.size.height -= 2 * 5
            super.frame = frame
        }
    }
3

If you want to have right / legal spacing, not a trick, you can try UICollectionView instead of UITableView.

UICollectionView is more generic than UITableView, and you can custom almost thing on it.

Huy Le
  • 2,503
  • 1
  • 15
  • 15
  • Agree ^_^. Also we can put a UIView inside a cell as a space between two cells. – Twitter khuong291 Jan 19 '16 at 15:19
  • 1
    Although using Collection View is good solution for this problem, keep in mind that you do not have the native Swipe to Delete option. If you need that, you will either hack the spacing between cells in table view, or hack the swipe to delete in collection view. :) – Boris May 05 '16 at 15:23
  • @Boris I don't think Swipe to Delete in CollectionView is a hack, it's a feature. Otherwise, TableView doesn't have "Swipe to delete", you can only turn on edit mode, "swipe to reveal a button" then tap on it to delete. – Huy Le Mar 14 '17 at 04:24
2

There are many right answers here.

The easiest way I found, was to create a container view inside the cell's contentView in Interface Builder. Then put all your cells meaningful content inside that and constrain to that view.

Constrain the container view according to your padding requirements.

Something like:

enter image description here

enter image description here

Then in your tableViewCellForRow add the following (assuming your container view outlet is called containerView).

    currentCell.containerView?.layer.cornerRadius = 8 // Your choice here.
    currentCell.containerView?.clipsToBounds = true

// Add borders, shadows etc. if you'd like.

Looks like

rounder corners

Ron Holmes
  • 81
  • 4
1

I made a subclass of UITableViewCell and a nib file to go with it. Drag a UIView into the contentView of the cell and to start with, make it the same size as contentView. This will act as a container and become the "new" contentView. You can put what you need in there, just like you would normally in the contentView of a UITableViewCell.

Now you can adjust the height of the container view to leave your desired spacing at the bottom of the cell. Finally, change the background color of the contentView to clearColor, which will complete the effect.

Aero
  • 390
  • 5
  • 9
1

Objective-C version for Jorge Casariego Swift code:


//play with the 'y' parameter of the whiteRoundedView to change the spacing 
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.contentView.backgroundColor = [UIColor colorWithRed:225/255.0 green:225/255.0 blue:225/255.0 alpha:1.0];
    UIView  *whiteRoundedView = [[UIView alloc]initWithFrame:CGRectMake(5, 2, self.view.frame.size.width-10, cell.contentView.frame.size.height)];
    CGFloat colors[]={1.0,1.0,1.0,1.0};//cell color white
    whiteRoundedView.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), colors);
    whiteRoundedView.layer.masksToBounds = false;
    whiteRoundedView.layer.cornerRadius = 5.0;
    whiteRoundedView.layer.shadowOffset = CGSizeMake(-1, 1);
    whiteRoundedView.layer.shadowOpacity = 0.2;
    [cell.contentView addSubview:whiteRoundedView];
    [cell.contentView sendSubviewToBack:whiteRoundedView];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //assign the right cell identifier
    UITableViewCell *cell = [[self tableView]dequeueReusableCellWithIdentifier:CellIdentifier];
    return cell.bounds.size.height;
}
Maciej Chrzastek
  • 1,380
  • 12
  • 18
-2

These methods will help you to achieve the spacing:-

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 15;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *invisibleView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds .size.width, 15)];
[invisibleView setBackgroundColor:[UIColor clearColor]];
return invisibleView;

}
Mohd Prophet
  • 1,511
  • 10
  • 17
-2

There 2 possible solutions:
1) Use UICollectionView with vertical layout. This is really flexible solution, you can write you own layout for collection. Some amazing results with custom layout
2) Like @sikhapol has commented. But you will have problems with highlight cell. In my project I create common cell with content outlet view, witch contains all content view.

class ShadowContentCell: UITableViewCell {
    @IBOutlet var content: UIView!
    private var contentBg: UIImageView!
    private var contentOverlay: UIImageView!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.frame = super.frame
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        // custom higlight color
        selectedBackgroundView = UIView()
        selectedBackgroundView.backgroundColor = nil
        contentBg = UIImageView(frame: content.bounds)
        contentBg.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
        contentBg.image = UIImage(color: content.backgroundColor)
        content.addSubview(contentBg)
        content.sendSubviewToBack(contentBg)

        contentOverlay = UIImageView(frame: content.bounds)
        contentOverlay.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
        contentOverlay.highlightedImage = UIImage(color: UIColor(white: 0.000, alpha: 0.2))
        content.addSubview(contentOverlay)
    }

    override func setHighlighted(highlighted: Bool, animated: Bool) {
        contentOverlay.highlighted = highlighted
        if animated {
            let transition = CATransition()
            transition.duration = 0.3
            transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            transition.type = kCATransitionFade

            contentBg.layer.addAnimation(transition, forKey: nil)
        }
    }
    override func setSelected(selected: Bool, animated: Bool) {
    }
}
Gralex
  • 4,285
  • 7
  • 26
  • 47