SwiftUI 对于自定义组件的封装和使用都非常友好,今天我们将以一个可横向滚动的卡片视图为例,介绍一下如何创建自定义组件,如何组装多个自定义组件,以及一点点动画相关的内容。
1. 预览

2. 构建卡片组件
通过分析结构,我们可以将卡片视图分解为以下 3 个部分:

我们将照此结构实现CardView.swift
,同时我们还会给 Card 添加一个触摸事件,在触摸 Card 时实现展开详情的动作。
1). 定义 @State 变量
为了实现状态切换时重新 layout,我们需定义几个变量:
@State var fold = true /// 是否展开 Card
var image: String = "" /// 卡片图片名称,供外部传入
var text: String = "" /// 卡片描述文本,供外部传入
2). Image
首先实现 Image 效果。
Image(image)
.resizable()
.frame(height: 200, alignment: .center)
.aspectRatio(contentMode: .fit)
.resizable()
: 使 Image 能够自适应 frame。
3). 描述 Text
使用一个 HStack
包裹描述 Text。
HStack {
Text(text)
.fontWeight(.light)
.font(.system(size: 13))
.lineLimit(fold ? 3 : nil)
.padding()
.scaledToFit()
Spacer()
}
lineLimit
: 设置为 nil 时即不限行数。scaledToFit
: Text 在改变行数时能自适应更改高度。
4). 底部 Text
if fold {
HStack {
Spacer()
Text("Read more")
.font(.system(size: 14))
.foregroundColor(.red)
}
.padding()
}
通过变量fold
,实现在展开 Card 时隐藏底部 “Read more” 描述。
5). 动画和触摸手势
为 Card 添加.animation
动画,使其在切换状态时能获得平滑的动画效果:
.animation(.easeInOut)
通过.onTapGestur
为 Card 添加触摸手势
.onTapGesture {
self.fold.toggle()
}
最后使用 VStack 将 3 个部分组装起来,完整代码如下:
VStack {
Image(image)
.resizable()
.frame(height: 200, alignment: .center)
.aspectRatio(contentMode: .fit)
HStack {
Text(text)
.fontWeight(.light)
.font(.system(size: 13))
.lineLimit(fold ? 3 : nil)
.padding()
.scaledToFit()
Spacer()
}
if fold {
HStack {
Spacer()
Text("Read more")
.font(.system(size: 14))
.foregroundColor(.red)
}
.padding()
}
}
.frame(width: fold ? 280 : 300)
.background(Color(white: 0.99))
.cornerRadius(10)
.shadow(color: .gray, radius: 10, x: 0, y: 3)
.animation(.easeInOut)
.onTapGesture {
self.fold.toggle()
}
使用 Xcode 预览效果如下:

3. 构建滚动组件
首先实现一个横向的ScrollView
,其中使用一个HStack
包裹我们的 Card 卡片:
ScrollView(.horizontal, showsIndicators: false) {
Spacer()
HStack(alignment: .center, spacing: 10) {
Spacer()
CardView(image: "card_cover1", text: lorem).padding()
CardView(image: "card_cover2", text: lorem).padding()
CardView(image: "card_cover3", text: lorem).padding()
Spacer()
}
Spacer()
}
使用 Xcode 预览效果如下:

4. 最终效果