swift TableViewCell折叠动画

前段时间学习了一下swift的基础动画,于是找了个TableViewcell的动画项目来学习学习Folding cell

FoldingCell-sample.gif

学习的过程中遇到了一些坑,在这里分享一下,给大家借鉴借鉴

UITableViewController

首先我刚看到这个动画的时候,我先给这个动画进行了分解,在这个动画开始前,应该是先修改了cell 的高度,于是我先着手修改cell的高度

//定义了cell打开时和关闭时的高度
let openCellHeight: CGFloat = 505
let closeCellHeight: CGFloat = 180

//设置所有cell 的初始高度
var cellHeights: [CGFloat] = []
override func viewDidLoad() {
        super.viewDidLoad()
        cellHeights = Array(repeating: closeCellHeight, count: rowCount)

    }
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return cellHeights[indexPath.row]
    }

//并在创建的 UITableViewController内重写didSelectorRowAt 方法
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
       let cell = tableView.cellForRow(at: indexPath) as! TestTableViewCell
        
        let cellIsCollapsed = cellHeights[indexPath.row] == closeCellHeight
        if cellIsCollapsed {
            cellHeights[indexPath.row] = openCellHeight
        } else {
            cellHeights[indexPath.row] = closeCellHeight
        }
        tableView.reloadData()
    }

这时点击cell会有高度的变化,但是你会发现cell的高度变化是一瞬间的,并不是慢慢变大,于是我查找了一下资料,发现使用tabelView.beginUpdates()和tabelView.endUpdates()来代替tableView.reloadData()实现cell高度渐变的效果,并且使用UIView.animate(withDuration: TimeInterval, animations: () -> Void)来设置了tabelViewcell高度变化的时间差

UIView.animate(withDuration: druation, delay: 0, options: .curveEaseIn, animations: { () -> Void in
            tableView.beginUpdates()
            tableView.endUpdates()
        }, completion: nil)

这时我们就能通过设置druation来控制cell高度变化的时间,但是,这时你会发现点击cell 后整个cell会变成灰色,经过查找后发现这是由cell的selectionStyle引起的,我们需要把cell.selectionStyle设置为UITableViewCellSelectionStyle.none或者在storybord的界面选择cell后修改Selection为None。

UITableTableCell

第一步,我们要实现点击前和点击后cell的view显现两个不同的View,我们需要在cell 内的ContentView中创建两个高度不同的View(这里我们把forgroundView设为cell关闭时的View,constrainView为cell 打开后的View)当cell是对应的高度时就设置该view的alphe为1,另一个着为0。在我们设置动画之前,我们需要设置的forgroundView和constrainView的位置,我的做法是在storyboard为两个view 都设置了上左右和Height的约束,并把forgroundView和constrainView的Top约束连接到UITableTableCell里并设置它们的constant大小相同

    @IBOutlet weak var foreguoundTop: NSLayoutConstraint!
    @IBOutlet weak var constrainTop: NSLayoutConstraint!

constrainTop.constant = foreguoundTop.constant

第二步,我们先实现一个view能折叠翻转,这时我就想起之前学习的CABasicAnimation的transform.rotation,于是我先试着在forgroundView延底部翻转

animation1 = CABasicAnimation(keyPath: "transform.rotation.x")
        animation1.fromValue = 0
        animation1.toValue = -CGFloat.pi / 2.0
        animation1.duration = 0.13

forgroundView.layer.add(animation1, forKey: "forgroundView")

这时你能发现forgroundView是延中线翻转的我们需要延底部的,这时我们需要设置forgroudView的layer.anchorPoint,默认的View的layer.anchorPoint都为CGPoint(x: 0.5, y: 0.5),这个是View的中心点,要让forgroundView延底部转,我们需要设置layer.anchorPoint为CGPoint(x: 0.5, y: 1),这时你发现forgroundView的确能延底部翻转,但是你会发现修改layer.anchorPoint后forgroundView的位置发生了偏移,往上偏移了半个forgroundView的高度,查阅资料后发现两条公式:

frame.origin.x = position.x - anchorPoint.x * bounds.size.width 
frame.origin.y = position.y - anchorPoint.y * bounds.size.height

且position不受anchorPoint影响,所以当我们修改View的anchorPoint时变化的就是frame.origin,如果我们要不影响forgroundView的位置
我们需要先记录forgroundView的frame,修改完anchorPoint后再赋值forgroundView的frame

var imageFrame = forgroundView.frame
        forgroundView.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
        forgroundView.frame = imageFrame

在这里我要分享一个我遇到的坑,当初我设置animation和修改anchorPoint都是在cell的awakeFormNib()方法里,这里无论我怎么修改forgroundView的frame,forgroun都不会有任何变化,后来查阅资料才发现,原来awakeFormNib的调用是在ViewController创建之前就调用了,这时获取的View的frame都是不对的,所以无法重新赋值,所以我建议修改frame放在viewDidLayoutSubviews或之后的方法里
第三步
我们现在已经可以在不移动forgroundView情况下翻转forgroundView了,但是我们发现中间翻转的时候是有白色的View的,这是需要创建的,这里我就把显现的ImageViews和需要翻转的animationViews分别创建,这时我们就需要获取forgroundView和constainsView的视图赋值给显现的View

public extension UIView {
    func setSampleView(frame: CGRect) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)
        guard let context = UIGraphicsGetCurrentContext() else {
            print("fail")
            return nil
        }
        context.translateBy(x: frame.origin.x * -1   , y: frame.origin.y * -1 )
        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

这里我扩展了UIView来获取forgroundView和constainsView的视图

let firstImage = constrainView.setSampleView(frame: CGRect(x: constrainView.bounds.minX, y: constrainView.bounds.minY, width: forgroundView.frame.width, height: forgroundView.frame.height))
        firstViewBg = UIImageView(image: firstImage)
        firstViewBg.frame = CGRect(x: constrainView.bounds.minX, y: constrainView.bounds.minY, width: forgroundView.frame.width, height: forgroundView.frame.height)

这样我们就可以获取以forgroundView和constrainView为图片的ImageViews和animationViews
第四步
这时我们需要把动画连接起来,我看了FoldingCell里它是通过设置动画的beginTime使动画连续起来,而我选择使用animationDidStop()来使动画连接起来,这个方法的调用需要cell继承CAAnimationDelegate
并且需要设置animatiom需要设置delegate和设置isRemoveOnCompletion为false

animation1.isRemovedOnCompletion = false
animation1.delegate = self

//上一个animation结束时就执行下一个动画
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if anim == firstImageView.layer.animation(forKey: "firstImageView") {
            firstImageView.alpha = 0
            secondImageView.alpha = 1
            secondImagebgView.alpha = 1
            firstViewBg.layer.masksToBounds = false
            secondImageView.layer.add(animation2, forKey: "secondImageView")
        }
}

当我们我们都设置好之后,运行起来之后我们会发现,我们的翻转并没有那种3D的立体感,这个我们需要设置layer.transform

var animationTransform = CATransform3DIdentity

animationTransform.m34 = -2.5 / 2000

firstImageView.layer.transform = animationTransform

CALayer中有一个transform属性便是专门用来控制3D形变的,transform属性默认值为CATransform3DIdentity, 在CATransform3DIdentity结构体中有一个m34允许我们将正交投影修改为有近大远小立体效果的透视投影,其中m34 = -1.0/z,这个z为观察者与控件之间的距离, m34必须在赋值transform之前设置才会生效.
到此我们的动画基本久已经完成了,在此我想补充一点,可能我们做到这之后运行起来可能会感觉View有时会有一种闪现的感觉,查找资料后,发现animation有个fillMode的属性我们设置未kCAFillModeForwards后能解决该问题

animation1.fillMode = kCAFillModeForwards

到此动画就算基本完成了附上代码

总结

新手刚学,知识水平有限,如有错误之处,还望指出.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi阅读 5,336评论 3 23
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,690评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,268评论 5 13
  • 在iOS实际开发中常用的动画无非是以下四种:UIView动画,核心动画,帧动画,自定义转场动画。 1.UIView...
    请叫我周小帅阅读 3,320评论 1 23
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 3,155评论 0 21

友情链接更多精彩内容