iOS图书动画

Posted 3行代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS图书动画相关的知识,希望对你有一定的参考价值。

<>

欢迎回到ios图书动画系列教程!在第一部分,我们学习了如何创建两个自定义的collection view layout并在图书书页中使用了阴影图层以使我们的App显得更加立体和真实。
在这一部分,我们将学习如何创建自定义的转场动画并通过捏放手势来打开一本书。

注意:感谢Attila Hegedüs创建了本教程的示例程序。

开始

本教程以前一部分的内容为基础。如果你还没有看过第一部分,或者想从一个新项目开始,你可以在这里下载上一部分教程中的完整示例程序。

技术分享

在Xcode中打开项目。现在,你可以选择一本书进行阅读,并从右边滑动进行翻页。这时的转场动画使用的是UINavigationController自带的动画效果。通过本教程的学习,我们将自定义这个动画效果,如下图所示:

技术分享

这个动画会在“打开书”和“合起书”两个状态之间一一种更加自然的方式平滑过渡,这将更能获得用户的欢心。让我们马上开始吧!

创建自定义的导航控制器

要创建自定义的push动画和pop动画,我们必须创建自定义导航控制器并实现UINavigationControllerDelegate协议。
在App文件夹上右击(或ctrl+左键)并点击New File。选择iOS\Source\Cocoa Touch Class模板并将类名设置为CustomNavigationController。让它继承自UINavigationController并将语言设置为Swift。点击Next,Create。
打开CustomNavigationController.swift,编辑其内容为:

上述代码分别解释如下:

  1. 在 viewDidLoad 方法中,设置CustomNavigationController的delegate属性为它自己。
  2. navigationController(_:animationControllerForOperation:fromViewController:toViewController:) 方法属于UINavigationControllerDelegate协议。这个方法在两个View Controller之间发生push或pop导航时调用。你可以在这个方法中分别针对push导航和pop导航返回各自的Transition对象。目前我们都返回了nil,这表明我们将使用UINavigationController内置的标准Transition。稍后我们将替换为自己的Transition对象。

现在我们拥有了自己的Navigation Controller类,接下来在故事板中将默认的UINavigationController替换为我们的CustomNavigationController。
打开Main.storyboard,在故事板编辑器左侧的对象窗口中选择Navigation Controller对象,打开Identity窗口,在Custom Class下,将Class由UINavigationController修改为CustomNavigationController,如下图所示:

技术分享

编译运行,什么变化都没有发生。这是因为在委托方法中我们仍然返回了nil,因此使用的仍然是UINavigationController内置的标准Transition。

创建自定义导航动画

最有趣的部分来了——创建我们的自定义Transition对象!:]
要自定义Transition类,我们必须实现UIViewControllerAnimatedTransitioning协议,最主要的是这几个方法:

  • transitionDuration: 必须实现。这个方法返回一个动画时长,使两个动画的播放时间相同(或不同)。
  • animateTransition: 必须实现。这个方法负责提供参与动画的两个控制器:一个to控制器,一个from控制器。Transiton的大部分工作在这个方法中完成。
  • animationEnded: 可选的方法。这个方法主要是用来通知你什么时候动画完成了。我们可以在这个方法中进行必要的清理动作。

创建自定义Transition类

在App文件夹上右击(或ctrl+左键),然后点击New File。选择iOS\Source\Coca Touch Class 模板,将类名设置为BookOpeningTransition,继承NSObject,语言Swift。然后点击Next,Create。
打开BookOpeningTransition.swift,编辑代码如下:

以上代码对应注释中的编号,分别解释如下:

  1. 声明 BookOpeningTransition 类实现UIViewControllerAnimatedTransitioning 协议。
  2. 声明一个 transforms 字典,键存储UICollectionViewCell,值则存储对应的CATransiform3D。这个字典保存在书打开后所有cell的翻页动画。
  3. 指定to视图控制器的背景色,它将让淡出淡入动画看起来更加清楚。
  4. 布尔值isPush 用于标识当前动画是Push动画还是Pop动画。
  5. 增加必须实现的 UIViewControllerAnimatedTransitioning 协议方法,以使编译错误不再出现,稍后我们会实现这些方法。

定义好需要的变量,接下来就是实现协议方法。
首先是transitionDuration(_:)方法:

transitionDuration(_:)方法返回了动画播放时长。这里,无论是Push动画还是Pop动画我们都设置为1秒。通过这个方法我们可以很方便地改变Push动画或Pop动画的时长。

然后,是第二个协议方法——animateTransition——这是最核心的部分!:]我们将这个方法分成两部分来介绍:

  1. 实现一个助手方法,用于创建Push动画所需的Transition对象。
  2. 实现一个助手方法,用于创建Pop动画所需的Transition对象。

创建Push动画

回想你在生活中打开一本书的样子:

技术分享

虽然看起来复杂,但我们只需要考虑两个状态,同时让UIView的animateWithDuration方法根据这两个状态进行不同的处理:

  1. 状态1,书处于合起状态。
  2. 状态2,书处于打开状态;这就是我们在第一部分教程中实现的部分。

首先,在实现animateTransition(:_)协议方法之前,我们来实现几个助手方法。
仍然在BookOpeningTransition.swift中,编写如下方法:

这个方法返回了一个Transform对象,并在z轴上增加了一点立体感。在播放动画时,我们将用到这个方法。

状态 1 – 合起书

在makePerspectiveTransform方法后实现如下方法:

func closePageCell(cell : BookPageCell) {
// 1
var transform = self.makePerspectiveTransform()
// 2
if cell.layer.anchorPoint.x == 0 {
// 3
transform = CATransform3DRotate(transform, CGFloat(0), 0, 1, 0)
// 4
transform = CATransform3DTranslate(transform, -0.7 * cell.layer.bounds.width / 2, 0, 0)
// 5
transform = CATransform3DScale(transform, 0.7, 0.7, 1)
}
// 6
else {
// 7
transform = CATransform3DRotate(transform, CGFloat(-M_PI), 0, 1, 0)
// 8
transform = CATransform3DTranslate(transform, 0.7 * cell.layer.bounds.width / 2, 0, 0)
// 9
transform = CATransform3DScale(transform, 0.7, 0.7, 1)
}

}

回想一下BookViewController,它是一个CollectionView,代表了书中的一页。我们将每一页和书脊对齐,以y轴为心进行旋转实现翻页效果。首先,书是合起(关闭)的。这个方法将每个cell(即书页)放平并置于封面的下面。
这是动画运行效果:

技术分享

以上代码解释如下:

  1. 用前面创建的助手方法,生成一个Transform对象。
  2. 判断cell是否是右侧页。
  3. 如果是,设置其角度为0,即放置为水平。
  4. 将它移动到封面下方并居中对齐。
  5. 将它缩放为70%。还记得我们前面将封面也缩放为70%吗?这里是同样的意思。
  6. 如果cell不是右侧页,则就是左侧页。
  7. 设置左侧页的角度为180度。要将它水平放置,我们需要将它翻到书脊的右边。
  8. 将它放到封面下方并居中对齐。
  9. 缩放70%。
  10. 最后,赋给cell的transform属性。

Now add the following method below the one you added above:

在上面的方法后添加如下方法:

setStartPositionForPush(_:toVC:)方法创建状态1的Transition。这个动画涉及到两个ViewController:

  • fromVC,类型为BooksViewController,用于滚动浏览图书列表。

  • toVC,BookViewController类型,让你可以翻阅选定的书。

以上代码解释如下:

  1. 保存BooksViewController的Cellection View的背景色,然后设置BookViewController的Collection View的背景色为nil。
  2. 隐藏封面。现在toVC将负责处理封面图片的显示。
  3. 遍历书中所有书页。
  4. 保存每一页的当前Transform到transforms字典。
  5. 由于书一开始是合起的,我们将该页转换为合起状态,然后更新阴影图层。
  6. 最后,忽略封面图片的阴影。

状态 2 – 打开书

现在,状态1的动画完成了,我们可以转移到状态2的处理中来。在这里我们将一本合起的书转换成一本打开的书。在setStartPositionForPush(_:toVC:)方法下添加如下方法:

上述代码解释如下:

  1. 隐藏所有书的封面,因为接下来我们要显示所选图书的内容。
  2. 在BookViewController中遍历书中每一页并读取先前保存在transform数组中的的Transform。

在从BooksViewController导航到BookViewController后,我们还需要进行一些清理工作。
在上面的方法之后加入如下方法:

在Push完成时,我们将BookViewController的Collection View的背景色设回原来保存的颜色,隐藏位于它下面的内容。

实现打开书的动画

现在我们已经实现了助手方法,接下来要实现Push动画了!
在空的animateTransition(_:)方法中加入以下代码:

以上代码解释如下:

  1. 获取Container View,Container View在两个View Controller发生转场时充当父视图的角色。
  2. 判断当前的转场动作是否是一个Push动作。
  3. 如果是,分别获取fromVC(BooksViewController)和toVC(BookViewController)。
  4. 将toVC(BookViewController)加到Container View。
  5. 设定Push动作的起止点,即toVC和fromVC。
  6. 开始动画。从起始点(书合起的状态)转变到终点(书打开状态)。
  7. 执行清理动作。
  8. 告诉系统,转换完成。

在Navigation Controller中应用Push动画

现在我们已经创建好Push动画,接下来就是将它应用到自定义的Navigation Controller中了。
打开BooksViewController.swift在类声明中增加属性: