源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

iOS实现日历翻页动画

  • 时间:2021-11-07 11:12 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:iOS实现日历翻页动画
[b]本文我主要描述两方面:[/b]     1.日历(简单描述原理)     2.翻页动画(重点) [b]最终的效果如下图:[/b] [b]   [/b] 图中沿四个对角的翻页动画,代表对应方向手势的滑动 [img]http://files.jb51.net/file_images/article/201608/20168490048111.gif?201674912[/img] [b]1. 日历[/b] 要实现一个日历,其实原理很简单,我们只要知道三个数据:     1.今天是哪一天     2.这个月的第一天是星期几(哪天)     3.这个月总共有多少天 根据这个三个数据,就可以把得到的日期显示在日历上了,至于日历用什么来显示,我个人比较喜欢用[b]UICollectionView[/b],一个[b]cell[/b]代表一天,当然也可以用很多个[b]label[/b],[b]button[/b]来显示。 [b]1.获取今天是哪一天[/b] 这个应该是最简单的: [code]NSDate() [/code], 就可以获取当前的日期 [b]2.获取这个月的第一天是星期几(哪天) [/b] 下面的方法都是作为[b]NSDate[/b]的[b]extension[/b]扩展的
//当前月第一天
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
}
[b]3.获取这个月总共有多少天[/b] 根绝上面这些数据,就可以得到日历里面每个格子应该显示的日期,具体的显示和有关日期的三个主要的类: [code]NSDate[/code], [code]NSCalendar[/code], [code]NSDateComponents [/code]由于不是本文的重点,我这里就不详细说了,如果有不明白的可以去看一下文档,或者如果我下次写一个详细的关于这三个类的(又挖一个坑。。)。 [b]2. 翻页动画[/b] [b]动画思路:[/b] 上面的动画属于转场动画的一种,所以我们可以利用[code]CATrasition[/code]进行动画,[code]CATransition[/code]的使用非常简单,只要设置动画时长,时间函数,[code]fillMode[/code]等,就可以得到想要的动画,[code]CATransition[/code]的[code]type[/code]代表的是过渡时候的动画效果,[code]subType[/code]一般代表动画的方向,但是查看了一下[code]CATransition[/code]的[code]type[/code]属性,官方文档里面只描述了下面四种预定义的转场动画效果:
NSString * const kCATransitionFade;
NSString * const kCATransitionMoveIn;
NSString * const kCATransitionPush;
NSString * const kCATransitionReveal;
我们需要的翻页动画并不在里面,在google了一下之后,找到了一个比较理想的效果,通过直接设置[code]CATransition[/code]的[code]type[/code]为"[code]pageCurl[/code]"或"[code]pangeUnCurl[/code]"进行动画,这两个值官方文档没有提供,我也不知道为啥这些大神能找到。。。 但是默认的朝上翻页只有左上角方向的动画,朝下翻页只有右下角方向的动画 [b]做出来的效果如下图:[/b] [img]http://files.jb51.net/file_images/article/201608/20168490741177.gif?2016749759[/img] 无法达到四个对角都能进行翻页动画的效果。 为了得到可以沿着四个对角方向翻页的效果,我们可以先在最底部添加一个[code]containerView[/code],然后在[code]containerView[/code]中添加[code]dayView[/code](下面提到的[code]dayView[/code]和代码中的[code]dayView[/code]都代表的是作为日历的[code]collectionView[/code])。 如果要进行朝右上角翻页,我们只要把[code]containerView[/code]的[code]layer[/code]先沿[code]y轴[/code]翻转[code]M_PI[/code],这样,最开始的右下角就变成左下角了,翻页时就会变成向右上角翻页 但是为了日历显示正确,我们需要把[code]dayView[/code]的[code]layer[/code]重新翻转过来,这样,[code]containerView[/code]是反的,但是我们看到的日期显示是正的 左下角翻页也是同样的道理。 [b]具体代码如下:[/b]
//为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"   //往上翻页的的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进行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 = 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
  let 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 = false
    animationContainerView.layer.transform = CATransform3DIdentity
    dayView.layer.transform = CATransform3DIdentity
    dayView.layer.removeAnimationForKey("pageCurl")
  }
}
[b]总结:[/b] 这篇文章没有介绍太多详细的内容,其实翻页的动画实现还有别的方法,但是因为我想实现的是可以沿着四个对角进行动画的效果,所以最终选择了这个方法,上面说的好像不是很具体,如果不是很明白,可以先查看一下CATranstion的使用方法。以上就是本文的全部内容,希望对大家开发IOS动画的时候能有所帮助。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部