【问题标题】:UITableViewCell Borders Change When tableView.reloadData() is called调用 tableView.reloadData() 时 UITableViewCell 边框发生变化
【发布时间】:2022-10-24 06:12:51
【问题描述】:

当调用reloadData 时,我对我的UITableViewCell 发生的事情感到非常困惑。当 tableview 首次加载时,每个单元格都有正确的舍入。顶部的顶角是圆形的,中间没有圆形,底部只有底部。

这是第一次加载时的样子:

如果调用 tableview.reloadData(1 次或多次)会发生以下情况

这是创建单元格的代码(in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath))

        guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCell().reuseIdentifier, for: indexPath) as? MyCell else { return UITableViewCell() }

        if indexPath.row == 0 {
            cell.roundCorners(corners: (top: true, bottom: false))
        }
        if indexPath.row == 1 {
            cell.roundCorners(corners: (top: false, bottom: false))
        }
        if indexPath.row == 2 {
            cell.roundCorners(corners: (top: false, bottom: true))
        }

这是圆角的代码:

    func roundCorners(corners: (top: Bool, bottom: Bool)) {
        if !corners.top && !corners.bottom {
            roundCorners(withRadius: 0)
        } else if corners.top && corners.bottom {
           roundCorners(withRadius: 20)
        } else if corners.top {
            roundCorners(corners: [.topLeft, .topRight], radius: 20)
        } else if corners.bottom {
            roundCorners(corners: [.bottomLeft, .bottomRight], radius: 20)
        }
    }

// and the actual rounding methods

    func roundCorners() {
        roundCorners(withRadius: bounds.size.height * 0.5)
    }

    func roundCorners(withRadius radius: CGFloat) {
        layer.cornerRadius = radius
        layer.masksToBounds = true
    }

    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 maskedCorners(withRadius radius: CGFloat) {
        layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        layer.cornerRadius = radius
        clipsToBounds = true
    }

    func maskedCorners(corners: CACornerMask, radius: CGFloat) {
        layer.maskedCorners = corners
        layer.cornerRadius = radius
        clipsToBounds = true
    }

我尝试了几件没有用的事情。

  1. 我没有重用单元格(我认为可能重用它们的方式是在使用绘图调用),而是尝试每次都实例化单元格

  2. 我尝试将单元格边框重置为 0,然后设置哪些角应该是圆角的

  3. 我尝试在更新边框后调用 .setNeedsDisplay()

    我还要注意,如果我注释掉舍入调用,则根本不会发生舍入,因此该问题与上面的代码严格隔离。

    非常愿意接受关于为什么在第一次 reloadData() 调用它之后会围绕中间单元格并保持它向前移动的建议。

【问题讨论】:

  • 我用自定义单元尝试了这个,即使在 reloadData 之后它也可以工作。

标签: ios swift uitableview


【解决方案1】:

对于这样的布局,您不必编写代码,而是可以使用 tableview 样式Inset Grouped

只需转到您的Storyboard settings > Table attribute inspector > Style > 选择Inset Grouped

这是输出:

【讨论】:

  • 这是一个可接受的解决方案,赞赏。我仍然不知道问题的根源,但这有效。
【解决方案2】:

夫妻问题...

1 - 您在单元格布局完成之前使用bounds,因此UIBezierPath(roundedRect: bounds, ...) 可能是错误的。

2 - 在这段代码中:

func roundCorners(corners: (top: Bool, bottom: Bool)) {
    if !corners.top && !corners.bottom {
        // NOT setting a layer mask
        roundCorners(withRadius: 0)
    } else if corners.top && corners.bottom {
        // NOT setting a layer mask
        roundCorners(withRadius: 20)
    } else if corners.top {
        // YES setting a layer mask
        roundCorners(corners: [.topLeft, .topRight], radius: 20)
    } else if corners.bottom {
        // YES setting a layer mask
        roundCorners(corners: [.bottomLeft, .bottomRight], radius: 20)
    }
}

单元格被重复使用,因此您可以获得用于第一行或最后一行的单元格 - 设置图层蒙版的位置 - 然后将其用作“中间”行和图层蒙版未清除.

因此,要么清除“中间”行 (layer.mask = nil) 的掩码,要么总是调用使用掩码的函数:

func roundCorners(corners: (top: Bool, bottom: Bool)) {
    if !corners.top && !corners.bottom {
        roundCorners(corners: [], radius: 0)
    } else if corners.top && corners.bottom {
        roundCorners(corners: [], radius: 20)
    } else if corners.top {
        roundCorners(corners: [.topLeft, .topRight], radius: 20)
    } else if corners.bottom {
        roundCorners(corners: [.bottomLeft, .bottomRight], radius: 20)
    }
}

编辑

这是你的一些示例代码可能find 让事情变得更易于管理......

细胞类

class RoundedCornersCell: UITableViewCell {
    
    enum CornerStyle {
        case solo, top, middle, bottom
    }
    
    public var cornerStyle: CornerStyle = .middle {
        didSet {
            setNeedsLayout()
        }
    }
    public var cornerRadius: CGFloat = 20.0 {
        didSet {
            setNeedsLayout()
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        var theCorners: UIRectCorner = []
        switch cornerStyle {
        case .solo:
            theCorners = .allCorners
        case .top:
            theCorners = [.topLeft, .topRight]
        case .bottom:
            theCorners = [.bottomLeft, .bottomRight]
        case .middle:
            ()
        }
        
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: theCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }

}

示例控制器类

class ExampleTableVC: UIViewController, UITableViewDataSource, UITableViewDelegate {

    let tableView = UITableView()
    
    var myData: [Int] = []
    var shuffledData: [Int]!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        view.backgroundColor = .systemBlue
        
        myData = Array(1...5)
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
        ])
        
        tableView.register(RoundedCornersCell.self, forCellReuseIdentifier: "rcc")
        tableView.dataSource = self
        tableView.delegate = self
        
        tableView.rowHeight = 60
        tableView.backgroundColor = .clear
        
        shuffledData = myData
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return shuffledData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "rcc", for: indexPath) as! RoundedCornersCell
        
        // if there's only one row
        if tableView.numberOfRows(inSection: indexPath.section) == 1 {
            c.cornerStyle = .solo
        } else {
            c.cornerStyle =
            indexPath.row == 0 ? .top
            : indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 ? .bottom
            : .middle
        }

        c.textLabel?.text = "(shuffledData[indexPath.row])"
        return c
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        shuffledData = myData.shuffled()
        tableView.reloadData()
    }
}

【讨论】:

  • 精彩的演绎和解释。非常非常感谢你
  • @Aggressor - 我添加了一些示例代码(可能)让你的生活更轻松一些。见编辑对我的回答。
【解决方案3】:

您可以对 tableView 进行圆角:

tableView.layer.cornerRadius = 20
tableView.clipsToBounds = true

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多