简体   繁体   中英

Tableview cell border not occupying full width in swift

I'm trying to add a border on all sides to my table view cell, I was able to add the border but facing an issue where the border on the right side is not occupying the full width of the cell as in the screenshot below. The cell border seems to take the initial width on which the storyboard UI is set, for instance, I have set the UI as iPhone SE but if I run on iPhone 11 this issue occurs. Seems to be some issue with the layout not being refreshed. I have tried adding setNeedsLayout, setNeedsDisplay, and layoutSubviews but none of them seems to work.

在此处输入图像描述

Storyboard layout

在此处输入图像描述

Below is the full code

class ViewController: UIViewController {

    var dataSource = [String] ()
    override func viewDidLoad() {
        super.viewDidLoad()
        for index in 1...10 {
            dataSource.append("Cell value \(index)")
        }
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell: MyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as? MyTableViewCell else{return UITableViewCell()}
        
        cell.titleLabel.text = dataSource[indexPath.row]
        
        if indexPath.row == 0 {
            cell.containerView.addBorder(toEdges: [.left, .top, .right], color: .gray, thickness: 1.0)
        } else if indexPath.row == dataSource.count - 1 {
            cell.containerView.addBorder(toEdges: .all, color: .gray, thickness: 1.0)
        } else {
            cell.containerView.addBorder(toEdges: [.left, .top, .right], color: .gray, thickness: 1.0)
        }
        return cell
    }
}

class MyTableViewCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var containerView: UIView!
}

extension UIView {
    func addBorder(toEdges edges: UIRectEdge, color: UIColor, thickness: CGFloat) {
        func addBorder(toEdge edges: UIRectEdge, color: UIColor, thickness: CGFloat) {
            let border = CALayer()
            border.backgroundColor = color.cgColor
            switch edges {
            case .top:
                border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)
            case .bottom:
                border.frame = CGRect(x: 0, y: frame.height - thickness, width: frame.width, height: thickness)
            case .left:
                border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.height)
            case .right:
                border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)
            default:
                break
            }
            layer.addSublayer(border)
        }
        
        if edges.contains(.top) || edges.contains(.all) {
            addBorder(toEdge: .top, color: color, thickness: thickness)
        }
        
        if edges.contains(.bottom) || edges.contains(.all) {
            addBorder(toEdge: .bottom, color: color, thickness: thickness)
        }
        
        if edges.contains(.left) || edges.contains(.all) {
            addBorder(toEdge: .left, color: color, thickness: thickness)
        }
        
        if edges.contains(.right) || edges.contains(.all) {
            addBorder(toEdge: .right, color: color, thickness: thickness)
        }
    }
}

You may find it much easier to use a UIView subclass to handle the "edges."

Add this class to your project:

class BorderedView: UIView {
    
    var edges: UIRectEdge = []
    var color: UIColor = .clear
    var thickness: CGFloat = 0
    
    var shapeLayer: CAShapeLayer!
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        shapeLayer = self.layer as? CAShapeLayer
        shapeLayer.fillColor = UIColor.clear.cgColor
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let pth = UIBezierPath()
        
        if edges.contains(.top) || edges.contains(.all) {
            pth.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
            pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
        }
        
        if edges.contains(.bottom) || edges.contains(.all) {
            pth.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
            pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        }
        
        if edges.contains(.left) || edges.contains(.all) {
            pth.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
            pth.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
        }
        
        if edges.contains(.right) || edges.contains(.all) {
            pth.move(to: CGPoint(x: bounds.maxX, y: bounds.minY))
            pth.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        }

        shapeLayer.lineWidth = thickness
        shapeLayer.strokeColor = color.cgColor
        shapeLayer.path = pth.cgPath
    }
}

Then assign the Custom Class of your containerView to BorderedView . Connect it as normal in your cell class:

@IBOutlet weak var containerView: BorderedView!

Change your cell class to:

class MyTableViewCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var containerView: BorderedView!
    
    func configureContainer(toEdges edges: UIRectEdge, color: UIColor, thickness: CGFloat) -> Void {
        containerView.edges = edges
        containerView.color = color
        containerView.thickness = thickness
    }

}

Then, in cellForRowAt , instead of calling:

cell.containerView.addBorder(toEdges: [.left, .top, .right], color: .gray, thickness: 1.0)

call it like this:

cell.configureContainer(toEdges: [.left, .top, .right], color: .gray, thickness: 1.0)

Result - with the tableView inset 8-pts on each side so you can see the left/right edges:

在此处输入图像描述

When the tableView / cells change size (such as on device rotation), the edges automatically update:

在此处输入图像描述

I agree with @Asperi' s answer. Either you have to subclass it and write this method in drawrect. Or for quick solution change frame.width to UIScreen.main.bounds.width.Check It will work with your current requirement and with current code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM