【问题标题】:UITableViewCell shadows overlayUITableViewCell 阴影叠加
【发布时间】:2021-10-07 20:28:51
【问题描述】:

我有一个带有单元格的表格视图。 覆盖阴影已完成,但这看起来不像我想要的。 我的阴影白色圆形矩形应该保持白色。阴影应覆盖在白色矩形下方。有关如何实现预期行为的任何建议?

我将阴影添加为单独的子视图


class ShadowView: UIView {
    
    override var bounds: CGRect {
        didSet {
            setupShadow()
        }
    }
    
    private func setupShadow() {
        layer.shadowColor = UIColor.red.cgColor
        layer.shadowOpacity = 1
        layer.shadowRadius = 40
        layer.shadowOffset = CGSize(width: 1, height: 10)
        layer.masksToBounds = false
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 5).cgPath
    }
}

然后

let shadowView = ShadowView()        
addSubview(shadowView)

我想要这样的东西。白色矩形是完全白色的。

【问题讨论】:

  • 好的。这越来越令人困惑。 ShadowView 是哪一个?黑白视图还是白色视图?如果它是白色的,为什么不将阴影添加到表格视图单元格的图层?
  • 层次结构是 UITableViewCell -> ShadowView -> MyContainerView。 MyContainerView 是一个圆形的白色视图,包含黑色矩形)。
  • @IvanVavilov - “阴影应该覆盖在白色矩形下方” - 很难知道你真正想要什么。您能否创建一个看起来像您想要的图像(而不是看起来像您不想要的图像)?
  • @DonMag 更新了原帖。
  • @IvanVavilov - 你的细胞高度都一样吗?或者高度会根据内容而有所不同?例如,“母亲节见妈妈”标签是否有可能换成多行...

标签: ios uiview calayer


【解决方案1】:

如您所见,问题在于行(单元格)是独立的视图。如果您允许元素延伸到单元格之外,它将与相邻视图重叠或重叠。

这里有一个简单的例子来说明......

每个单元格都有一个systemYellow 视图,该视图在顶部和底部延伸到其框架之外:

如果我们使用Debug View Hierarchy 来检查布局,它看起来像这样:

正如我们所见,由于初始 z 顺序,每个单元格都覆盖了系统黄色视图向上延伸的部分,向下延伸的部分与下一个单元格重叠。

当我们滚动一点时,单元格会在不同的 z 顺序位置重新绘制(基于 tableView 如何重新使用它们):

现在我们看到一些 systemYellow 视图与上面的行重叠,一些与下面的行重叠,还有一些两者都重叠。

检查布局向我们展示了单元格的 z 顺序位置:

如果我们想保持 z-order 以便 systemYellow 视图不与它下面的单元格重叠,我们可以添加一个 func 来操纵 z-order 位置:

func updateLayout() -> Void {
    for c in tableView.visibleCells {
        tableView.bringSubviewToFront(c)
    }
}

我们需要在 tableView 滚动时(以及布局更改时)调用它:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    updateLayout()
}

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    updateLayout()
}

因此,您的布局也发生了同样的事情......阴影延伸到单元格的框架之外,并且与相邻单元格重叠或重叠。

如果我们开始使用相同的方法来管理单元格的 z 顺序,我们可以得到:

因此,我们将白色圆角矩形视图保留在“上方阴影”之上。当然,现在我们的阴影覆盖了视图的底部

我们可以更改.shadowPath 的矩形来避免这种情况:

override func layoutSubviews() {
    super.layoutSubviews()
    var r = bounds
    r.origin.y += 40
    layer.shadowPath = UIBezierPath(roundedRect: r, cornerRadius: 5).cgPath
}

我们得到这个输出:

还有一个问题——如果我们使用默认单元格.selectionStyle,我们会得到:

这可能是不可接受的。

所以,我们可以将.selectionStyle 设置为.none,并在我们的单元类中实现setSelected。在这里,我更改了圆角矩形背景和文本颜色以使其非常明显:

这里是一些示例代码——不需要@IBOutlet@IBAction 连接,因此只需将新表视图控制器的类分配给ShadowTableViewController

class ShadowView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        layer.shadowColor = UIColor.red.cgColor
        layer.shadowOpacity = 1
        layer.shadowRadius = 40
        layer.masksToBounds = false
        layer.cornerRadius = 12
        layer.shouldRasterize = true
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        var r = bounds
        r.origin.y += 40
        layer.shadowPath = UIBezierPath(roundedRect: r, cornerRadius: 5).cgPath
    }

}

class ShadowCell: UITableViewCell {
    
    let shadowView = ShadowView()
    let topLabel = UILabel()
    let bottomLabel = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        
        shadowView.backgroundColor = .white
        
        topLabel.font = .boldSystemFont(ofSize: 24.0)
        bottomLabel.font = .italicSystemFont(ofSize: 20.0)
        bottomLabel.numberOfLines = 0
        
        let stack = UIStackView()
        stack.axis = .vertical
        stack.spacing = 8
        
        stack.addArrangedSubview(topLabel)
        stack.addArrangedSubview(bottomLabel)
        
        shadowView.translatesAutoresizingMaskIntoConstraints = false
        stack.translatesAutoresizingMaskIntoConstraints = false
        
        shadowView.addSubview(stack)
        contentView.addSubview(shadowView)
        
        let mg = contentView.layoutMarginsGuide
        
        NSLayoutConstraint.activate([
            
            shadowView.topAnchor.constraint(equalTo: mg.topAnchor),
            shadowView.leadingAnchor.constraint(equalTo: mg.leadingAnchor),
            shadowView.trailingAnchor.constraint(equalTo: mg.trailingAnchor),
            shadowView.bottomAnchor.constraint(equalTo: mg.bottomAnchor),
            
            stack.topAnchor.constraint(equalTo: shadowView.topAnchor, constant: 12.0),
            stack.leadingAnchor.constraint(equalTo: shadowView.leadingAnchor, constant: 12.0),
            stack.trailingAnchor.constraint(equalTo: shadowView.trailingAnchor, constant: -12.0),
            stack.bottomAnchor.constraint(equalTo: shadowView.bottomAnchor, constant: -12.0),
            
        ])
        
        contentView.clipsToBounds = false
        self.clipsToBounds = false
        self.backgroundColor = .clear

        selectionStyle = .none
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        shadowView.backgroundColor = selected ? .systemBlue : .white
        topLabel.textColor = selected ? .white : .black
        bottomLabel.textColor = selected ? .white : .black
    }

}

class ShadowTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.separatorStyle = .none
        tableView.register(ShadowCell.self, forCellReuseIdentifier: "shadowCell")
    }

    func updateLayout() -> Void {
        for c in tableView.visibleCells {
            tableView.bringSubviewToFront(c)
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        updateLayout()
    }
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        updateLayout()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 30
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "shadowCell", for: indexPath) as! ShadowCell
        c.topLabel.text = "Row: \(indexPath.row)"
        var s = "Description for row \(indexPath.row)"
        if indexPath.row % 3 == 1 {
            s += "\nSecond Line"
        }
        if indexPath.row % 3 == 2 {
            s += "\nSecond Line\nThirdLine"
        }
        c.bottomLabel.text = s
        return c
    }
    
}

注意:这只是示例代码,不应被视为生产就绪

【讨论】:

    猜你喜欢
    • 2017-11-06
    • 2016-10-05
    • 2011-04-02
    • 1970-01-01
    • 2014-01-05
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    相关资源
    最近更新 更多