学习原生 iOS 开发,快速入门 Swift 语言,使用 SwiftUI 控件,类似 React 的声明式语法、响应式数据、组件拆分。
使用 Swift + SwiftUI + SwiftData ,可以低成本开发苹果生态的跨端应用。该方案虽然对设备系统兼容性有要求,但用于入门和体验原生 APP 开发流程足够了。
Swift 语法
通过以下链接快速过一遍 Swift 基础语法,并结合 Unwrap 这个免费 APP,适合有其他编程语言经验的开发者:
https://www.hackingwithswift.com/articles/242/learn-essential-swift-in-one-hour
SwiftUI 学习思路
掌握基本语法后,接着就可以学习 SwiftUI 的教程了:
- 100 Days of SwiftUI ,从第 16 天开始: https://www.hackingwithswift.com/100/swiftui
- 苹果官方 SwiftUI 教程: https://developer.apple.com/tutorials/SwiftUI
- 苹果官方 SwiftUI 学习路径: https://developer.apple.com/swiftui/get-started/
也可以借助 AI 工具生成 SwiftUI 项目,在项目中学习,有了语言基础和基本的概念后,对 AI 生成的项目,调整起来也会比较顺手。
Xcode 基本操作
文件操作
新建文件: File > New > File From Template (快捷键 Cmd+N) ,如创建一个新的 SwiftUI 视图。
显示文件扩展名:XCode > Settings > General > File Extensions ,选择 Show All
查看代码 print 信息: View > Debug Area > Show Debug Area (快捷键:Shift+Cmd+Y)
真机调试
- 在 Xcode 中配置真机设备部署,通过数据线连接真机,设置信任电脑
- iPhone 需要开启开发者模式,并且在通用-设备管理中信任自己的开发者证书
- 在 Windows - Device & Simulators 中管理真机设备
- 真机联通后,后续可以使用 wifi 无线调试,无需额外配置
创建项目
在模板选择器中,选择 iOS 作为平台,选择 App 模板。
输入项目名称,如 WeSplit ,选择 SwiftUI 作为界面,选择 Swift 作为语言,存储选项设置为无。
需要创建 .gitignore 文件,忽略用户特定的 Xcode 设置 xcuserdata 、编译产物 build 和 DerivedData 等文件。
SwiftUI App 的基本结构
从文件目录可以看到以下文件被创建了出来:
源代码文件
WeSplitApp.swift
:应用程序的入口点,定义了App结构体,通常包含@main属性标识应用入口。ContentView.swift
:主要视图文件,使用SwiftUI构建应用的用户界面。
资源文件
Assets.xcassets
:资产目录,用于管理应用中的图像、颜色和图标等资源,包含AppIcon和AccentColor等预设资源集。
预览文件
Preview Content/Preview Assets.xcassets
:预览专用的资源目录,例如存放预览用的图片、颜色配置等,与应用正式发布时使用的主资源目录相区分。
App 结构体
@main
属性标识这个是应用程序的入口点,专为SwiftUI设计- 结构体遵循 App 协议,必须实现
var body: some Scene
计算属性来提供应用的场景内容 - WindowGroup :创建一个窗口场景,适用于多窗口应用
- 包含 ContentView() 作为根视图,这是应用启动后显示的第一个界面
import SwiftUI
@main
struct iTourApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
ContentView 主要视图组件
ContentView.swift 是SwiftUI应用的主要视图组件,定义了应用的用户界面内容和布局结构。
View 协议
struct ContentView: View
声明遵循 View 协议的结构体,这是 SwiftUI 中构建界面的基本单元,所有文本、按钮、图像等都是 View ,包括自己的组合其他视图的布局。- View 协议只有一个要求,即有一个名为 body 的计算属性,该属性返回
some View
。可以向视图结构体添加更多属性和方法。
视图组件
Swift UI 中,通过声明式语法定义视图,通过传入属性调用组件
- Swift UI 通过声明式语法来构建界面
- 本质上 VStack Form 等可嵌套内容的组件,都是闭包函数
- 原理是 Swift 的“尾随闭包”语法允许将函数的最后一个参数移到函数调用外部,且“单表达式闭包”中“隐式返回语法”允许省略 return 关键字
- 使用 VStack (垂直堆栈)组织界面元素,内部包裹了多个元素,并在外层应用了 .padding 修饰符调整样式
- Text 定义文本,Image 定义图片,传递了 systemName 属性,应用了 .imageScale 、 .foregroundStyle 等修饰符调整样式
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
修饰符
SwiftUI 修饰符(Modifiers)是用于定制视图外观和行为的核心工具,它们以链式调用的方式作用于视图,每个修饰符都会返回一个新视图(原始视图的变体)。
某些修饰符是通用的,某些修饰符仅适用于特定容器(如 .listRowSeparator(_:)
只对 List
有效)。
修饰符链式调用,顺序敏感。定义的顺序影响最终效果(例如先加背景还是先加内边距)。
Text("A").padding().background(.red) // 背景包裹整个文本+内边距
Text("B").background(.red).padding() // 背景仅包裹文本
如先定义圆角再定义背景,圆角不会生效于背景,呈现的背景图里没有圆角。
.cornerRadius(29) // 圆角不会
.background(.blue)
实时预览
开启实时预览:编辑区右上角按钮,打开更多菜单,启用 Canvas ,并设置 Layout - Canvas On Right 。
实时预览中的刷新快捷键:“Preview paused” 时需手动点击刷新按钮,可使用快捷键:Option+Cmd+P
Preview 声明为视图创建预览,支持在 Xcode 中实时查看界面效果。
在选择真机设备后,会在 Xcode Previews 应用中呈现预览效果。
Preview 比起使用模拟器或者真机安装应用的方式,效率和体验都提升了很多。
#Preview {
ContentView()
}
Selectable
Preview 预览时,左下角默认选中 Live 模式,可切换 Selectable 模式,点击可选择 SwiftUI 控件。
有些组件嵌套后不太好选中某个组件,如使用 NavigationStack 后里面的组件无法再被选中。
SwiftUI Insecptor
可以在 View -> Insecptors -> Show Insecptor 打开 SwiftUI Insecptor ,可操作选中的组件对应的属性和修饰符,如 Button 修改背景色、字体色等。
Live 模式,可通过把光标放到代码中的某个组件名词中来选中该组件,即可直接操作 Insecptor 。
Insecptor 中的属性值不是全量的,适合初学时做参考。
响应式数据
创建响应式数据
通过 @State
创建响应式数据,定义为 private 属性。值被修改后,会刷新界面中回显的内容。
@State private var tapCount = 0
var body: some View {
Button("\(tapCount)") {
tapCount += 1
}
}
双向绑定
通过 $
符号创建双向绑定,可用于 TextField 等表单控件。仅用于显示时,不需要 $
符号。
@State private var name = ""
var body: some View {
Form {
TextField("Enter your name", text: $name)
Text("Your name is \(name)")
}
}
计算属性
计算属性就通过 Swift 结构体的,如果参与计算的是 State ,改变后对应计算属性也会更新。
var totalPerPerson: Double {
let peopleCount = Double(numberOfPeople + 2)
let tipSelection = Double(tipPercentage)
let tipValue = checkAmount / 100 * tipSelection
let grandTotal = checkAmount + tipValue
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
}
数据监听
通过修饰符 .onChange(of: isReminderEnabled) { _ in ... }
监听值变化并执行操作,如:
Toggle("开启每日提醒", isOn: $isReminderEnabled)
.onChange(of: isReminderEnabled) { _, newValue in
if newValue {
notificationManager.scheduleDailyReminder(at: reminderTime)
} else {
notificationManager.cancelAllNotifications()
}
}
@FocusState 绑定输入框焦点状态
用于关闭键盘,避免弹出键盘后不会消失。
定义变量值,不需要设置默认值:
@FocusState private var amountIsFocused: Bool
通过 TextField 的 .focused() 修饰符绑定该状态:
.focused($amountIsFocused)
NavigationStack 增加 toolbar 修饰符,也是作用在直接子元素,用于判断当键盘激活时显示菜单按钮
.toolbar {
if amountIsFocused {
Button("Done") {
amountIsFocused = false
}
}
}
设置 amountIsFocused 为 false 后,会生效到 TextField 的 focused 修饰符里,使得键盘自动关闭。
拆分组件
定义一个新组件(struct + View),如定义了一个可复用的按钮组件,接受 title 作为参数:
struct MyButton: View {
var title: String
var body: some View {
Button(action: {
print("按钮被点击")
}) {
Text(title)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
在主视图中使用:
struct ContentView: View {
var body: some View {
VStack {
MyButton(title: "点击我")
MyButton(title: "提交")
}
}
}
拆分的组件可以单独在 Preview 中自由调用,方便临时调试单个组件,或使用 Selectable 模式查看。
#Preview {
VStack {
MyButton(title: "点击我")
MyButton(title: "提交")
}
}
自定义修饰符
通过创建可复用的封装简化代码:
// 定义修饰符
struct PrimaryButtonStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
// 使用扩展简化调用
extension View {
func primaryButtonStyle() -> some View {
modifier(PrimaryButtonStyle())
}
}
// 应用
Button("Submit") { ... }
.primaryButtonStyle()
项目设置与 icon
在左侧项目导航栏中,找到 Assets.xcassets ,选中 AppIcon,有各种 icon slot ,把对应尺寸的 PNG 拖拽到对应 slot 上,即可设置成功。
进入项目设置:在左侧文件导航栏中,点击最顶层的蓝色项目图标(项目名称),在 General - App Icons and Launch Images 设置图标,默认已设置了该图标的名字。