Reputation: 30
I have a requirement where I need to border width 1 (CGFloat) on the top, left, and bottom side of the uiview and 0 (CGFloat) on the right side. I tried adding multiple CALayers for each side, but the view was not good, as while scrolling the border did not scroll with uiview.
Code I tried using:
enum ViewSide: String {
case left
case right
case top
case bottom
}
func addBorderToAllSides(color: UIColor, thickness: CGFloat) {
addBorder(toSides: [.left, .right, .bottom, .top], withColor: color, andThickness: thickness)
}
func addBorder(toSides sides: [ViewSide], withColor color: UIColor, andThickness thickness: CGFloat) {
sides.forEach { (side) in
///remove previously added sublayer
layer.sublayers?.removeAll(where: {$0.name == side.rawValue})
let border = CALayer()
border.backgroundColor = color.cgColor
switch side {
case .left:
border.frame = CGRect(x: frame.minX, y: frame.minY, width: thickness, height: frame.height)
case .right:
border.frame = CGRect(x: frame.maxX, y: frame.minY, width: thickness, height: frame.height)
case .top:
border.frame = CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: thickness)
case .bottom:
border.frame = CGRect(x: frame.minX, y: frame.maxY, width: frame.width, height: thickness)
}
border.name = side.rawValue
layer.addSublayer(border)
}
}
}
src - https://gist.github.com/MrJackdaw/6ffbc33fc274838412bfe3ad48592b9b
What I'm trying to achieve:
here for each item, adding the same border width on each side causes the border in the middle of 2 items to be double width. Want to get rid of that.
Any help is appreciated
Upvotes: 0
Views: 563
Reputation: 77477
There are various ways to do this, but one method that you may like is to add a CAShapeLayer
as a single sublayer, and use a UIBezierPath
for the sides.
Here's a simple example:
class SidesCell: UICollectionViewCell {
enum ViewSide: String {
case left
case right
case top
case bottom
}
var sides: [ViewSide] = []
let sidesLayer: CAShapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
contentView.layer.addSublayer(sidesLayer)
sidesLayer.fillColor = UIColor.clear.cgColor
sidesLayer.strokeColor = UIColor.gray.cgColor
sidesLayer.lineWidth = 1.0
}
// modify this based on what you're setting in the cell
// label text, colors, whatever...
func setData(_ str: String, sides: [ViewSide]) -> Void {
self.sides = sides
// use other data
}
// this will be called when the cell is ready for layout
override func layoutSubviews() {
super.layoutSubviews()
let pth = UIBezierPath()
if sides.contains(.left) {
pth.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
pth.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
}
if sides.contains(.top) {
pth.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
}
if sides.contains(.right) {
pth.move(to: CGPoint(x: bounds.maxX, y: bounds.minY))
pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
}
if sides.contains(.bottom) {
pth.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
}
sidesLayer.path = pth.cgPath
}
}
Then, in cellForItemAt
, you would set the sides needed for each cell.
For example, if you have 7 cells, and you want Top/Left/Bottom "side lines" on the first 6:
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "myIdentifier", for: indexPath) as! SidesCell
if indexPath.item == 6 {
c.setData("Test", sides: [.top, .bottom])
} else {
c.setData("Test", sides: [.top, .left, .bottom])
}
return c
Upvotes: 1