1. 日历
要实现一个日历,其实原理很简单,我们只要知道三个数据:
1.今天是哪一天
2.这个月的第一天是星期几(哪天)
3.这个月总共有多少天
根据这个三个数据,就可以把得到的日期显示在日历上了,至于日历用什么来显示,我个人比较喜欢用UICollectionView,一个cell代表一天,当然也可以用很多个label,button来显示。
1.获取今天是哪一天
这个应该是最简单的: NSDate()
, 就可以获取当前的日期
2.获取这个月的第一天是星期几(哪天)
下面的方法都是作为NSDate的extension扩展的
//当前月第一天func firstDateOfCurrentMonth() ->NSDate{let calendar = NSCalendar(identifier:NSCalendarIdentifierGregorian )let currentDateComponents = calendar!.components([.Year,.Month], fromDate: self)let startOfMonth = calendar!.dateFromComponents(currentDateComponents)let date = startOfMonth?.dateByAddingTimeInterval(8*60*60)return date!}//当前月的第一天是星期几func firstDayOfCurrentMonth() -> Int {let calendar = NSCalendar.currentCalendar()let components = calendar.components(.Weekday, fromDate: firstDateOfCurrentMonth())return components.weekday-1}3.获取这个月总共有多少天
NSDate
, NSCalendar
, NSDateComponents
由于不是本文的重点,我这里就不详细说了,如果有不明白的可以去看一下文档,或者如果我下次写一个详细的关于这三个类的(又挖一个坑。。)。CATrasition
进行动画,CATransition
的使用非常简单,只要设置动画时长,时间函数,fillMode
等,就可以得到想要的动画,CATransition
的type
代表的是过渡时候的动画效果,subType
一般代表动画的方向,但是查看了一下CATransition
的type
属性,官方文档里面只描述了下面四种预定义的转场动画效果:NSString * const kCATransitionFade;NSString * const kCATransitionMoveIn;NSString * const kCATransitionPush;NSString * const kCATransitionReveal;我们需要的翻页动画并不在里面,在google了一下之后,找到了一个比较理想的效果,通过直接设置
CATransition
的type
为"pageCurl
"或"pangeUnCurl
"进行动画,这两个值官方文档没有提供,我也不知道为啥这些大神能找到。。。
无法达到四个对角都能进行翻页动画的效果。
为了得到可以沿着四个对角方向翻页的效果,我们可以先在最底部添加一个containerView
,然后在containerView
中添加dayView
(下面提到的dayView
和代码中的dayView
都代表的是作为日历的collectionView
)。
如果要进行朝右上角翻页,我们只要把containerView
的layer
先沿y轴
翻转M_PI
,这样,最开始的右下角就变成左下角了,翻页时就会变成向右上角翻页
但是为了日历显示正确,我们需要把dayView
的layer
重新翻转过来,这样,containerView
是反的,但是我们看到的日期显示是正的
左下角翻页也是同样的道理。
具体代码如下:
//为dayView(代表日历的collectionview)添加一个滑动手势func addPanGestureToDayView() {let swipe = UIPanGestureRecognizer(target: self, action: #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" //往上翻页的的typelet kPageUnCurlKey = "pageUnCurl"//往下翻页的type//添加动画到日历func addAnimationToDayView(pan: UIPanGestureRecognizer) {let translation = pan.translationInView(dayView)//创建一个转场动画let transitioin = CATransition()transitioin.duration = pageCurlDurationtransitioin.timingFunction = CAMediaTimingFunction(name: "default")//在动画结束之后保证状态不被移除(这个两个属性得同时设置)transitioin.fillMode = kCAFillModeForwardstransitioin.removedOnCompletion = false//设置代理,在动画开始和结束的代理方法中可以处理一些事情transitioin.delegate = selfif translation.y < 0 {//手势向上**if translation.x > 0 {//手势朝右上角滑动,朝右上翻页//沿y轴对containerView进行M_PI角度翻转,使containerView的右下角变为左下角animationContainerView.layer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 0, 1, 0)//因为dayView是加在containerView上面的,如果不把dayView重新翻转回去,显示出来的界面都是反的dayView.layer.transform = CATransform3DMakeRotation(CGFloat(-M_PI), 0, 1, 0)}//转场动画的效果transitioin.type = kPageCurlKey//转场动画方向transitioin.subtype = kCATransitionFromBottom//设置一个month的key,为了在动画结束时判断动画代表的是上一个月,还是下一个月transitioin.setValue("next", forKey: "month")}else{//下if translation.x < 0 {//手势朝左下角滑动,朝左下翻页animationContainerView.layer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 0, 1, 0)dayView.layer.transform = CATransform3DMakeRotation(CGFloat(-M_PI), 0, 1, 0)}transitioin.type = kPageUnCurlKeytransitioin.subtype = kCATransitionFromToptransitioin.setValue("pre", forKey: "month")}dayView.layer.addAnimation(transitioin, forKey: "pageCurl")}动画开始和停止时,进行一些处理:
//因为上面设置了 transitioin.delegate = self,这两个代理方法会自动调用override func animationDidStart(anim: CAAnimation) {//动画开始时,判断当前动画是代表往上翻页,还是往下翻页,来刷新日历时间animatiing = truelet components = GregorianCalendar?.components([.Year,.Month,.Day], fromDate: date)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 = falseanimationContainerView.layer.transform = CATransform3DIdentitydayView.layer.transform = CATransform3DIdentitydayView.layer.removeAnimationForKey("pageCurl")}}总结: