iOS 进阶 5-SwiftUI

97 阅读14分钟

前言:之前一直都是使用 UIkit 在写页面,对 SwiftUI 没怎么实战过,只是知道他挺好用的,代码风格很简约,最近在写 watch 的项目,这里简单的总结下 SwiftUI 的内容 目录

一、SwiftUI 是什么

二、开发环境搭建

安装 Xcode

创建 SwiftUI 项目

三、基础语法与常用组件

(一)视图和控件

(二)布局容器

(三)动画和效果

(四)数据绑定与状态管理

四、进阶应用

(一)页面导航

(二)网络请求

五、实战案例

(一)项目搭建

(二)界面设计与功能实现

六、总结与展望

一、SwiftUI 是什么

SwiftUI 是苹果公司在 2019 年 WWDC(苹果全球开发者大会)上推出的一款用于构建用户界面的全新框架,它基于 Swift 语言,采用声明式语法,能让开发者以一种更加直观、简洁的方式来创建 iOS、iPadOS、macOS、watchOS 和 tvOS 应用的用户界面。

在 SwiftUI 诞生之前,苹果平台的应用开发主要依赖于 UIKit(用于 iOS 和 iPadOS)、AppKit(用于 macOS)和 WatchKit(用于 watchOS)等框架 ,这些框架采用命令式编程风格,开发者需要详细地描述每一个界面元素的创建、布局和更新过程,代码往往较为冗长和复杂。而 SwiftUI 的出现,带来了一种全新的编程范式,它让开发者专注于描述界面 “是什么样”,而不是 “如何去创建”,大大简化了界面开发的流程。

SwiftUI 的优势众多,其声明式语法是一大亮点。以创建一个简单的包含文本和按钮的界面为例,在 UIKit 中,你可能需要这样写:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

@implementation ViewController

  • (void)viewDidLoad {

[super viewDidLoad];

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];

label.text = @"Hello, World!";

[self.view addSubview:label];

UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];

button.frame = CGRectMake(100, 200, 100, 50);

[button setTitle:@"Tap Me" forState:UIControlStateNormal];

[button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:button];

}

  • (void)buttonTapped {

NSLog(@"Button Tapped!");

}

@end

同样的功能,用 SwiftUI 实现则简洁得多:

import SwiftUI

struct ContentView: View {

var body: some View {

VStack {

Text("Hello, World!")

Button(action: {

print("Button Tapped!")

}) {

Text("Tap Me") }

}

}

}

可以看到,SwiftUI 的代码更直观、更易读,开发者只需描述出界面的最终状态,SwiftUI 会自动处理界面的更新和渲染。

SwiftUI 还具有强大的跨平台特性,一套代码可以在多个苹果平台上运行,减少了开发者为不同平台编写重复代码的工作量。它与 Swift 语言深度集成,充分利用了 Swift 的语言特性,如泛型、闭包、类型推断等,让代码更加简洁和安全。而且 SwiftUI 支持实时预览功能,开发者在编写代码时,能够即时看到界面的变化,大大提高了开发效率。

二、开发环境搭建 在开始学习 SwiftUI 之前,我们需要先搭建好开发环境。SwiftUI 开发主要依赖于 Xcode,它是苹果官方提供的集成开发环境,集代码编写、调试、界面设计等功能于一体 。下面我们来一步步完成开发环境的搭建。

安装 Xcode 前往 App Store:如果你使用的是 Mac 电脑,打开系统自带的 App Store 应用。 搜索 Xcode:在 App Store 的搜索栏中输入 “Xcode”,然后在搜索结果中找到 “Xcode” 应用。 下载安装:点击 “获取” 按钮,系统会自动下载并安装 Xcode。由于 Xcode 安装包较大,下载过程可能需要一些时间,请耐心等待。安装完成后,你可以在 “应用程序” 文件夹中找到 Xcode 图标。 创建 SwiftUI 项目 安装好 Xcode 后,我们就可以创建一个 SwiftUI 项目了,步骤如下:

打开 Xcode:双击 “应用程序” 文件夹中的 Xcode 图标,启动 Xcode。 新建项目:在 Xcode 启动界面中,选择 “创建一个新的 Xcode 项目”,或者点击菜单栏中的 “文件”->“新建”->“项目”。 选择项目模板:在弹出的模板选择窗口中,选择 “iOS” 选项卡,然后在 “应用程序” 类别中选择 “App” 模板,点击 “下一步”。 配置项目信息:在这一步,你需要填写项目的相关信息: 产品名称:输入你想要的项目名称,比如 “SwiftUIExample”。 组织标识符:通常采用反向域名的形式,例如 “com.yourcompany”,这里可以随意填写一个符合规则的标识符。 语言:选择 “Swift”,因为 SwiftUI 是基于 Swift 语言的。 用户界面:确保选择的是 “SwiftUI”。 其他选项可以保持默认,填写完成后点击 “下一步”。 选择项目存储位置:选择一个合适的文件夹来保存你的项目,然后点击 “创建”。 至此,一个全新的 SwiftUI 项目就创建完成了。Xcode 会自动为你生成一些基础代码和文件,其中ContentView.swift文件是项目的主视图文件,你可以在这里编写你的 SwiftUI 界面代码 。现在,你已经成功搭建好了 SwiftUI 的开发环境,可以开始编写代码,体验 SwiftUI 的强大功能了!

三、基础语法与常用组件 掌握 SwiftUI 的基础语法与常用组件是进行开发的关键,它们是构建丰富用户界面的基石,下面我们来详细了解一下。

(一)视图和控件 Text:用于显示文本。通过.font()可以设置字体,如.font(.title)设置为标题字体;.foregroundColor()设置文本颜色,例如.foregroundColor(.red)将文本颜色设为红色 。示例代码如下: Text("欢迎学习SwiftUI")

.font(.headline)

.foregroundColor(.blue)

Image:用来展示图像。.resizable()使图像可缩放,.frame()设定图像的宽高,如: Image("logo")

.resizable()

.frame(width: 100, height: 100)

这里假设项目资源中有名为 “logo” 的图片。

  1. Button:创建按钮,第一个参数是按钮显示的文本,闭包中是按钮点击后的操作。例如:

Button(action: {

print("按钮被点击了")

}) {

Text("点击我")

}

TextField:用于用户输入文本。使用@State修饰符来绑定输入的文本,实现数据双向绑定 。示例: struct ContentView: View {

@State private var inputText = ""

var body: some View {

TextField("请输入内容", text: $inputText)

.textFieldStyle(RoundedBorderTextFieldStyle())

}

}

Toggle:开关控件,通过@State绑定布尔值来控制开关状态 。代码如下: struct ContentView: View {

@State private var isOn = false

var body: some View {

Toggle("开关", isOn: $isOn)

}

}

(二)布局容器 VStack:垂直排列子视图,常用于表单、设置项列表等布局。通过spacing属性设置子视图间的垂直间距,alignment属性指定子视图在水平方向上的对齐方式 。例如: VStack(spacing: 20, alignment:.leading) {

Text("第一行文本")

Text("第二行文本")

}

HStack:水平排列子视图,适用于导航栏、图文混排等场景。类似 VStack,也有spacing和alignment属性 。示例: HStack(spacing: 10, alignment:.center) {

Image(systemName: "person.fill")

Text("用户名")

}

ZStack:按深度方向堆叠子视图,后添加的视图覆盖在前一个之上,常用于浮动按钮、叠加蒙层等。alignment属性指定子视图在水平和垂直方向上的对齐方式 。如下: ZStack(alignment:.topTrailing) {

Image("background")

.resizable()

.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)

Text("新消息")

.padding(8)

.background(Color.red)

.foregroundColor(.white)

.cornerRadius(10)

}

List:展示动态内容列表。结合ForEach循环遍历数据数组,展示列表项。假设定义一个结构体数组: struct Fruit: Identifiable {

let id = UUID()

let name: String

}

struct ContentView: View {

@State private var fruits = [

Fruit(name: "苹果"),

Fruit(name: "香蕉"),

Fruit(name: "橙子")

]

var body: some View {

List {

ForEach(fruits) { fruit in

Text(fruit.name)

}

}

}

}

(三)动画和效果 SwiftUI 中实现动画主要通过withAnimation和.animation。withAnimation用于显式添加动画,当执行动画块中的代码时,相关视图属性变化会带有动画效果 。例如:

struct ContentView: View {

@State private var isExpanded = false

var body: some View {

VStack {

Button(action: {

withAnimation(.easeInOut(duration: 0.5)) {

isExpanded.toggle()

}

}) {

Text(isExpanded ? "折叠" : "展开")

}

Rectangle()

.fill(Color.blue)

.frame(height: isExpanded ? 200 : 100)

}

}

}

.animation为视图添加隐式动画,当视图相关属性值变化时自动添加过渡动画 。如:

struct ContentView: View {

@State private var offsetX: CGFloat = 0

var body: some View {

VStack {

Button(action: {

offsetX += 100

}) {

Text("移动视图")

}

Rectangle()

.fill(Color.green)

.frame(width: 100, height: 100)

.offset(x: offsetX, y: 0)

.animation(.default)

}

}

}

(四)数据绑定与状态管理 @State:用于声明视图内部的可变状态,当状态变化时,SwiftUI 自动重新渲染相关视图。以计数器为例: struct ContentView: View {

@State private var count = 0

var body: some View {

VStack {

Text("计数: (count)")

Button(action: {

count += 1

}) {

Text("增加")

}

}

}

}

@Binding:在视图间传递和共享可读写的值,创建对属性的引用,多个视图共享同一份数据,数据更改在所有引用处生效。例如父视图控制子视图开关的显示: struct ParentView: View {

@State private var isToggleOn = false

var body: some View {

VStack {

Toggle(isOn: $isToggleOn) {

Text("控制子视图开关")

}

ChildView(isToggleOn: $isToggleOn)

}

}

}

struct ChildView: View {

@Binding var isToggleOn: Bool

var body: some View {

if isToggleOn {

Text("子视图可见")

}

}

}

四、进阶应用 (一)页面导航 在 SwiftUI 中,实现页面跳转和构建多页面应用主要依赖于NavigationLink,它是构建导航界面的关键组件,能够轻松实现页面间的切换和导航栏的管理 。例如,我们要构建一个简单的多页面应用,包含主页面和详情页面。

首先,在主页面中创建NavigationLink:

import SwiftUI

struct ContentView: View {

var body: some View {

NavigationView {

VStack {

NavigationLink(destination: DetailView()) {

Text("跳转到详情页面")

}

}

.navigationTitle("主页面")

}

}

}

struct DetailView: View {

var body: some View {

Text("这是详情页面")

.navigationTitle("详情页面")

}

}

在上述代码中,NavigationView创建了一个导航容器,NavigationLink的destination属性指定了跳转的目标视图为DetailView,点击Text("跳转到详情页面")即可跳转到详情页面。同时,通过navigationTitle修饰符分别设置了主页面和详情页面导航栏的标题 。

当需要在动态列表中实现页面跳转时,比如展示一个商品列表,点击每个商品跳转到对应的商品详情页面,代码如下:

struct Product: Identifiable {

let id = UUID()

let name: String

}

struct ContentView: View {

let products = [

Product(name: "商品1"),

Product(name: "商品2"),

Product(name: "商品3")

]

var body: some View {

NavigationView {

List(products) { product in

NavigationLink(destination: ProductDetailView(product: product)) {

Text(product.name)

}

}

.navigationTitle("商品列表")

}

}

}

struct ProductDetailView: View {

let product: Product

var body: some View {

Text("商品详情:(product.name)")

.navigationTitle("商品详情")

}

}

这里通过ForEach循环遍历products数组,为每个商品创建一个NavigationLink,点击列表项即可跳转到对应的商品详情页面,并在详情页面展示商品名称。

(二)网络请求 在 SwiftUI 中,使用URLSession进行 API 数据获取是常见的操作,结合 JSON 解析,我们可以轻松展示网络数据。以获取一个 JSON 格式的用户列表数据为例,假设 API 返回的数据如下:

[

{

"id": 1,

"name": "张三",

"age": 25

},

{

"id": 2,

"name": "李四",

"age": 30

}

]

首先,定义一个结构体来映射 JSON 数据:

struct User: Codable, Identifiable {

let id: Int

let name: String

let age: Int

}

然后,在视图中发起网络请求并解析数据:

struct ContentView: View {

@State private var users: [User] = []

var body: some View {

List(users) { user in

VStack(alignment:.leading) {

Text("姓名:(user.name)")

Text("年龄:(user.age)")

}

}

.onAppear {

fetchData()

}

}

func fetchData() {

guard let url = URL(string: "example.com/api/users") else { return }

URLSession.shared.dataTask(with: url) { data, response, error in

guard let data = data, error == nil else {

print(error?.localizedDescription ?? "No data")

return

}

do {

let decodedUsers = try JSONDecoder().decode([User].self, from: data)

DispatchQueue.main.async {

self.users = decodedUsers

}

} catch {

print("解析错误:(error)")

}

}.resume()

}

}

在上述代码中,@State修饰的users数组用于存储获取到的用户数据。fetchData函数中,通过URLSession.shared.dataTask发起网络请求,获取数据后,使用JSONDecoder进行 JSON 解析,将解析后的数据在主线程中更新到users数组,从而在List中展示出来 。这样,我们就实现了在 SwiftUI 中获取和展示网络数据的功能。

五、实战案例 接下来,我们通过构建一个简单的新闻资讯类 App,来巩固前面所学的 SwiftUI 知识,进一步了解 SwiftUI 在实际开发中的应用。这个 App 主要包含新闻列表页和新闻详情页,用户可以在列表页查看新闻标题和摘要,点击列表项跳转到详情页查看新闻的完整内容。

(一)项目搭建 创建项目:打开 Xcode,新建一个 iOS 项目,选择 “App” 模板,产品名称设为 “NewsApp”,语言选择 “Swift”,用户界面选择 “SwiftUI”,然后点击 “下一步”,选择项目保存位置并创建项目。 数据模型:创建一个 Swift 文件,命名为News.swift,用于定义新闻的数据模型。假设新闻包含标题、作者、发布日期、摘要和详情内容,代码如下: import Foundation

struct News: Identifiable {

let id = UUID()

let title: String

let author: String

let date: String

let summary: String

let content: String

}

(二)界面设计与功能实现 新闻列表页:新建一个 SwiftUI 文件,命名为NewsListView.swift,用于展示新闻列表。在这个视图中,我们使用NavigationView创建导航容器,List展示新闻列表,NavigationLink实现列表项到详情页的跳转 。代码如下: import SwiftUI

struct NewsListView: View {

// 假设预定义一些新闻数据

let newsList = [

News(title: "科技巨头发布新芯片", author: "科技日报", date: "2024-10-10", summary: "某科技巨头今日发布了全新一代芯片,性能大幅提升...", content: "详细内容..."),

News(title: "新能源汽车销量创新高", author: "财经时报", date: "2024-10-09", summary: "本月新能源汽车销量突破新高,行业发展迅猛...", content: "详细内容...")

]

var body: some View {

NavigationView {

List(newsList) { news in

NavigationLink(destination: NewsDetailView(news: news)) {

VStack(alignment:.leading) {

Text(news.title)

.font(.headline)

Text("作者:(news.author) - (news.date)")

.font(.subheadline)

.foregroundColor(.gray)

Text(news.summary)

.font(.body)

}

}

}

.navigationTitle("新闻资讯")

}

}

}

struct NewsListView_Previews: PreviewProvider {

static var previews: some View {

NewsListView()

}

}

在上述代码中,List遍历newsList数组,为每个新闻创建一个列表项,列表项包含新闻标题、作者、发布日期和摘要。点击列表项,通过NavigationLink跳转到NewsDetailView,并将当前新闻对象传递过去 。

新闻详情页:新建一个 SwiftUI 文件,命名为NewsDetailView.swift,用于展示新闻的详细内容。代码如下: import SwiftUI

struct NewsDetailView: View {

let news: News

var body: some View {

VStack(alignment:.leading) {

Text(news.title)

.font(.largeTitle)

.padding(.top, 20)

Text("作者:(news.author) - (news.date)")

.font(.subheadline)

.foregroundColor(.gray)

.padding(.top, 10)

Text(news.content)

.font(.body)

.padding(.top, 20)

}

.padding()

.navigationTitle("新闻详情")

}

}

struct NewsDetailView_Previews: PreviewProvider {

static var previews: some View {

let sampleNews = News(title: "示例新闻", author: "示例作者", date: "2024-10-10", summary: "示例摘要", content: "示例详细内容")

NewsDetailView(news: sampleNews)

}

}

在这个视图中,根据传递过来的news对象,展示新闻的标题、作者、发布日期和完整内容。

将新闻列表页设置为主视图:打开ContentView.swift文件,将NewsListView设置为主视图,代码如下: import SwiftUI

struct ContentView: View {

var body: some View {

NewsListView()

}

}

struct ContentView_Previews: PreviewProvider {

static var previews: some View {

ContentView()

}

}

运行项目,你将看到一个简单的新闻资讯类 App,在列表页可以查看新闻列表,点击列表项可以跳转到详情页查看新闻的详细内容 。通过这个实战案例,你可以将前面所学的 SwiftUI 知识应用到实际开发中,加深对 SwiftUI 的理解和掌握。

六、总结与展望 通过本文的学习,我们对 SwiftUI 有了较为全面的认识。从它简洁直观的声明式语法,到丰富多样的基础组件和布局容器;从实现页面导航、网络请求等进阶应用,再到通过实战案例将所学知识融会贯通 ,SwiftUI 展现出了强大的功能和便捷性,为开发者带来了全新的开发体验。

SwiftUI 的学习是一个不断积累和实践的过程。希望读者在掌握了本文的内容后,能够继续深入学习 SwiftUI 的更多高级特性,如手势识别、自定义视图、与 Core Data 集成等 ,不断提升自己的开发能力。同时,积极参与实际项目开发,在实践中巩固所学知识,解决实际问题,积累开发经验。

展望未来,随着苹果对 SwiftUI 的持续更新和优化,SwiftUI 的功能将更加完善,性能将进一步提升,应用场景也会更加广泛。它不仅将在移动应用开发领域发挥重要作用,还可能在桌面应用、智能穿戴设备应用等领域展现出更大的潜力 。相信在不久的将来,SwiftUI 会成为苹果平台应用开发的主流框架,为用户带来更多优质、创新的应用。让我们一起期待 SwiftUI 的精彩未来,在 SwiftUI 的学习和实践道路上不断前行!