The original

In the early days I wanted to implement a tableView style similar to the one on the right side of the iPad Settings page. Its main feature is the setting of rounded corners throughout the grouping, as shown in the figure below

The difficulties in

Since the Section is not a real View, it’s a logical container for tableViews to manage and design. So how do I round the entire Section?

Also, if you want to use the tableViewCell’s own elements, there should be some padding between the Cell and the parent view, and the highlight can’t be a full line, which I didn’t consider.

plan

At that time, I found two schemes on the Internet. One is to calculate the position of the Section and add a white background at the bottom.

One is to treat each Section as a Row, which has the disadvantage of doing click highlighting itself and not reusing cells very well.

Neither option now looks very elegant.

I came up with a third solution, which is roughly like this: the first Cell of the Section is set to the upper left and upper right corners; the last Cell is set to the lower left and lower right corners; if there is only one Cell, all corners are set.

implementation

First, the width of the Row does not need to cover the entire Row, so I need to adjust the position of the Cell. After the adjustment, I can set the rounded corners. There are three ways to set the rounded corners, and one way not to set the rounded corners.

Adjust the Cell position

So I set the contentInsets of the tableView, and now I can slide the tableView left and right, because the tableView inherits from the scrollView, However, the width of the contentSize is the same as the width of the frame, so it does not slide left and right. After setting the left and right margins of the contentInsets, the tableView is no longer as expected.

Think about my goal: adjust the width of the tableViewCell so that its x-coordinate is not zero.

I can do this in layoutSubviews, where any layout code will come anyway, including the layout set by AutoLayout.

Then I customize a TableViewCell so that I can customize the layoutSubviews operation:


class TableViewCell: UITableViewCell {
    override func layoutSubviews(a) {
        super.layoutSubviews()
        adjustMyFrame()
    }
    
    func adjustMyFrame(a) {
        frame = CGRect(x: 16, y: frame.minY, width: superview! .frame.width -32, height: frame.height)
    }
}

Copy the code

Set the rounded corners

Using Google, it was easy to find a way to set some rounded corners, and I added a no-rounded method to avoid reuse issues

extension UIView {
    func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
    func noCornerMask(a) {
        layer.mask = nil}}Copy the code

After the analysis, consider these four states as an enumeration:

class TableViewCell: UITableViewCell {
    enum Position {
        case solo
        case first
        case middle
        case last
    }
    var position: Position = .middle
}
Copy the code

Then you can set it in layoutSubviews. Since this rounded corner is set by mask, it does not cause off-screen rendering problems and can be used safely.

override func layoutSubviews(a) {
    super.layoutSubviews()
    adjustMyFrame()
    setCorners()
}
func setCorners(a) {
    let cornerRadius: CGFloat = 4.0
    switch position {
    case .solo: roundCorners(corners: .allCorners, radius: cornerRadius)
    case .first: roundCorners(corners: [.topLeft, .topRight], radius: cornerRadius)
    case .last: roundCorners(corners: [.bottomLeft, .bottomRight], radius: cornerRadius)
    default: noCornerMask()
    }
}
Copy the code

Also, you need to specify this location in the tableViewCell’s data source:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell".for: indexPath) as! TableViewCellcell.textLabel? .text = listData[indexPath.section][indexPath.row] cell.accessoryType = .disclosureIndicatorif listData[indexPath.section].count= =1 {
        cell.position = .solo
    } else if indexPath.row == 0 {
        cell.position = .first
    } else if (indexPath.row == listData[indexPath.section].count - 1) {
        cell.position = .last
    } else {
        cell.position = .middle
    }
    return cell
}
Copy the code

Then, custom secant line, fine tune, OK!