第五篇 swiftUI Landmarks 动画

2,645 阅读6分钟

动画视图和过渡

使用SwiftUI时,无论效果在哪里,您都可以单独为视图或视图状态的更改添加动画效果。SwiftUI为您处理这些组合、重叠和可中断动画的所有复杂性。

在本教程中,您将为包含图表的视图制作动画,该视图用于跟踪用户在使用Landmarks应用程序时徒步旅行。使用animation(_:)修饰符,您将看到为视图制作动画是多么容易。

下载初学者项目并遵循本教程,或打开已完成的项目并自行探索代码。

第一节 将徒步旅行数据添加到应用程序中

在添加动画之前,您需要一些动画。在本节中,您将导入和建模徒步旅行数据,然后添加一些预构建的视图,以便在图表中静态显示该数据。

第一步 添加资源文件

下载初学者项目,在Resources文件下找到hikeData.json,拖入您的工程Resources目录下

第二步 创建模型Hike.swift

使用菜单项文件>新建>文件,在项目的模型组中创建一个名为Hike.swift的新Swift文件。
Landmark结构一样,Hike结构符合Codable,并具有与相应数据文件中的键匹配的属性。

import Foundation
struct Hike: Codable, Hashable, Identifiable {
    var id: Int
    var name: String
    var distance: Double
    var difficulty: Int
    var observations: [Observation]

    static var formatter = LengthFormatter()

    var distanceText: String {
        Hike.formatter
            .string(fromValue: distance, unit: .kilometer)
    }

    struct Observation: Codable, Hashable {
        var distanceFromStart: Double

        var elevation: Range<Double>
        var pace: Range<Double>
        var heartRate: Range<Double>
    }
}

第三步 将hikes数组加载到您的模型对象中。

因为您在最初加载后永远不会修改徒步旅行数据,因此您无需使用@Published属性对其进行标记。

final class ModelData: ObservableObject {
   @Published var landmarks:[Landmark] = load("landmarkData.json")
    var hikes: [Hike] = load("hikeData.json")
}

第四步 添加Hikes资源文件

Hikes文件夹从下载文件的资源文件夹拖到项目的视图组中。在单击完成之前,请务必选择“如果需要复制项目”和“创建组”。

第五步 预览

HikeView.swift中,打开实时预览,并尝试显示和隐藏图表。

请务必在整个教程中使用实时预览,以便您可以尝试每个步骤的结果。

第二节 给视图添加动画

在等价视图上使用animation(:)修改器时,且视图的可动画属性发生变化时,swiftUI会生成一个动画。
视图的颜色、不透明度、旋转、大小和其他属性都可以设置动画。当视图不等价,可以使用animation(
:value:)修改器在指定值更改时启动动画。

第一步 按钮旋转时添加动画

HikeView.swift中,通过添加一个动画修饰器,当showDetail的值发生变化时,按钮开始旋转,

.animation(.easeInOut, value: showDetail)

第二步 按钮添加放大动画

当图形可见时,给按钮添加一个放大动画

.scaleEffect(showDetail ? 1.5 : 1.0)

第三步 更改动画类型

把动画类型easeInOut改成 spring().

.animation(.spring(), value: showDetail)

swiftUI包括预定义或自定义的基础动画,以及弹簧和流体动画。可以调整动画的速度,在动画开始前设置延迟,或者指定动画重复

第四步 关闭旋转动画

scaleEffect修饰器上方再添加一个动画修饰器,动画类型设置成nil

.animation(nil, value: showDetail)

第五步 移除动画修饰器

在进行下一步之前,先移除之前添加的两个动画修饰器

第三节 状态变化时生成动画

现在您已经学会了如何给某个视图添加动画,是时候根据状态变化添加动画了。
在这里,您将对用户点击按钮并切换showDetail状态属性时发生的所有更改应用动画。

第一步 使用withAnimation

用withAnimation函数包装showDetail.toggle()的调用。 受showDetail属性影响的两个视图(按钮和HikeDetail视图)现在都有动画过渡。

第二步 设置动画时长

传递一个4秒时间的基础动画,您可以传递和animation(_:value:)相同的动画

第三步 在动画的过程中,尝试打开和关闭图形视图

在进行下一步之前,先清除withAnimation的参数

第四节 自定义转场动画

默认情况下,视图的转场效果是渐隐渐现的,您可以通过transition(_:)修饰器来自定义转场动画

第一步 给HikeView.swift 添加一个transition(_:)修饰器

if showDetail { HikeDetail(hike: hike) .transition(.slide)

现在图形展示和消失都会有滑动效果了

第二步 提取转场代码

提取刚刚添加为AnyTransition静态属性的转场,并在视图的转场修改器中访问新属性。
当您扩展自定义转场时,这将使您的代码尽可能的干净

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        AnyTransition.slide
    }
}

第三步 切换slide->move(edge:)

使用slide图形从左侧滑入屏幕,右侧滑出屏幕, 将slide换成move(edge:),使视图滑入滑出都在左侧

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        AnyTransition.move(edge: .trailing)
    }
}

第四步 使用非对称修饰器 asymmetric(insertion:removal:)

当视图显示和消失时,使用asymmetric(insertion:removal:)可以提供不同的转场动画。比如:
显示时,需要从右侧进入,且伴随着透明度逐渐显示。
消失时,需要缩小,且伴随着透明度逐渐消失。

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        .asymmetric(
            insertion: .move(edge: .trailing).combined(with: .opacity),
            removal: .scale.combined(with: .opacity))
    }
}

第五节 为复杂的效果组合动画

单击条形图下方的按钮时,图形会在三组不同的数据之间切换。在本节中,您将使用合成动画为构成图形的胶囊提供一个动态的波纹过渡。

第一步 修改showDetail为true

在HikeView中,将showDetail的默认值更改为true,然后将预览固定到画布上。 这样,当您在另一个文件中处理动画时,就可以在上下文中看到图形。

第二步 在HikeGraph.swift中,定义一个新的波纹动画,并将其应用于每个生成的图形胶囊。

extension Animation { static func ripple() -> Animation { Animation.default }}

第三步 将动画切换到弹簧动画,减少阻尼分数以使钢筋跳跃。

通过在实时预览中切换仰角、心率和速度,可以看到动画的效果。

extension Animation {
    static func ripple() -> Animation {
        Animation.spring(dampingFraction: 0.5)
    }
}

第四步 将动画加速一点,以缩短每个条移动到其新位置所需的时间。

第五步 根据位置添加延迟

extension Animation {
    static func ripple(index: Int) -> Animation {
        Animation.spring(dampingFraction: 0.5)
            .speed(2)
            .delay(0.03 * Double(index))
    }
}

第六步 观察效果

观察自定义动画在图形之间转换时如何提供波纹效果。 在继续下一个教程之前,请确保取消固定预览,即showDetail改为false