最近业余写项目,实现了一种翻转卡片的效果,感觉效果还不错,记录下。首先来看下效果图:
卡片翻转效果图.gif
实现思路:
整个效果由一个背景图和3个卡片视图组成。获得一个数据数组后,在第一个卡片图上添加拖动Pan手势,修改卡片视图的transform中的Rotation属性。拖动结束后,刷新下3个卡片视图,每拖动结束一次就刷新一下3张卡片视图。
下面来看代码:
第一步: 初始化UI
//3张卡片视图
fileprivate var firstCell: WYClassBreakCell?
fileprivate var secondCell: WYClassBreakCell?
fileprivate var thirdCell: WYClassBreakCell?
fileprivate func setupUI(){
//1.添加背景
addSubview(bgImageView)
//2.添加numLabel
addSubview(numLable)
//3.添加cell
let margin: CGFloat = 10
let cellWith: CGFloat = kScreenW - 2*30
let cellHeight: CGFloat = cellWith/3*4
let x: CGFloat = (kScreenW - cellWith)/2
let y: CGFloat = (self.bounds.height - cellHeight)/2-30 - margin
var lastView: UIView = self
for i in 0..<3
{
let cell = WYClassBreakCell.classBreakCell()
cell.layer.anchorPoint = CGPoint(x: 0.5, y: 2.0)
cell.layer.position = CGPoint(x:0, y: 0)
cell.frame = CGRect(x: x, y: y - CGFloat(i)*margin, width: cellWith, height: cellHeight)
cell.tag = i + 1
if(i != 0)
{
insertSubview(cell, belowSubview: lastView)
lastView = cell
}else
{
//只有第一张添加手势
let pan = UIPanGestureRecognizer(target: self, action: #selector(pan(pan:)))
cell.addGestureRecognizer(pan)
addSubview(cell)
lastView = cell
}
}
//保存下属性
firstCell = viewWithTag(1) as? WYClassBreakCell
secondCell = viewWithTag(2) as? WYClassBreakCell
thirdCell = viewWithTag(3) as? WYClassBreakCell
}
2.刷新卡片视图的方法
方法作用: 根据index下标设置模型数组中对应的数据,如果当前展示的卡片是第1张,他的模型数据在数组中的index = 0,参数就传0
fileprivate func refershUIWithIndex(index : Int)
{
guard let classBraekList = classBraekList else{
return
}
//设置页码
numLable.numb.text = "\(index+1)"
if index <= (classBraekList.count) - 3 //不是倒数三张
{
//1.初始化图形位置
indentityTransFrom()
//3.显示数据
firstCell?.classBreakModel = (classBraekList[index])
secondCell?.classBreakModel = (classBraekList[index+1])
thirdCell?.classBreakModel = (classBraekList[index+2])
}else{//倒数3张
if index == (classBraekList.count) - 2 //还有2张
{
//初始化
indentityTransFrom()
//隐藏第3个Cell
thirdCell?.isHidden = true
//设置值
firstCell?.classBreakModel = (classBraekList[index])
secondCell?.classBreakModel = (classBraekList[index+1])
}else if index == (classBraekList.count) - 1 //最后一张
{
//初始化
indentityTransFrom()
//隐藏第2,3个Cell
thirdCell?.isHidden = true
secondCell?.isHidden = true
firstCell?.classBreakModel = (classBraekList[index])
}
}
//初始化图片位置
private func indentityTransFrom(){
firstCell?.isHidden = false
secondCell?.isHidden = false
thirdCell?.isHidden = false
if isFirst { //是否是第一张图片
firstCell?.transform = CGAffineTransform.identity
isFirst = false
}else
{
firstCell?.transform = CGAffineTransform(rotationAngle: CGFloat(-M_PI_4) * 0.1)
UIView.animate(withDuration: 0.2){
self.firstCell?.transform = CGAffineTransform.identity
}
secondCell?.transform = CGAffineTransform.identity
thirdCell?.transform = CGAffineTransform.identity
}
}
3.手势处理
手势处理中有两个部分需要进行处理
- 滑动时候处理
滑动分为左划和右划。 - 右划时:先判断是否是第一张,如果是第一张往右划,会有一个小抖动,如果不是第一张,设置动画,做成由下往上转的动画
- 左划时:获取手指的x偏移量,根据偏移量修改第一张卡片和第二张卡片的偏移量
- 取消滑动或者手指抬起时候处理
取消或结束手势时根据pan.view的transfrom.b来判断,这个数组是通过打印最后确定的,可以更改,也可以使用transform.a, .c, .d来确定。 - 如果超过限定的值,就表示此次切换卡片有效,进行处理
- 如果没有超过限定值,就表示此次切换卡片无效,设置第一张卡片和第二张卡片的transform = CGAffineTransform.identity。
@objc fileprivate func pan(pan: UIPanGestureRecognizer)
{
//第二个视图
let secondView = secondCell
//判断手指是否抬起了,或者手势被取消了
if pan.state == .ended || pan.state == .cancelled {
if CGFloat((pan.view?.transform.b)!) < -0.25 //滑动比例超过0.25
{
UIView.animate(withDuration: 0.1, animations: {
pan.view?.transform = CGAffineTransform(rotationAngle: CGFloat(-M_PI_2))
}, completion: { (isFinished) in
//刷新界面
self.index += 1
if self.index <= (self.classBraekList?.count)! - 1
{
self.refershUIWithIndex(index: self.index)
}else //最后一张再滑动 从父视图上面移除
{
self.removeFromSuperview()
}
})
}else
{
pan.view?.transform = CGAffineTransform.identity
secondView?.transform = CGAffineTransform.identity
}
if pan.translation(in: pan.view).x > 0 //右划
{
if index == 0
{
firstCell?.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_4) * 0.1)
UIView.animate(withDuration: 0.2){
self.firstCell?.transform = CGAffineTransform.identity
}
}else
{
//更新下成员变量index的值
index = index - 1
//先设置first的transform 是 90度
self.firstCell?.transform = CGAffineTransform(rotationAngle: CGFloat(-M_PI_2))
UIView.animate(withDuration: 0.5){
self.firstCell?.transform = CGAffineTransform.identity
self.refershUIWithIndex(index: self.index)
}
}
}
}else //手指正在滑动的时候
{
//获取手指的x偏移值
let offsetX = pan.translation(in: pan.view).x
if offsetX < 0
{
//计算百分比
let percent: CGFloat = offsetX / self.bounds.width
//计算这次要旋转的度数
let radians = CGFloat(M_PI_4) * percent
pan.view?.transform = CGAffineTransform(rotationAngle: radians)
secondView?.transform = CGAffineTransform(rotationAngle: radians/2)
}
}
}
这个效果的主要核心代码就是这些。
最后
项目的Demo地址:https://github.com/WzhGoSky/CardDance
如果你感觉这个效果不错,请给个❤️,码字也辛苦,😁😁
网友评论
懵逼了