动画时长(Animation Duration)

动画时长是指动画播映的「秒」数。到目前为止,您看到的所有动画代码都运用了默许的动画时长(大约是 0.4 秒左右)。在本节中,咱们将展示如何在动画中设置不同的动画时长。比方运用.linear设置匀速:

View.animation(.linear, value: change) //匀速,默许大约 0.4 秒左右
View.animation(.linear(duration: 2), value: change) //2 秒
View.animation(.linear(duration: 5), value: change) //5 秒

Option:定制化你的动画

除了匀速(linear),之前的章节中,还简介了比方easeIneaseOuteaseInOut等类型,它们都包含一个duration参数,这个参数决议了动画从开端到结束的时间。

Option:定制化你的动画

一旦设置了duration,不论动画类型是「匀速」,仍是曲线「加快」或「减速」,终究的履行时间都是相同(duration)的。如下例:

长时动画很合适作为开屏动画或者某个重要版块的开幕介绍,比方:

Option:定制化你的动画

动画距离(Animation Delay)

到目前为止,在本书中,动画都是在被触发后立即开端的。咱们能够经过在运用的动画中增加「距离」办法来控制动画的开端时机,这是让一个动画在另一个动画之后发生的好办法,比方:

Option:定制化你的动画

首要实现一个动画时长 2 秒的幕布动画:

GeometryReader(content: { geometry in
	HStack(spacing: 0) {
		//左幕布
		Rectangle()
			.fill(.green)iiii'ii'i'ii'ii
			.offset(x: changed ? -geometry.size.width / 2 : 0)//向左移动(隐藏)
		//右幕布
		Rectangle()
			.fill(.green)
			.offset(x: changed ? geometry.size.width / 2 : 0)//向右移动(隐藏)
	}.animation(.easeIn(duration: 2), value: changed)//动画时长 2 秒
})

然后将「文字」(Hello SwiftUI)「推迟」 2 秒后扩大(恰好幕布打开):

Text("Hello SwiftUI")
	.frame(width: 300, height: 100)
	.font(changed ? .largeTitle : .body)
	.foregroundStyle(changed ? .black : .gray)
	.fontWeight(changed ? .black : .ultraLight)
	.animation(.easeIn.delay(2), value: changed) // 推迟 2 秒后履行

Delay_Intro.swift

Option:定制化你的动画

推迟办法需要在动画类型(比方lineareaseIn等)后增加.delay()润饰符即可,它不能作为一个独立的参数来传入.animate润饰符,要依托「前置」的动画类型,它的入参是TimeInterval类型,单位是秒(s)。

「动画距离」常用于「组合」动画「顺次」展示的场景,比方设置一个倒计时:

ZStack {
	Image(systemName: "1.circle")
		.font(.system(size: 144))
		.background()
		.opacity(change ? 0 : 1)
		.animation(.linear.delay(3), value: change)//3 秒后履行
	Image(systemName: "2.circle")
		.font(.system(size: 144))
		.background()
		.opacity(change ? 0 : 1)
		.animation(.linear.delay(2), value: change)//2 秒后履行
	Image(systemName: "3.circle")
		.font(.system(size: 144))
		.background()
		.opacity(change ? 0 : 1)
		.animation(.linear.delay(1), value: change)//1 秒后履行
}

Option:定制化你的动画

重复(Repeat)

前面现已学习了许多触发动画的办法。当动画播映时,它只「播映一次」。可是,有一种办法能够让动画「重复播映」,不论是设定「次数」重复仍是「无限」重复。

次数重复(Repeat Count)

//重复 3 次
View.animation(.easeIn(duration: 1).repeatCount(3), value: changed)

Option:定制化你的动画

AnimationOption_RepeatCount_Intro.swift

那么怎么了解动画的屡次重复呢?

比方上例中的重复 3 次,咱们将过程分化如下:

Option:定制化你的动画

在「第二次」动画中,呈现了「反转」(reverse)。动画从开始状况「改动」到停止状况假如称之为「正」,那么停止状况「改动」到开始状况则能够称之为「反」。

Option:定制化你的动画

默许情况下,repeatCount办法是「反转的」,假如想要禁止反转,能够传入autoreverses参数并指定为false

View.animation(.easeIn(duration: 1)
   .repeatCount(3, autoreverses: false), value: changed)

Option:定制化你的动画

这样设置后,每次重复都是「开始」到「停止」

Option:定制化你的动画

无限重复(Repeat Forever)

Option:定制化你的动画
repeatFovever不必指定次数,它能够无限重复下去,与repeatCount相似的是,它也默许是「反转」的。

Option:定制化你的动画

声明式语法虽然简洁,可是简洁的背面是杂乱被高度封装,动画时长、动画距离与重复经过「链式」调用的方式能够很容易的组合运用,可是「不同的链次序」也会带来「不同的作用」,比方下例:

View.animation(.linear(duration: 0.2).repeatForever().delay(1), value: changed)
View.animation(.linear(duration: 0.2).delay(1).repeatForever(), value: changed)

Option:定制化你的动画

  • delay(1)放在最终,代表动画会在 1 秒后履行,每个动画的履行时长是 0.2 秒。
  • delay(1)夹在linearrepeatForever之间,代表动画的「每次重复都要推迟 1 秒」才会履行,每个动画的履行时长是 0.2 秒。

动画规模(Animation Scope)

规模(Scope)是指某事涉及的区域巨细。增加动画润饰符的「位置」(链式调用的次序)会影响到哪些润饰符(Modifier)或视图(View)会被「应用」动画。

链式调用

假如把动画润饰符(.animate)放在视图的润饰符链的「最终」,那么所有「改动」的润饰符都会应用动画作用。

Option:定制化你的动画

容器视图

假如把动画润饰符应用在「父视图或容器视图」上,那么所有「子视图」的改动都会带有动画作用。

Option:定制化你的动画

润饰在父视图或容器视图时,即使某个兄弟视图「没有」可改动的特点,假如「布局」发生改动,它也会发生联动的动画,如下例:

VStack {
	//例 1
	HStack {
		//宽度改动;.animation 润饰「子视图」
		Color.orange.frame(width: state * 400).animation(.linear(duration: 3), value: state)
		Color.green
			.overlay {
				Text("没有改动特点")
					.foregroundStyle(.white)
					.font(.title)
			}
	}
	//例 2
	HStack {
		//宽度改动
		Color.orange.frame(width: state * 400)
		Color.green.overlay {
			Text("没有改动特点")
				.foregroundStyle(.white)
				.font(.title)
		}
		//.animation 润饰「父视图」
	}.animation(.linear(duration: 3), value: state)
	//滑块触发动画
	Slider(value: $state)
}.padding()

Option:定制化你的动画

例 2 中,绿色方块没有改动特点,可是橘色方块宽度改动会导致容器视图内的布局也发生改动(橘色宽增,则绿色宽减,反之亦然),由于例 2 中动画润饰符(.animation)润饰在父视图,所以绿色方块也呼应了动画的渐变作用。得益于这个特性,在实际的运用傍边,极大的简化了杂乱视图布局中动画的规划难度。

掩盖

动画场景下,既想要容器视图一劳永逸的快捷(润饰父视图,子视图均生效),又想要个性化的对子视图进行定制(某个子视图有特殊的要求),两者能够兼得吗?

常用 SwiftUI 的开发者对此应该不会生疏,默许情况下,子视图的润饰符默许会掩盖掉父视图的润饰符作用,比方子视图的阶段样式.title的会掩盖父视图的.body

Option:定制化你的动画

动画润饰符也同理,如下例:

VStack {
	Button("开端") {
		changed.toggle()
	}
	Text("父视图动画")
	Circle()
		.fill(.cyan)
		.frame(width: 100)
		.offset(x: changed ? 150 : -150)
		//子:动画润饰符,「掩盖」父
		.animation(.linear(duration: 2), value: changed)
	Text("子视图动画")
	Circle()
		.fill(.green)
		.frame(width: 100)
		.offset(x: changed ? 150 : -150)
	Spacer()
}.font(.title)
	//父:动画润饰符
	.animation(.linear, value: changed)

Option:定制化你的动画

关闭动画

假如想要某个视图,「不呼应」的动画「影响」,能够传入.none来手动禁用动画,如下例:

View.animation(.none, value: changed)