Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get 1 pixel width borders in UICollectionView cells? (code provided)

How can I get a pixel perfect one (1) pixel with border line in a UICollectionView (e.g. to make a month calendar). The issue is that where cells meet, their borders meet, so it is effectively 2 pixels (not 1 pixel).

Therefore this is a CollectionView layout related issue/question. As the way each cell puts it own 1 pixel border gives rise, when the cells all meet up with zero spacing, to what looks like a 2 pixels border overall (except for outer edges of the collectionView which would be 1 pixel)

Seeking what approach/code to use to solve this.

Here is the custom UICollectionViewCell class I use, and I put borders on the cells in here using drawrect.

import UIKit

class GCCalendarCell: UICollectionViewCell {

    @IBOutlet weak var title : UITextField!

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

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func drawRect(rect: CGRect) {
        self.layer.borderWidth = 1
        self.layer.borderColor = UIColor.redColor().CGColor
    }

}
like image 772
Greg Avatar asked Oct 11 '15 12:10

Greg


2 Answers

There are a few strategies you can take to solve this issue. Which is best depends on some of the details of your layout.

Option 1: Half Borders

Draw borders of 1/2 the desired width around each item. This has been suggested. It achieves the correct width for borders between items, but the borders around the edge of the collection will also be half the desired width. To solve this you can draw a border around the entire collection, or sections.

If you're month layout only renders days in a given month, meaning each month has a unique non-rectangular shape, fixing the edge borders is more complicated than it's worth.

Option 2: Overlapping Cells

Draw the full border around each item but adjust the frames so they overlap by half of the width of the borders, hiding the redundancy.

This is the simplest strategy and works well, but be aware of a few things:

  • Be mindful that the outer edges of the layout are also slightly changed. Compensate for this if necessary.
  • The borders will overlap, so you can't use semi-transparent borders.
  • If not all the borders are the same color, some edges will be missing. If there is just a single cell with a different border, you can adjust the item's z-index to bring it on top of the other cells.

Option 3: Manual Drawing

Rather than using borders on the cell at all, you can add a border around the container and then manually draw the borders in the superview. This could be accomplished by overriding drawRect(_:) and using UIBezierPaths to draw horizontal and vertical lines at the intervals of the cells. This is the most manual option. It gives you more control, but is much more work.

Option 4: Draw Only Some Borders in Each Cell

Rather than assigning the layer's border width and border color, which draws a border around the entire view, selectively draw edges of cells. For example: each cell could only draw it's bottom and right edge, unless there is no cell above or to the right of it.

This can again lead to complications if you have an irregular grid. It also requires each cell to know about it's context in the grid which requires more structure. If you go this route, I would suggest subclassing UICollectionViewLayoutAttributes and adding additional information about which edges should be drawn. The edge drawing itself can again be done by creating UIBezierPath's in drawRect(_:) for each edge.

like image 104
Anthony Mattox Avatar answered Oct 11 '22 09:10

Anthony Mattox


I usually divide the border width by the screen scale:

width = 1.0f / [UIScreen mainScreen].scale
like image 30
Wilmar Avatar answered Oct 11 '22 09:10

Wilmar