轻学编程|Swift 教程 004:构建个人简历页面
轻学编程|Swift 教程 004:构建个人简历页面

轻学编程|Swift 教程 004:构建个人简历页面

本节课我们将制作一个简单的个人简历视图,并学习以下知识:

  • cornerRadius
  • spacing
  • ScrollView

在构建过程中,我们将复习布局、Text 和 Image 等知识,并实现间距控制以及内容滚动的效果。

个人简历

目标效果:

请先尝试独立完成,再继续阅读下面的讲解。

创建项目

创建新的 iOS 项目,或者继续使用之前的项目。

默认的 ContentView 代码:

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
}

现在清空代码,开始编写自己的内容:

struct ContentView: View {
    var body: some View {
        
    }
}

整体结构

在实际开发中,我们通常会先设计整体结构。

我们的页面包含:

  1. 标题
  2. 个人信息
  3. 个人介绍

我们在最外层添加一个 VStack:

struct ContentView: View {
    var body: some View {
        VStack {
            
        }
        .padding()
    }
}

VStack 可以确保视图垂直排序,并且可以控制视图的排列和间距。

标题

首先,我们创建一个 Text 显示标题。

在这里,我将标题显示为自己的英文名称:

Text("FangJunyu")

标题通常加大加粗,这里使用 font 和 fontWeight 修饰符:

Text("FangJunyu")
    .font(.largeTitle)
    .fontWeight(.bold)

排版

SwiftUI 默认的对齐方式为 center,现在标题显示在 ContentView 的中间位置。

我们需要将标题显示在视图的顶部,可以使用 Spacer 实现布局的调整:

VStack {
    Text("FangJunyu")
        .font(.largeTitle)
        .fontWeight(.bold)
    Spacer()
}
.padding()

Spacer 会占据剩余空间,将 Text 推到容器的上方。

留白

如果认为文字部分距离顶部过近,可以使用 padding 或者 Spacer。

1. padding

VStack {
    Text("FangJunyu")
        .font(.largeTitle)
        .fontWeight(.bold)
    Spacer()
}
.padding(.top,20)

设置 VStack 的 padding 顶部边距为 20。

2. Spacer

VStack {
    Spacer().frame(height:20)
    Text("FangJunyu")
        .font(.largeTitle)
        .fontWeight(.bold)
    Spacer()
}
.padding()

也可以使用 Spacer,设置 frame 的高度,可以获得一个指定高度的留白。

实现效果:

图片

我们准备一张自己的大头照,放入 Assets 资源文件夹中。

在 ContentView 中使用 Image 显示图片:

VStack {
    Text("FangJunyu")
        .font(.largeTitle)
        .fontWeight(.bold)
    Image("image")
    Spacer()
}
.padding(.top, 20)

因为图片的原尺寸比较大,我们需要使用 frame 控制显示大小。

Image("image")
    .resizable()
    .frame(width: 160, height: 200)

这时我们会发现一个问题:

如果 frame 的宽高比例与图片原始比例不一致,图片就会发生变形。

例如:

Image("image")
    .resizable()
    .frame(width: 160, height: 100)

为了避免图片变形,我们需要使用 scaledToFit:

Image("image")
    .resizable()
    .scaledToFit()
    .frame(width: 140, height: 200)

scaledToFit 的作用是:

在给定的 frame 范围内,按照图片原始比例进行缩放,并完整显示图片内容。

它不会强行拉伸图片去填满整个 frame,而是保持宽高比例不变,等比例缩放,直到某一边刚好贴合边界。

这意味着:

  • 如果 frame 的宽度较小,图片会以宽度为参考进行缩放。
  • 如果 frame 的高度较小,图片会以高度为参考进行缩放。
  • 图片始终保持原始比例,不会变形。

通常可以设置一个方向的尺寸。例如:

Image("image")
    .resizable()
    .scaledToFit()
    .frame(width: 140)

这样系统会根据宽度 140 自动计算合适的高度,同时保持图片比例。

如果需要固定视觉比例或避免在复杂布局中被压缩,可以同时约束宽高。

圆角

如果希望图片以圆角形式显示,可以使用 cornerRadius 修饰符:

.cornerRadius(10)

例如:

Image("image")
    .resizable()
    .scaledToFit()
    .frame(width: 140)
    .cornerRadius(20)

在 Image 的修饰符后面添加 cornerRadius(20)。

cornerRadius(20) 表示对视图的边界进行裁切,并应用 20 的圆角半径。

添加圆角之后,图片的四个角会变为圆弧形状,使视觉效果更加柔和、现代。

这种设计风格在当前界面设计中非常常见,例如 iOS 的应用图标就采用了圆角矩形的形态(不过系统图标使用的是连续曲率的超椭圆形,而不是简单的圆角)。

个人信息

现在开始实现图片左侧的个人信息区域。从界面结构可以看出,个人信息与图片是横向排列,需要使用 HStack 排序。

HStack {
    // Text
    Image("image")
        .resizable()
        .scaledToFit()
        .frame(width: 140)
        .cornerRadius(20)
}

个人信息内容是垂直排列。

因此,外层使用 HStack,个人信息使用 VStack,文本内容使用 Text。

基本结构:

HStack {
    VStack {
        Text("Name")
        Text("Fang Junyu")
        Text("Age")
        Text("28")
        Text("Country")
        Text("China")
    }
    Image("image")
        .resizable()
        .scaledToFit()
        .frame(width: 140)
        .cornerRadius(20)
}

加粗字段标题

为了区分字段名与字段值,可以对字段名使用 fontWeight:

Text("Name")
    .fontWeight(.bold)
Text("Fang Junyu")

文本左对齐

VStack 默认是居中对齐,如果希望所有文本左对齐,需要设置对齐方式:

VStack(alignment: .leading) {
    Text("Name")
        .fontWeight(.bold)
    Text("Fang Junyu")
    ...
}

.leading 表示按照左侧对齐(在从左到右语言环境下)。

间距

如果希望个人信息与图片之间保留固定间距,我们前面学习的是 Spacer 设置留白:

Spacer()
    .frame(width: 10)

也可以使用 HStack 的 spacing 参数:

HStack(spacing: 30) {
    VStack(alignment: .leading) {
        ...
    }

    Image("image")
        .resizable()
        .scaledToFit()
        .frame(width: 140)
        .cornerRadius(20)
}

spacing: 30 表示两个子视图之间的间距为 30 pt。

spacing 是什么?

在 VStack、HStack、ZStack 中,spacing 可以控制子视图之间的距离。

例如:

VStack(spacing: 10) {
    VStack {}
    VStack {}
    VStack {}
}

VStack 中的子视图的间距会被设置为 10pt。

需要注意的是,spacing 只作用于”直接子视图”,不会影响嵌套容器内部的布局。

控制列表内部间距

如果希望字段之间增加间距,最直接的方式是 VStack 设置 spacing:

VStack(alignment: .leading, spacing: 10) {
    Text("Name")
        .fontWeight(.bold)
    Text("Fang Junyu")
    Text("Age")
        .fontWeight(.bold)
    Text("28")
    Text("Country")
        .fontWeight(.bold)
    Text("China")
}

这样所有子视图之间的间距都会变成 10 pt。

但是观察界面会发现一个问题:

字段组之间有间距,同时字段名与字段值之间也出现了相同的间距。

这是因为 spacing 作用于当前容器的所有直接子视图。

在这个结构中,每个 Text 都是外层 VStack 的直接子视图,因此间距是统一的。

如果希望字段组之间有间距,字段名与字段值之间保持默认紧凑间距,可以将”字段名 + 字段值”视为一个逻辑整体,在外面用 VStack 包裹:

VStack(alignment: .leading, spacing: 10) {
    VStack(alignment: .leading) {
        Text("Name")
            .fontWeight(.bold)
        Text("Fang Junyu")
    }
    VStack(alignment: .leading) {
        Text("Age")
            .fontWeight(.bold)
        Text("28")
    }
    VStack(alignment: .leading) {
        Text("Country")
            .fontWeight(.bold)
        Text("China")
    }
}

这时结构就变成外层 VStack 之间有间距,内层 VStack 之间为默认紧凑间距,因此字段名与字段值之间不会出现额外空隙。

个人介绍

接下来实现个人介绍区域。

从界面结构上看,介绍内容由多行文本组成,文本是纵向排列。

因此,可以使用 VStack 配合 Text:

VStack {
    Text("I am an iOS developer.")
    Text("I build apps with Swift.")
    Text("I like to learn new things.")
    Text("I am a good person.")
    Text("I am a good developer.")
    Text("I work hard every day.")
}

增加间距

现在个人信息和个人介绍部分比较紧凑,样式并不美观。

因为它们都位于同一个外层容器中,可以在外层容器上控制整体间距:

VStack(spacing: 30) {
    ...
    HStack(spacing: 30) {
        ...
    }
    VStack {
        Text("I am an iOS developer.")
        Text("I build apps with Swift.")
        ...
    }
}
.padding()

实现效果:

列表间距

使用 spacing 设置个人介绍文本之间的间距:

VStack(spacing:10) {
    Text("I am an iOS developer.")
    Text("I build apps with Swift.")
    ...
}

完成个人简历

现在,我们个人简历的基本框架已经完成。

滚动视图

现在页面结构使用的是 VStack,介绍文本数量较少时没有问题,但如果增加到 20 条、30 条甚至更多,内容高度就会超出屏幕。

例如:

VStack(alignment:.leading, spacing: 10) {
    Text("I am an iOS developer.")
    Text("I build apps with Swift.")
    Text("I like to learn newthings.")
    Text("I am a good person.")
    Text("I am a good developer.")
    Text("I work hard every day.")
    Text("I am an iOS developer.")
    Text("I build apps with Swift.")
    ...
}

这时就会出现两个现象:

  • 下方内容被截断
  • 页面无法滚动

这不是 VStack 的问题,VStack 只是一个布局容器,它不会自动提供滚动能力。

什么是 ScrollView

ScrollView 是一个可滚动容器,适合大量超出屏幕大小的内容。例如垂直或水平列表。

基本结构:

ScrollView {
    ...
}

如果想要实现滚动效果,就需要将整个页面内容包裹在 ScrollView 中:

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 30) {
                ...
            }
            .padding()
        }
    }
}

这样,页面整体成为一个可滚动区域,当内容超出屏幕高度时,可以自然滚动。

ScrollView 默认会显示滚动条指示器,如果希望隐藏滚动条,可以设置:

ScrollView(showsIndicators: false) {
    ...
}

完整代码

struct ContentView: View {
    var body: some View {
        ScrollView(showsIndicators: false) {
            VStack(spacing: 30) {
                Text("FangJunyu")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                HStack(spacing: 30) {
                    VStack(alignment: .leading, spacing: 10) {
                        VStack(alignment: .leading) {
                            Text("Name")
                                .fontWeight(.bold)
                            Text("Fang Junyu")
                        }
                        VStack(alignment: .leading) {
                            Text("Age")
                                .fontWeight(.bold)
                            Text("28")
                        }
                        VStack(alignment: .leading) {
                            Text("Country")
                                .fontWeight(.bold)
                            Text("China")
                        }
                    }
                    Image("image")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 140, height:200)
                        .cornerRadius(20)
                }
                VStack(spacing:10) {
                    Text("I am an iOS developer.")
                    Text("I build apps with Swift.")
                    Text("I like to learn new things.")
                    Text("I am a good person.")
                    Text("I am a good developer.")
                    Text("I work hard every day.")
                }
                Spacer()
            }
            .padding(.top, 20)
        }
    }
}
   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注