之前看到一个多张银行卡卡片叠加、点击动态展开的效果,感觉很不错,抽空实现了一下。
思路:
用 UITableView 实现,一张卡片就是一个 Cell,展开一张卡片就是 update 这个 Cell 的高度。
Cell
绘制圆角、阴影
@IBOutlet var cardView: UIView!
@IBOutlet var cardImg: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let radius: CGFloat = 15
cardView.layer.cornerRadius = radius
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.5
cardView.layer.shadowOffset = CGSize(width: 0.5, height: 0.5)
cardView.layer.shadowRadius = radius
cardImg.layer.cornerRadius = radius
cardImg.clipsToBounds = true
backgroundColor = .clear
contentView.backgroundColor = .clear
}
从上图可以看到,我这里使用xib画的cell,由于卡片比例是固定的,所以cardView加比例约束,防止卡片变形。进一步根据比例和屏幕宽度,计算出卡片展开时cell的高度openHeight(卡片被遮挡时的高度固定54):
let openHeight: CGFloat = (UIScreen.main.bounds.size.width - 30) * (593 / 939) + 25
let closeHeight: CGFloat = 54
CardView 左、上、右相对superView都设置了约束,因为要实现效果是上一张卡被下一张卡遮挡,所以底部不需要加约束。
显示时,上面所有的cell都是close的,被点击才会更新高度(展开或收起)。当点击第二个cell时,关闭前一个cell,打开后一个cell,保证一次只打开一张卡。最后一个cell是固定open的,点击也不会改变高度。
现在运行效果是左边这样的,好像是有点样子了,但是是上面的卡遮住了下面的卡。上拉一下,让部分卡片到屏幕外,松手之后又变成了右边这样。这是因为随着cell的复用,界面上cell的之间的层级是变化的,导致卡的层级也发生了变化。
那如果设置cell的clipsToBounds,让超出部分不显示呢?
答案是不行:
可以看到,红框是实际cell的位置,要让cell2卡片挡住cell1卡片,cell1的卡片必须超出cell1的范围,穿过cell2卡片下方。所以clipsToBounds这个方法行不通
解决办法
这时候需要用到layer的zPosition属性:
这个属性的作用是设置显示的优先级,数字越高,就越会显示在最前面(但是不会影响响应链的优先级)
layer.zPosition探究
具体到这里,就是保证在下面的cell.layer.zPosition的值永远比上面的大就行了。比如说这里界面上,从上到下的layer.zPosition的值分别设置为是1,2,3,这样就可以保证下张卡一直显示在上一张卡上面。
最近用Xcode11运行demo,发现在iOS13的模拟器上面执行动画时,cell上面透明部分会变成不透明,折腾了我好久,最后发现其实只是iOS13模拟器的问题,我用真机就完全没问题,fxxk。
总体思路就是这些,下面附上demo。
ZJTableViewManager中的ZJTableViewDemo的BasicExample的UpdateHeight范例