一种卡片翻转效果

作者: Hayder | 来源:发表于2017-01-08 23:27 被阅读545次

    最近业余写项目,实现了一种翻转卡片的效果,感觉效果还不错,记录下。首先来看下效果图:


    卡片翻转效果图.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
    如果你感觉这个效果不错,请给个❤️,码字也辛苦,😁😁

    相关文章

      网友评论

      • Charles___:我这刚好有个oc版的 :flushed:
        Charles___:@小czy 恩恩是呢
        小czy: @Charles姚 你那个支持往回滑动吗,看你的简书是探探类型的
      • 小czy:哥,我项目正需要。这个类似网易公开课的是客户指定要的。用后我给你打赏
        小czy: @Hayder 嗯嗯,明天开始改
        Hayder:@小czy swift 的差不多的 自己翻译成OC就好了 思路是一样的
        小czy:只看图就评论了。没想到是swift。。。。
        懵逼了
      • 我_是你哥:正需要,谢谢
      • coderZQ:抢占楼层,沙发沙发沙发******
      • coderZQ:海哥牛逼!
        Hayder:666666
      • 亡灵诅咒:棒棒棒

      本文标题:一种卡片翻转效果

      本文链接:https://www.haomeiwen.com/subject/aymnvttx.html