Transitions
在前两章中,您学习了如何基于视图的动画属性(如位置和alpha)创建动画。但是,如何处理想要增加或删除视图的情况呢?
您可以使用前面章节中的方法,并在界面中进出视图。不过,本章将向您展示如何使用转换为视图的任何一组更改设置动画
转场是可以应用于视图的预定义动画。这些预定义的动画不会尝试在视图的开始和结束状态之间进行插值。相反,您将设计动画,以便各种状态的变化显得自然
一: Transitions实例
为了更好地理解何时使用过渡动画,本节将向您介绍可以使用Transitions
动画的各种动画场景
添加一个视图
要在屏幕上添加一个新视图的动画,可以调用与前面章节中使用的方法类似的方法。这次的区别在于你会选择一个预定义的过渡效果,并为所谓的动画容器视图设置动画效果
该过渡为容器视图添加了动画,并且在动画运行时,添加到视图的任何新视图都以子视图的形式出现
为了更好地解释如何为容器视图设置动画,以及何时执行子视图之间的转换,请考虑以下代码片段:
var animationContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
//set up the animation container
animationContainerView = UIView(frame: view.bounds)
animationContainerView.frame = view.bounds
view.addSubview(animationContainerView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//create new view
let newView = UIImageView(image: UIImage(named: "banner"))
newView.center = animationContainerView.center
//add the new view via transition
UIView.transition(with: animationContainerView,
duration: 0.33,
options: [.curveEaseOut, .transitionFlipFromBottom],
animations: {
self.animationContainerView.addSubview(newView)
},
completion: nil
)
}
在这种假设的情况下,您在视图控制器的viewDidLoad()
中创建一个名为animationContainerView
的新视图。 然后您定位并将此容器添加到视图
稍后,当您要创建动画过渡时,您将创建一个新的视图来进行动画处理; 这里叫做newView
。
要创建转换,请调用transition(with:duration: options:animations:completion:)
。 这和标准的UIView动画方法几乎是一样的,但是在这种情况下,你提供了一个额外的参数视图,它作为过渡动画的容器视图
这里有一个新的动画选项.transitionFlipFromBottom
,你还没有看到。 这是本章介绍中讨论的预定义转换之一. .transitionFlipFromBottom
翻转视图的底部边缘作为视图翻转的“hinge”
最后,你在你的动画块中的动画容器中添加一个子视图,这会导致子视图在转换过程中出现
预定义过渡动画选项的完整列表如下所示:
- .transitionFlipFromLeft
- .transitionFlipFromRight
- .transitionCurlUp
- .transitionCurlDown
- .transitionCrossDissolve
- .transitionFlipFromTop
- .transitionFlipFromBottom
移除视图(Removing a view)
使用过渡动画从屏幕中删除子视图非常类似于添加子视图。要使用过渡动画完成此操作,只需在动画闭包表达式中调用
removeFromSuperview()
就可以了
//remove the view via transition
UIView.transition(with: animationContainerView, duration: 0.33,
options: [.curveEaseOut, .transitionFlipFromBottom],
animations: {
self.newView.removeFromSuperview()
},
completion: nil
)
和前面的例子一样,包装器转换将执行翻转动画,newView将在所有的结尾消失。
隐藏和展示视图(Hiding/showing a view)
[站外图片上传中...(image-7401e1-1512631213095)]
到目前为止,在本章中,您只学习了改变视图层次结构的转换。 这就是为什么你需要一个容器视图的过渡 - 这使得层次结构的变化在上下文中
相比之下,您不需要担心设置容器视图来隐藏和显示观点。 在这种情况下,转换使用视图本身作为动画容器
考虑下面的代码来使用转换来隐藏子视图:
//hide the view via transition
UIView.transition(with: self.newView, duration: 0.33,
options: [.curveEaseOut, .transitionFlipFromBottom],
animations: {
self.newView.isHidden = true
},
completion: nil
)
在这里你传入你想要显示或隐藏的视图作为转换的第一个参数transition(with:duration:options:animations:completion:)
。 你之后所做的就是在动画块中设置你的视图的isHidden属性,并且瞧,过渡动画开始
替代视图(Replacing a view with another view)
用另一个视图替换一个视图也是一个简单的过程。您只需传入现有视图作为第一个参数,并将toView:
参数设置为您希望替换的视图,如下所示
//replace via transition
UIView.transition(from: oldView, to: newView, duration: 0.33,
options: .transitionFlipFromTop, completion: nil)
UIKit为您提供了多少重要的功能?
在本章的其余部分中,您将通过转换来展示和隐藏UI元素,并学习一些可以引入自己的项目的新动画技巧
二: (混合转场)Mixing in transitions
您将继续在本章中的Bahama Air登录屏幕项目上工作; 你已经创建了一些引人注目的动画在这个屏幕上的意见,添加一点点的登录表单,并使按钮反应被挖掘
接下来,您将模拟一些用户身份验证并为几个不同的进度消息添加动画。一旦用户点击登录按钮,您将显示消息,包括“Connecting...”,“Authorizing...”和“Failed”
如果您还没有完成前面的章节,您可以从本章的资源文件夹中的入门项目开始。如果你已经在自己的项目的最后几章中的例子,很好的工作! 您可以继续使用现有的项目
打开ViewController.swift
并查看viewDidLoad()
,这种方法的一部分添加了一个隐藏的图像视图存储在类变量状态。代码然后创建一个文本标签,并将其作为子视图添加到状态
您将使用状态向用户显示进度消息。 消息来自消息数组,这是starter
项目中包含的另一个类变量。
将以下方法添加到ViewController
func showMessage(index: Int) {
label.text = messages[index]
UIView.transition(with: status, duration: 0.33,
options: [.curveEaseOut, .transitionCurlDown],
animations: {
self.status.isHidden = false
},
completion: {_ in
//transition completion
} )
}
这个方法带有一个名为index
的参数,你可以使用它来将label
的值设置为基于index
的消息内容
上面还有一个新的动画选项:.transitionCurlDown
,这种过渡使得视图像一张纸在合法的平板上被翻转一样,看起来像下面一样
现在是时候锻炼你的新showMessage(index :)
方法。 在login()
中找到以下代码块:
animations: {
self.loginButton.bounds.size.width += 80.0
}, completion: nil)
这是您现有的动画块,当用户点击它时,会使登录按钮反弹。 你会添加一些新的东西 - 一个完成封闭 - 这个动画,调用showMessage(index:)
.
将完成的nil参数值替换为以下闭包表达式:
completion: { _ in
self.showMessage(index: 0)
}
闭包需要一个Bool参数,它告诉你动画是否成功完成,或者在动画完成之前被取消
注意:上面的完成关闭具有完成的单个参数。因为你不关心它是否完成,Swift允许你跳过绑定参数,把“_”放在它的位置。
在闭包中,只需调用索引为0的showMessage
即可显示消息数组中的第一条消息
建立并运行你的项目; 点击登录按钮,你会看到状态标题出现你的第一个进度信息
[站外图片上传中...(image-e206e0-1512631213095)]
你看到横幅像一张纸一样卷起来了吗? 这是一个非常好的方式来关注通常只显示为静态文本标签的消息。
注意:你的一些动画似乎很快运行,不是吗?有时确保动画在正确的位置以正确的顺序发生是非常棘手的。 或者,也许你只是想让事情发生得更慢,所以你可以欣赏效果!要在不改变代码的情况下减慢应用程序中的所有动画,请从
iPhone Simulator
菜单中选择“最前面的应用程序中的调试/切换慢动画”。 现在点击您的应用程序中的登录按钮,享受生动的慢动作的动画和过渡!
对于下一个动画,您首先需要保存横幅的初始位置,以便您可以将下一个横幅放在正确的位置。
将以下代码添加到viewDidLoad()
的末尾,将横幅的初始位置保存到名为statusPosition
的属性中:
statusPosition = status.center
现在,您可以开始设计视图动画和过渡的混合。
添加以下方法通过标准从屏幕上删除状态消息动画:
func removeMessage(index: Int) {
UIView.animate(withDuration: 0.33, delay: 0.0, options: [],
animations: {
self.status.center.x += self.view.frame.size.width
},
completion: { _ in
self.status.isHidden = true
self.status.center = self.statusPosition
self.showMessage(index: index+1)
}
) }
在上面的代码中,您可以使用您的老朋友animate(withDuration:delay:options:animations:completion:)
将状态移动到屏幕可见区域之外。
当动画在完成关闭时完成时,将状态移回原始位置并隐藏。 最后你再次调用showMessage
,但这次你传递下一条消息的索引来显示
将标准动画与转换相结合非常容易:只需调用相应的API,UIKit就会调用背景中相应的Core Animation
位
现在,您需要完成showMessage
和removeMessage
之间的调用链,以模拟真实的身份验证过程
找到showMessage(index :)
并用下面的代码替换注释//转换完成:
delay(2.0) {
if index < self.messages.count-1 {
self.removeMessage(index: index)
} else {
//reset form
} }
一旦转换完成,你等待2.0秒,并检查是否有任何剩余的消息。如果是这样,通过removeMessage(index :)
删除当前消息。 然后,您可以在removeMessage(index :)
的完成块中调用showMessage(index :)
来按顺序显示下一条消息。
注意:
delay(_:completion :)
是一个方便的函数,在经过延迟后运行一段代码。 它在ViewController.swift
的顶部定义。 在这里你用它来模拟通常的网络访问延迟。
再次构建并运行您的项目; 享受由此产生的动画序列,其如此更新认证进程消息
[站外图片上传中...(image-e6c9b9-1512631213095)]
转换是动画知识的一个小而重要的子集,以保留在你的比喻工具箱中,因为它们是在UIKit中创建3D风格动画的唯一方法。
如果您期待学习更精细的3D效果,您将有机会在第六部分“3D动画”中详细讨论Core
在进入下一部分之前,请尝试尝试本章中的挑战。既然你在前三章学到了很多关于动画的知识,一个挑战是不够的 - 我给了你三个Animation和3D图层转换。
这些挑战使您有机会在Bahama Air登录屏幕上完成开发工作 - 同时也是您面临的第一个挑战。活泉!
#######挑战
挑战一
到目前为止,您只能看到其中一个内置的过渡动画。 你不是很好奇看到别人是什么样子吗?
在这个挑战中,你可以尝试所有其他可用的过渡动画,并使用你最喜欢的动画进度信息横幅
打开ViewController
并在showMessage(index :)中找到指定过渡动画的行
.transitionCurlDown`
UIView.transitionWithView(status, duration: 0.33, options:
[.curveEaseOut, .transitionCurlDown], an
将.transitionCurlDown
替换为其他任何可用的过渡动画,然后构建并运行项目以查看它们的外观。 以下是可用转换的列表:
.transitionFlipFromLeft
.transitionFlipFromRight
.transitionCurlUp
.transitionCurlDown
.transitionCrossDissolve
.transitionFlipFromTop
.transitionFlipFromBottom
哪一个你认为在这个屏幕上的其他动画效果最好?
如果你没有最爱,请尝试我的最爱过渡:.transitionFlipFromBottom
。 我认为它非常适合横幅图形:
挑战二
对于这个挑战,您可以通过撤消一旦点击登录按钮后运行的所有动画,将表单重置为初始状态。 这样,如果登录失败,当用户再次点击登录按钮时,他们会看到所有的动画再次发生
以下列出了完成这一挑战所需的一般步骤:
- 1:创建一个新的空方法resetForm()并从你的代码中调用它占位符评论//重置表单的生命。
- 2:在
resetForm()
中使用transition(with:duration:options:animations:completion :)
将状态的可见性设置为隐藏和居中为self.statusPosition
。 这应该重置旗帜到其初始状态。 使用0.2秒的时间进行转换。 - 3: 如果隐藏横幅的转换使用与显示横幅的动画完全相反的动画,那将会很好。 例如,如果您通过
.transitionCurlDown
显示横幅,则使用.transitionCurlUp
将其隐藏。 相反
.transitionFlipFromBottom
将是.transitionFlipFromTop
...等等 - 4: 接下来,在
resetForm()
中添加一个对animate(withDuration:delay:options:animations:completion :)
的调用。 在动画闭包模块中进行以下调整- 将
self.spinner
(“登录”按钮内的活动指示器)移动到(-20.0,16.0)的原始位置, - 将
self.spinner
的alpha
属性设置为0.0以隐藏它。 - 将“登录”按钮的背景颜色调回原始值:
UIColor(red: 0.63, green: 0.84, blue: 0.35, alpha: 1.0).
- 继续重置对“登录”按钮的所有更改,然后减小由80.0点的
bounds.size.width
属性 - 最后,将该按钮移回密码字段下的原始位置减少60.0点
- 将
如果您在身份验证过程中精确地颠倒了所有的动画,屏幕一旦所有的认证信息都显示出来,
[站外图片上传中...(image-c2bfe3-1512631213095)]
做得好! 而现在这一章的über挑战...
[站外图片上传中...(image-571d0-1512631213095)]
关于挑战:在背景中动画云彩
如果背景中的那些云在屏幕上慢慢地移动,并从另一侧再次出现,这不是很酷吗?
是的,这将是非常酷 - 这就是你的挑战!
四个云图像视图已经连线到ViewController
的四个插座,所以你很好走。 你可以尝试使用你自己新发现的过渡动画知识来自己动手,或者你可以按照下面的配方
- 1: 创建一个签名为
animateCloud(cloud:UIImageView)
的新方法,并在其中添加代码。 - 2: 首先,计算平均云速度。 假设云应该在大约60.0秒内穿过屏幕的整个长度。 调用常量
cloudSpeed
并将其设置为60.0/view.frame.size.width
- 3: 接下来,计算动画将云移动到屏幕右侧的持续时间。 请记住,云不是从屏幕的左边缘开始,而是从随机点开始。 你可以计算正确的持续时间,通过考虑云需要遵循的路径的长度,并将结果乘以平均速度:`(view.frame.size.width - cloud.frame.origin.x)*cloudSpeed
- 4: 然后调用
animate(withDuration:delay:options:animation:completion :)
以上面刚计算的持续时间。 您需要从中创建TimeInterval的实例,因为编译器不会为您确定正确的类型:TimeInterval(duration)
。 对于选项参数使用.curveLinear
; 这是少数几次使用动画而没有缓动的情况之一。 云层背景自然很深,所以他们的运动看起来应该是平坦的。 - 5: 在动画闭包表达式中,将云的frame.origin.x属性设置为self.view.frame.size.width。 这将云移动到屏幕区域之外。
- 6: 在完成关闭块内,将云从当前位置移动到屏幕另一边的外部。 不要忘记像本章前面所做的那样,使用“_”来跳过闭包参数。 要正确定位云,请将其
frame.origin.x
设置为-cloud.frame.size.width
。 - 7: 仍然在完成关闭工作,添加一个调用
animateCloud()
,以便云重新在屏幕上动画。 - 8: 最后,将以下代码添加到
viewDidAppear()
的末尾,以启动所有四个云的动画
animateCloud(cloud1)
animateCloud(cloud2)
animateCloud(cloud3)
animateCloud(cloud4)
这应该使所有四个云彩慢慢地穿过屏幕创造一个好,不引人注目的作用。
如果您完成了本章的挑战,恭喜! 他们很难!:]
过去几章有很多信息需要消化,但是你采取了一种僵硬的,静态的登录表单,并将其转化为对用户的引人注目和有趣的体验:
[站外图片上传中...(image-decc0f-1512631213095)]
现在是时候从新材料中稍微休息一下,把所有的视图动画知识加入测试! 在下一章中,您将使用各种各样的实际操作来为Bahama Air应用程序添加一些严肃的优雅的动画。