Advertisement

ios动态效果实现翻页_iOS实现日历翻页动画

阅读量:

本文我主要描述两方面:

1.日历(简单描述原理)

2.翻页动画(重点)

最终的效果如下图:

图中沿四个对角的翻页动画,代表对应方向手势的滑动

1. 日历

要实现一个日历,其实原理很简单,我们只要知道三个数据:

1.今天是哪一天

2.这个月的第一天是星期几(哪天)

3.这个月总共有多少天

基于这三个数据就可以将获得的日期在日历中呈现出来。关于如何展示日历个人更倾向于使用UITableView来实现这一功能每个单元格代表一天当然还可以使用多个label和按钮来完成展示

1.获取今天是哪一天

这个应该是最简单的: NSDate(), 就可以获取当前的日期

2.获取这个月的第一天是星期几(哪天)

下面的方法都是作为NSDate的extension扩展的

//当前月第一天

func firstDateOfCurrentMonth() ->NSDate{

let calendar = NSCalendar(identifier:NSCalendarIdentifierGregorian )

let currentDateComponents = calendar!.component([.Year,.Month], fromSelf)

设置变量startOfMonth为当前日期组件对应的月份。

let date = startOfMonth?.dateByAddingTimeInterval(86060)

return date!

}

//当前月的第一天是星期几

func firstDayOfCurrentMonth() -> Int {

let calendar = NSCalendar.currentCalendar()

让 components 代表日历表中从第一个工作日开始的成员。

return components.weekday-1

}

3.获取这个月总共有多少天

基于这些数据, 从而能够得到每个日历单元格应显示的日期. 具体的显示方式以及与日期相关的三个主要类: NSDate、NSCalendar和NSDateComponents. 由于这些内容并非本文的重点讨论对象,这里不做详细阐述. 如果有不明白的地方, 可以参考官方文档, 或者等我以后撰写一篇更加详细的介绍这三个类的文章 (又一次挖坑待填).

2. 翻页动画

动画思路:

该动画归类于转场动画的一种类型。因此该动画类型可通过CATransition进行配置。配置过程相对简便,在此过程中仅需指定以下关键参数: animationDuration 表示动画时长; timeFunction 用于控制过渡节奏; fillMode 则影响填充效果设置。此外, CATransition 的 type 属性主要决定着过渡的整体效果, 而 subType 则通常用于指定具体过渡的方向性设置。值得注意的是, 官方文档仅提供了四种预设的过渡效果选项:

NSString * const kCATransitionFade;

NSString * const kCATransitionMoveIn;

NSString * const kCATransitionPush;

NSString * const kCATransitionReveal;

我们所需的翻页动画不在内部,在查阅相关资料后发现了较为理想的动画效果。在设置Transition类型时,默认使用的是不同的曲线,在设置Transition类型时,默认使用的是不同的曲线,在设置Transition类型时,默认使用的是不同的曲线

但是默认情况下翻页仅支持左上方动画切换和右下方动画切换

做出来的效果如下图:

无法达到四个对角都能进行翻页动画的效果。

为了实现沿四个对角方向翻页的效果, 随后, 我们可以在容器视图中嵌入日历视图. 这样一来, 在容器视图中嵌入日历视图就能实现沿四个对角方向翻页的效果

为了实现朝右上角翻页的目的,只需将containerView的layer绕y轴旋转M_π弧度.这样做的结果是使原本位于右下角的区域移动到左下角.这样一来,在执行翻页操作时,页面会朝着右上角的方向进行翻转.

然而为了确保日历能够正确显示,在dayView层中必须反转其层级结构;通过这种操作虽然在容器视图中呈现倒置状态(即容器视图反过来了),但实际展示出来的日期仍然是正确的

左下角翻页也是同样的道理。

具体代码如下:

//为dayView(代表日历的collectionview)添加一个滑动手势

func addPanGestureToDayView() {

let swipe: swipeType = UIPanGestureRecognizer(target of self, paneAction: paneSelector(#selector(self.panOnDayView(_:))))

dayView.addGestureRecognizer(swipe)

}

//当在dayView上滑动时触发

func panOnDayView(pan: UIPanGestureRecognizer) {

//如果手势的状态是结束状态

//或者当前动画已经结束(防止上一个翻页动画还没结束,就开始下一个)

//添加翻页的转场动画到dayView上

if pan.state == .Ended && !animatiing{

addAnimationToDayView(pan)

}

}

let pageCurlDuration = 0.5 //动画时间

let kPageCurlKey = "pageCurl" //往上翻页的的type

let kPageUnCurlKey = "pageUnCurl" //往下翻页的type

//添加动画到日历

func addAnimationToDayView(pan: UIPanGestureRecognizer) {

let translation = pan.translationInView(dayView)

//创建一个转场动画

let transitioin = CATransition()

transitioin.duration = pageCurlDuration

transitioin.timingFunction = CAMediaTimingFunction(name: "default")

//在动画结束之后保证状态不被移除(这个两个属性得同时设置)

transitioin.fillMode = kCAFillModeForwards

transitioin.removedOnCompletion = false

//设置代理,在动画开始和结束的代理方法中可以处理一些事情

transitioin.delegate = self

if translation.y < 0 {//手势向上

if translation.x > 0 {//手势朝右上角滑动,朝右上翻页

绕y轴旋转π弧度后,容器视图(containerView)的右下角将移动至左下方

animationContainerView.layer.transform被CATransform3DMakeRotation(...)所赋值

由于 dayView 是叠加在 containerView 上面的。若未将 dayView 重新翻转回来,则会导致显示界面出现倒置。

dayView.layer.transform = CATransform3DMakeRotation(CGFloat(-M_PI), 0, 1, 0)

}

//转场动画的效果

transitioin.type = kPageCurlKey

//转场动画方向

transitioin.subtype = kCATransitionFromBottom

//设定一个月份键用于判断动画是否处于当前月份或下个月份之间。

transitioin.setValue("next", forKey: "month")

}else{//下

if translation.x < 0 {//手势朝左下角滑动,朝左下翻页

animation层组件视图的transform属性被赋值为一个CATransform3DMakeRotationFunction实例,并传递了π值常量、零参数、单位参数和零参数。

dayView.layer.transform = CATransform3DMakeRotation(CGFloat(-M_PI), 0, 1, 0)

}

transitioin.type = kPageUnCurlKey

transitioin.subtype = kCATransitionFromTop

transitioin.setValue("pre", forKey: "month")

}

dayView.layer.addAnimation(transitioin, forKey: "pageCurl")

}

动画开始和停止时,进行一些处理:

由于在代码中设置了 transitioin delegate = self, 这些代理功能将自动启用

override func animationDidStart(anim: CAAnimation) {

首先,在动画启动的瞬间识别当前动画的方向变化,并据此触发相应的操作以更新日历显示内容。

animatiing = true

让组件为GregorianCalendar?.valueOf(.年, .月, .日),自定义日期。

if anim.valueForKey("month") as! String == "next" {

components?.month += 1

}else if anim.valueForKey("month") as! String == "pre"{

components?.month -= 1

}

date = (GregorianCalendar?.dateFromComponents(components!))!

dateDidChaged!(date: date)

}

//当动画结束后,将layer的transform属性归零,并停止为dayView的layer添加向上翻页动画

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {

if flag {

animatiing = false

animationContainerView.layer.transform = CATransform3DIdentity

dayView.layer.transform = CATransform3DIdentity

dayView.layer.removeAnimationForKey("pageCurl")

}

}

总结:

本文未对具体内容进行详尽介绍。
值得注意的是,在实现翻页动画时还存在其他方法。
鉴于我特别关注的是沿四个对角线方向展现动态效果,
因此决定采用这一方案。
其中提到的信息尚不够明确,
如仍有疑问,则建议参考CATranstion的具体使用指南。
以上即为此文的主要内容,
期望能对未来开发iOS动画提供些许帮助。

全部评论 (0)

还没有任何评论哟~