UITableViewCell 阴影覆盖
UITableViewCell shadows overlay
我有一个包含单元格的 table 视图。
叠加阴影已完成,但看起来不像我想要的那样。
我的阴影白色圆形矩形应该保持白色。阴影应该覆盖在白色矩形下方。关于如何实现预期行为的任何建议?
我将阴影添加为单独的子视图
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)
我想要这样的东西。白色矩形是完全白色的。
如您所见,问题在于行(单元格)是分开的 视图。如果您允许元素延伸到单元格之外,它将与相邻视图重叠或欠重叠。
这里有一个简单的例子来说明...
每个单元格都有一个 systemYellow
视图,在顶部和底部延伸到其框架之外:
如果我们使用 Debug View Hierarchy
检查布局,它看起来像这样:
正如我们所见,由于初始 z 顺序,每个单元格都覆盖了向上延伸的 systemYellow 视图部分,而向下延伸的部分与下一个单元格重叠。
当我们稍微滚动时,单元格会在不同的 z 顺序位置重新绘制(基于 tableView 重新使用它们的方式):
现在我们看到一些 systemYellow 视图与上面的行重叠,一些与下面的行重叠,还有一些两者都重叠。
检查布局会向我们显示单元格的 z 顺序位置:
如果我们想要保持 z 顺序以便 none systemYellow 视图与其下方的单元格重叠,我们可以添加一个函数来操纵 z 顺序位置:
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
,我们会得到:
这可能不被接受table。
因此,我们可以将 .selectionStyle
设置为 .none
,并在我们的单元格 class 中实现 setSelected
。在这里,我更改了圆角矩形背景和文本颜色,使其非常明显:
这是一些示例代码——不需要 @IBOutlet
或 @IBAction
连接,因此只需将新的 table 视图控制器的 class 分配给 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
}
}
注意:这只是 示例代码,不应被视为生产就绪。
我有一个包含单元格的 table 视图。 叠加阴影已完成,但看起来不像我想要的那样。 我的阴影白色圆形矩形应该保持白色。阴影应该覆盖在白色矩形下方。关于如何实现预期行为的任何建议?
我将阴影添加为单独的子视图
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)
我想要这样的东西。白色矩形是完全白色的。
如您所见,问题在于行(单元格)是分开的 视图。如果您允许元素延伸到单元格之外,它将与相邻视图重叠或欠重叠。
这里有一个简单的例子来说明...
每个单元格都有一个 systemYellow
视图,在顶部和底部延伸到其框架之外:
如果我们使用 Debug View Hierarchy
检查布局,它看起来像这样:
正如我们所见,由于初始 z 顺序,每个单元格都覆盖了向上延伸的 systemYellow 视图部分,而向下延伸的部分与下一个单元格重叠。
当我们稍微滚动时,单元格会在不同的 z 顺序位置重新绘制(基于 tableView 重新使用它们的方式):
现在我们看到一些 systemYellow 视图与上面的行重叠,一些与下面的行重叠,还有一些两者都重叠。
检查布局会向我们显示单元格的 z 顺序位置:
如果我们想要保持 z 顺序以便 none systemYellow 视图与其下方的单元格重叠,我们可以添加一个函数来操纵 z 顺序位置:
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
,我们会得到:
这可能不被接受table。
因此,我们可以将 .selectionStyle
设置为 .none
,并在我们的单元格 class 中实现 setSelected
。在这里,我更改了圆角矩形背景和文本颜色,使其非常明显:
这是一些示例代码——不需要 @IBOutlet
或 @IBAction
连接,因此只需将新的 table 视图控制器的 class 分配给 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
}
}
注意:这只是 示例代码,不应被视为生产就绪。