[SwiftUI 100 天] 构建卡片栈

636 阅读3分钟

译自 www.hackingwithswift.com/books/ios-s…

更多内容,欢迎关注公众号 「Swift花园」

喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

构建卡片栈

我们已经设计了单张卡片的数据模型及关联视图,下一步是把多张卡片堆叠在一起,以表示所有用户想要学习的内容。这个卡片堆会随着用户移除卡片而改变,所以我们需要将它标记为 @State

目前为止我们还没有设计添加卡片的入口,所以先用 10 张示例卡片来生成卡片堆。Swift 的数组有一个很有用的构造器,init(repeating:count:),它接收一个值,然后以重复某个次数的方式创建数组。我们这里就可以用它来创建简单的测试数组。

把下面这个属性添加到 ContentView

@State private var cards = [Card](repeating: Card.example, count: 10)

我们的 ContentView 将会包含多个堆栈:

  1. 卡片的堆栈需要是 ZStack ,以便我们可以把卡片重叠在一起,并营造出 3D 的效果。
  2. ZStack 外面是一个 VStack。当前 VStack 还没有发挥作用,稍后我们会利用它在卡片下方添加定时器。
  3. VStack 的外面又是一个 ZStack,以便我们把卡片和定时器放在背景之上。

初看这些堆栈可能有过渡设计的嫌疑,但随着我们往前推进,你会发现它们是很合理的。

接下来的代码中唯一复杂的部分在于我们如何在卡片堆里摆放卡片,以便它们之间互相重叠。我之前提过,编写 SwiftUI 代码的最佳实践是把任何复杂的计算分离到方法或者 modifier 里去。

基于这个思想,我们要创建一个 stacked() modifier,接收数组的索引和总大小,然后基于这两个值对视图进行偏移。通过偏移卡片,我们能创建出很有吸引力的卡片堆叠效果。

把下面这个扩展添加到 ContentView.swift,在 ContentView 结构体之外:

extension View {
    func stacked(at position: Int, in total: Int) -> some View {
        let offset = CGFloat(total - position)
        return self.offset(CGSize(width: 0, height: offset * 10))
    }
}

如你在代码中看到的那样,对于数组中每个位置增量,我们把视图往下偏移 10 个点。因为卡片的 Y 偏移就是 0,10,20,30,等等。

基于这个简单的 modifier,我们现在就可以创建出非常好看的卡片效果。把 ContentViewbody 属性替换成下面这样:

var body: some View {
    ZStack {
        VStack {
            ZStack {
                ForEach(0..<cards.count, id: \.self) { index in
                    CardView(card: self.cards[index])
                        .stacked(at: index, in: self.cards.count)
                }
            }
        }
    }
}

运行代码,你会明白我说的阴影的变化会体现出卡片深度的增加。在白色背景上,这个效果十分突兀,不过我们可以添加一个背景,让视觉效果看起来更好。

在项目的 github 文件中,你可以找到 background@2x.jpg 和 background@3x.jpg —— 把它们拉进你的 asset catalog 里。

然后添加下面这个 Image 视图到 ContentView,在最外层的 ZStack 的最下一层:

Image("background")
    .resizable()
    .scaledToFill()
    .edgesIgnoringSafeArea(.all)

添加背景只是一个很小的改动,但对于整个应用的视觉提升很大!


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~

Swift花园微信公众号