[SwiftUI 100 天] 登月计划 - part1
2020/3/28 23:02:32
本文主要是介绍[SwiftUI 100 天] 登月计划 - part1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
译自 Moonshot: Introduction
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
Moonshot 介绍
在这个项目中我们将构建一个让用户了解 NASA 的阿波罗航天计划相关任务和宇航员的 app 。你不仅会更精通 Codable
, 重要的是可以接触到滚动视图,导航,以及更有趣的布局。
是的,你仍会做一些 List,Text
等视图的实践,但也会开始解决一些在 SwiftUI 中很重要的问题 —— 如何让一个图像正确适应空间?如何用计算属性整理代码?如何组合更小的视图到更大的视图以便保持项目结构的条理?
要做的事情很多,所以我们开始吧:用 Single View App 模板创建一个新的 iOS app ,取名叫“Moonshot” 。按照惯例,在开始项目之前,让我们先近距离接触一些你需要掌握的新技能...
译自 Resizing images to fit the screen using GeometryReader
用 GeometryReader 调整图像大小以适应屏幕
当我们在 SwiftUI 中创建一个 Image
视图时,它会根据内容的尺寸自动排列自身。因此,如果图片是 1000x500 ,那么 Image
视图也会是 1000x500 。这个机制有时候正是你想要的,但多数情况下你会想要图像以一个更小的尺寸显示,而我将在这个项目中向你演示如何做到这一点,同时也会介绍如何利用一个叫 GeometryReader
的新类型,帮助我们把图像适配到设备屏幕的宽度。
首先,添加某个图片到你的工程。图片内容无关紧要,只要比屏幕宽就行。我的图片名叫 “Example” ,显然你可以在代码中替换成你自己的图片名称。
让我们把图像绘制到屏幕:
struct ContentView: View { var body: some View { VStack { Image("Example") } } }复制代码
即便是在预览中,你也能看出图像对于可用的显示空间来说太大了。和其他视图一样, Image 也有相同的 frame()
modifier ,所以你可以尝试把它缩小,像这样:
Image("Example") .frame(width: 300, height: 300)复制代码
但是,上面的代码不会管用 —— 你的图像还是以全尺寸显示。如果你想知道为什么,可以再仔细看一看预览窗口:你会发现图像是全尺寸的,但图像中央有一个 300x300 的蓝色盒子。也就是说,图像的 frame 已经正确设置了,但图像内容却还是原始尺寸。
尝试把图像代码改成下面这样:
Image("Example") .frame(width: 300, height: 300) .clipped()复制代码
现在事情看起来更明显了:我们的图像视图的确是 300x300 ,但这不是我们想要的:
如果你希望图像的内容也调整大小,我们需要用resizable()
modifier ,像这样:
Image("Example") .resizable() .frame(width: 300, height: 300)复制代码
这样一来好些了,但仅仅只是好一点。图像尺寸虽然正确,但很可能被挤压了。我的图片不是正方形的,所以当它被调整大小塞进一个正方形时,看起来是变形的。
为了解决这个问题,我们需要让图像按比例缩放。实现这一点要用到 aspectRatio()
modifier 。它让我们提供一个要应用的比例作为参数,但如果我们忽略这个参数,SwiftUI 会自动使用原始的高宽比。
当谈到 “应该如何应用(比例)” 的部分,SwiftUI 把它叫做 content mode ,并且给了我们两个选项: .fit
表示图像会把自己整个装进容器,即使会让视图部分留白。 而 .fill
表示视图不会留白,也就意味着图像的一部分会跑出容器。
你可以自行尝试,看一下两者的区别。首先是应用 .fit
模式:
Image("Example") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 300, height: 300)复制代码
然后应用 .fill
模式:
Image("Example") .resizable() .aspectRatio(contentMode: .fill) .frame(width: 300, height: 300)复制代码
当我们只要固定尺寸的图像时,上面的方案可以很好满足。但是我们经常需要图像能自动放大一边或者两边以填满屏幕。就是说,不再硬编码宽度为 300 ,而是 ”让这个图像填满屏幕宽度“ 。
SwiftUI 给了我们一个专门的类型,叫 GeometryReader
,它非常地强大。是的,我知道 SwiftUI 里有很多东西都很强大,但是老实说:你能用 GeometryReader
做到的事情会让你大吃一惊。
我们还会在后面的项目中介绍更多 GeometryReader
的细节,不过眼下我们将用它来完成一件事:确保图像填满容器视图的完整宽度。
就像我们用过的其他视图,GeometryReader
也是一个视图,除了构建时它传给我们一个 GeometryProxy
对象。这个对象可以用来查询环境信息:容器有多大?我们的视图位置在哪里?是否有安全区 insets ?等等。
我们可以这个几何代理来设置图像的宽度,像这样:
VStack { GeometryReader { geo in Image("Example") .resizable() .aspectRatio(contentMode: .fit) .frame(width: geo.size.width, height: 300) } }复制代码
这样一来,无论我们用的是什么设备,图像都将填满整个屏幕宽度。
作为最后一个小技巧,我们可以从图像上移除 height
设置,像这样:
VStack { GeometryReader { geo in Image("Example") .resizable() .aspectRatio(contentMode: .fit) .frame(width: geo.size.width) } }复制代码
因为我们已经给到 SwiftUI 足够的信息,它能够自动搞明白高度:它知道原始宽度,知道目标宽度,也知道 content mode ,所以它能理解目标高度应该如何针对目标宽度成比例设置。
译自 How ScrollView lets us work with scrolling data
ScrollView 的工作方式
你已经知道 List
和 Form
可以让我们创建可滚动的数据表格,但是当我们想要滚动任意的视图 —— 比如,某些我们手动创建的视图 —— 我们需要求助于 SwiftUI 的 ScrollView
。
ScrollView 可以横向滚动,竖向滚动,或者两个方向都滚动,你还可以控制系统是否显示滚动指示器 —— 滚动指示器是一种让用户可以感知内容大小的小滚动条。 当我们把视图放进滚动视图中时,SwiftUI 能够自动算出内容的大小,以方便用户从一头滚动到另一头。
举个例子,我们可以创建一个有 100 个文本视图的滚动列表,像这样:
ScrollView(.vertical) { VStack(spacing: 10) { ForEach(0..<100) { Text("Item \($0)") .font(.title) } } }复制代码
在模拟器中运行,你会发现你可以在滚动视图上自由地拖曳,滚向底部,你还会发现 ScrollView
处理安全区域的方式正如 List
和 Form
—— 它们的内容会处于屏幕底部横线的下方,但由于额外的 padding ,最终的视图都是完整可见的。
你可能还会留意到,只能扣中间区域才能滚动,这有点恼人 —— 一般要整个区域都可以滚动。为了实现这个行为,我们要让 VStack
占据更多的空间,同时保留默认居中的方式不变,像这样:
ScrollView(.vertical) { VStack(spacing: 10) { ForEach(0..<100) { Text("Item \($0)") .font(.title) } } .frame(maxWidth: .infinity) }复制代码
现在你可以在屏幕上的任何地方点击并拖拽滚动,这样就容易使用多了。
看起来很简单对吧? ScrollView
的确明显比原来的 UIScrollView
容易使用。不过,你需要意识到一个关键点:当我们添加视图到一个滚动视图时,它们是被立即创建的。
为了演示这一点,我们在一个常规的文本视图上套一个包装,像这样:
struct CustomText: View { var text: String var body: some View { Text(text) } init(_ text: String) { print("Creating a new CustomText") self.text = text } }复制代码
然后把它用在 ForEach
中:
ForEach(0..<100) { CustomText("Item \($0)") .font(.title) }复制代码
结果看起来是一样,但当你运行 app ,你会在 Xcode 的日志里看到 100 行的 “Creating a new CustomText” 打印 —— SwiftUI 不会等你向下滚动将要看到视图的时候创建,它只会立即创建它们。
你可以 List
上尝试相同的实验,像这样:
List { ForEach(0..<100) { CustomText("Item \($0)") .font(.title) } }复制代码
运行这份代码,你会发现它是懒加载的:只有在真正用到的时候才创建 CustomText
实例。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~
这篇关于[SwiftUI 100 天] 登月计划 - part1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值