tableView 如何对整个 Section 设置圆角

早年我曾想实现一个类似 iPad 设置页面右方的 tableView 的风格。它的特点主要是在整个分组设置圆角,如下图所示

难点

由于 Section 并不是一个真实存在的 View,而是 tableView 便于管理设计的一个逻辑容器。那么如何对整个 Section 设置圆角呢?

还有,如果要利用 tableViewCell 的自带元素,那么 Cell 与父视图应该有一定的 padding,且高亮不能是完整的一行,这个也是我当初没考虑到的。

方案

当时在网上找了两种方案,一种是计算好 Section 的位置,在底下添加白底。

一种是在把每个 Section 当做一个 Row 处理,这样的坏处是要自己处理点击高亮,而且没有很好复用 Cell。

现在看来,这两种方案都不太优雅。

我想到了第三种方案,大概是这样的: Section 第一个 Cell 设置左上、右上圆角,最后一个 Cell 设置左下,右下边角,如果只有一个 Cell,设置全部圆角。

实现

首先,Row 的宽度不需要占满整个行,所以我需要对 Cell 的位置进行调整,调整完毕之后,设置圆角,圆角可以分3种情形来设置,还有一种不设置的情况。

调整 Cell 的位置

一开始,我设置了 tableView 的 contentInsets 属性,结果却让 tableView 可以左右滑动了,因为 tableView 继承于 scrollView,只不过他的 contentSize 的宽度与 frame 的宽度一致才没有左右滑动,设置了 contentInsets 的左右边距之后,tableView 不再跟预想一样了。

想想我的目标:调整 tableViewCell 的宽度,使之的 x 坐标不为 0。

那么这个任务就好办了,我可以在 layoutSubviews 里面去操作,反正任何布局代码都会到这里来,包括通过 AutoLayout 设置的布局。

然后我自定义了一个 TableViewCell,从而能够自定义 layoutSubviews 操作:

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

复制代码

设置圆角

通过 Google,很容易找到了设置部分圆角的办法,然后我在此基础上添加了一个不设置圆角的方法以避免复用问题

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() {
        layer.mask = nil
    }
}
复制代码

经过刚才的分析,把这4种状态作为一个枚举:

class TableViewCell: UITableViewCell {
    enum Position {
        case solo
        case first
        case middle
        case last
    }
    var position: Position = .middle
}
复制代码

然后在 layoutSubviews 里面设置就行了。由于这种圆角是通过 mask 设置的,不会造成离屏渲染问题,可以放心使用。

override func layoutSubviews() {
    super.layoutSubviews()
    adjustMyFrame()
    setCorners()
}
func setCorners() {
    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()
    }
}
复制代码

同时,需要在 tableViewCell 的数据源指定这个位置:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
    cell.textLabel?.text = listData[indexPath.section][indexPath.row]
    cell.accessoryType = .disclosureIndicator
    if 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
}
复制代码

然后,再自定义分割线,细调一下,就OK啦!

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章