Skip to content

第42天 项目 8 第四部分

你现在已经完成了“月球探索”(Moonshot)项目,这是我们首个开始变得有难度的项目——讲解耗时更长,我们使用了自定义的SwiftUI布局,我甚至还悄悄加入了一些高级Swift特性。并非我们之后所有的项目都会这么难,但未来的项目肯定会更“复杂”:它们包含的内容更多,这意味着最终做出的应用会更有趣,也更能代表真实世界中的应用开发场景。

随着复杂度的提升,出错的概率也会增加,而Swift在这方面相当“严格”——想必你现在已经深有体会,哪怕第20行有一个小小的错误,都可能导致第5行出现一个莫名其妙的错误,这可能会让人灰心丧气。

不过,希望今天的这句话能给你带来启发。我特意为今天挑选了这句话,原因留给读者自行体会,这句话就是:别慌!这类问题很常见,目前解决它们最简单的方法就是把你最近添加的代码注释掉,一直这样做,直到代码能正常运行。之后你可以慢慢重新加入代码,直到找到导致编译失败的部分,然后修复它。

所以,别慌——你能做到的!

今天你需要完成第8个项目的总结章节,完成相关复习,然后攻克它的三个挑战。

  • 月球探索:总结
  • 第8个项目“月球探索”复习

月球探索:总结

作者:Paul Hudson 2023年11月1日

这个应用是我们目前构建过的最复杂的应用。没错,它包含多个视图,但我们也跳出了列表(lists)和表单(forms)的范畴,开始使用我们自己的滚动布局,通过containerRelativeFrame()来获得精确的尺寸,从而充分利用可用空间。

但这也是我们目前编写过的最复杂的Swift代码——泛型(generics)是一项极具威力的特性,一旦加入约束条件,你就能开启大量功能,既能节省时间,又能提升灵活性。

你现在也开始体会到Codable的实用性了:它能一次性解码层级化的数据,这一能力非常宝贵,这也是它成为众多Swift应用核心组件的原因。

复习所学内容

任何人都能看完教程在教程中,但要记住所学的知识,就需要实际付出努力了。我的职责是确保你能从这些教程中收获尽可能多的知识,完成后面的练习题,可以帮助你检验自己的学习成果。

挑战

学习编程最好的方法之一就是尽可能多地自己写代码,所以这里有三种三种扩展这个应用的方式,帮助你确保完全理解其中的原理。

  1. MissionView中添加发射日期,放在任务徽章下方。考虑到可用空间更大,你可以选择不同的格式来显示日期,具体方式由你决定。
  2. 将一到两段视图代码提取到新的SwiftUI视图中——MissionView中的水平滚动视图就是一个很好的选择,如果你遵循了我的样式设计,也可以将Rectangle分隔线提取出来。
  3. 作为一个有难度的挑战,在ContentView中添加一个工具栏项,用于切换任务的显示方式(网格视图或列表视图)。

提示:对于最后一个挑战,最佳做法是将所有网格代码和列表代码分别做成两个独立的视图,然后在ContentView中用if条件语句来切换它们。你不能直接给if条件语句添加SwiftUI修饰符,但可以将该条件语句包裹在Group中,然后给Group添加修饰符,示例如下:

swift
Group {
    if showingGrid {
        GridLayout(astronauts: astronauts, missions: missions)
    } else {
        ListLayout(astronauts: astronauts, missions: missions)
    }
}
.navigationTitle("My Group")

在为列表设置样式时,你可能会遇到一些困难,因为在iOS系统中,列表默认有特定的外观和风格。可以尝试给列表添加.listStyle(.plain)修饰符,然后给列表行的内容添加类似.listRowBackground(Color.darkBackground)的修饰符——这应该能帮你在实现目标的道路上走得更远。

【练习题】月球探索

问题1/12:以下哪些陈述是正确的?

  • 选项1:containerRelativeFrame()允许我们读取视图容器的尺寸。
  • 选项2:在尝试拉伸图像视图的内容之前,我们应该使用aspectRatio(contentMode: .resize)

问题2/12:以下哪些陈述是正确的?

  • 选项1:如果我们指定了图像的宽度,就必须同时指定它的高度。
  • 选项2:LazyVGridLazyHGrid可以放在ScrollView内部。

问题3/12:以下哪些陈述是正确的?

  • 选项1:只要我们的Swift代码与源数据匹配,Codable就能处理“结构体嵌套结构体”的情况。
  • 选项2:一个结构体只能遵循一个协议,不能同时遵循两个协议。

问题4/12:以下哪些陈述是正确的?

  • 选项1:给视图添加clipped()修饰符,可以确保视图始终在其框架内显示。
  • 选项2:滚动视图(ScrollView)可以是水平的或垂直的,但不能同时是水平和垂直的。

问题5/12:以下哪些陈述是正确的?

  • 选项1:自适应网格项(Adaptive grid items)允许SwiftUI根据可用空间创建尽可能多的列。
  • 选项2:SwiftUI视图预览(previews)不应有自己的局部变量。

问题6/12:以下哪些陈述是正确的?

  • 选项1:在使用泛型时,T代表“转换”(Transform)。
  • 选项2:在滚动视图内部使用延迟垂直栈(Lazy vertical stacks)可以减少运算量。

问题7/12:以下哪些陈述是正确的?

  • 选项1:NavigationLink需要NavigationStack才能工作。
  • 选项2:我们可以通过使用frame(maxWidth: .fill)让滚动视图占据所有可用的屏幕宽度。

问题8/12:以下哪些陈述是正确的?

  • 选项1:你可以随意从维基百科获取文本,因为它属于公有领域。
  • 选项2:scaledToFill()修饰符可能会导致图像的部分内容超出其容器框架。

问题9/12:以下哪些陈述是正确的?

  • 选项1:NavigationStack允许我们推入一个新的自定义视图,也可以推入Text这类基础类型的视图。
  • 选项2:sheet()需要NavigationStack才能工作。

问题10/12:以下哪些陈述是正确的?

  • 选项1:泛型允许我们编写可用于多种不同类型的代码。
  • 选项2:设置JSONDecoderdateStyle属性,可以自定义日期的加载方式。

问题11/12:以下哪些陈述是正确的?

  • 选项1:尖括号是[和]。
  • 选项2:嵌套结构体(nested struct)指的是放在另一个结构体内部的结构体。

问题12/12:以下哪些陈述是正确的?

  • 选项1:如果Codable遇到可选属性,只有当该属性在源数据中存在时,它才会尝试对其进行解档。
  • 选项2:你可以嵌套结构体,但不能嵌套类。

本站使用 VitePress 制作