
Swift学习摘记
初见
默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用 _
表示不使用参数标签:
1 | func greet(_ person: String, on day: String) -> String { |
参数标签指的是调用时候的名称, 参数名指的是函数内部.
函数是第一等类型,这意味着函数可以作为另一个函数的返回值。
1 | func makeIncrementer() -> ((Int) -> Int) { |
函数也可以作为另一个函数的输入参数:
1 | func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool { |
可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。
1 | let sortedNumbers = numbers.sorted { $0 > $1 } |
actor
与 class
类似, 但是可以序列化访问, 保护共享、可变的数据.
对象和类
构造与析构
使用 self.
替代 this->
, 使用 init
和 deinit
分别声明构造和析构函数:
1 | class NamedShape { |
子类如果要重写父类的方法的话,需要用 override
标记:
1 | class Square: NamedShape { |
- 使用
:className
的方法声明父类.super.init
是用来调用父类(超类)的初始化方法的。当创建一个子类的实例时,子类可能需要初始化一些自己的属性,同时还需要确保父类的属性也被正确初始化。这时就需要使用super.init
来调用父类的初始化方法,完成父类的初始化过程- 严格的顺序要求: 子类必须先初始化自己的属性,然后调用
super.init
,最后才能访问或修改继承来的属性。
计算属性
普通的计算属性
在属性内部使用 {}
并加上 return
, 可以让访问这个属性的时候, 返回值由结构体的其他属性计算得到.
1 | struct Temperature { |
- 此时实例化一个结构体就只需要给出一个属性的值.
self
在上述的Swift代码中是不可或缺的, 因为形参和内部属性的名称相同.
使用 getter 和 setter 的计算属性:
1 | class EquilateralTriangle: NamedShape { |
计算属性同样是一个属性, 但是根据调用方式的不同, 有返回和设置两种方式.
1 | triangle.perimeter = 9.9 |
Mutating
默认情况下, 结构体中的方法不能直接修改结构体的属性. 需要显式声明为 mutating
:
1 | struct User{ |
属性监视器
使用 willSet
和 didSet
。写入的代码会在属性值发生改变时调用,但不包含构造器中发生值改变的情况:
- 分别可以使用
newValue
与oldValue
来表示属性将要改变的值以及改变之前的值.
e.g. 确保三角形的边长总是和正方形的边长相同。
1 | class TriangleAndSquare { |
枚举
1 | enum Rank: Int { |
- 如果没有设置第一个成员的初始值, 默认从
0
开始; - 缺省值按照递增处理;
case
之外可以设置方法.
使用 init?(rawValue:)
初始化构造器来从原始值创建一个枚举实例:
1 | if let convertedRank = Rank(rawValue:3){ |
if let
表示可选绑定, 安全地解包可选值
解包
if let
解包
1 | if let A = B { |
如果 B
不是 nil, 就将其赋值给A, 然后执行 {}
内部的语句.
??
1 | var score : Int ? = nil |
对字典进行索引:
1 | // scores是一个Int数组 |
swift支持对字典进行更新或者移除的时候, 返回并使用就值:
1 | if let oldValue = scores.updateValue(100, forKey:"fad"){ |
@IBAction
表示组件交互和代码相绑定(允许在交互的时候执行外部定义的函数);@IBOutlet
表示允许代码的响应改变组件本身的状态(字体、大小等).

概念
闭包
闭包指的是可以在特定位置运行的、不需要名称的函数.
1 | scene.setOnStartHandler{ |
toggle()
可以自动切换变量的布尔值.1
2
3Button("Press Me") {
isOn.toggle()
}
状态属性
使用
@State
在视图之外定义;当状态属性的值发生改变时, 会自动更新视图中相关的部分.
对于状态对象, 使用
@StateObject
来声明.
绑定
由 @Binding
声明将属性连接到其他地方, 允许子视图对属性的修改并同步.
在属性的前面增加 $
,表明会同步修改可信源.
字符串插值
在较长字符串中使用常量、变量或代码表达式,使它们替换为其当前值以求出字符串的值。
例如,在字符串”Katy ate a \(fruit).”中,如果fruit 是带有值 “peach”的变量,那么在求字符串的值时,\(fruit)由”peach”替换,变为 “Katy ate a peach.”。
其他
自动的动画效果
当状态属性发生改变时, 我们希望对应控制的视图元素的变化具有动画效果, 那么可以指定: e.g.
1 | Circle() |
其中的
value: isOn
表示追踪的状态属性.
在一个视图中创建状态对象, 然后在 app
中声明为环境变量并在子视图中使用.
设计原则
principle
- 需要长按进行交互的组件, 在轻触时ICON放大或者缩小
官方手册学习记录
基础知识
Swift 使用字符串插值将常量或变量的名称作为占位符包含在较长的字符串中,并提示 Swift 将其替换为该常量或变量的当前值。将名称包在括号中,并在左括号前用反斜杠进行转义:
1
2print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 打印 "The current value of friendlyWelcome is Bonjour!"不必使用
;
, 但是如果想在一行中编写多个独立语句,则必须使用分号:1
2let cat = "🐱"; print(cat)
// 打印 "🐱"整数边界: 使用 min, max进行访问
1
2let minValue = UInt8.min // minValue 等于 0,类型为 UInt8
let maxValue = UInt8.max // maxValue 等于 255,类型为 UInt8类型别名:
typealias
1
2
3
4
5typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在为 0
元组
- 作用: 多个值组合成一个复合值
1 | let http404Error = (404, "Not Found") |
如果只需要元组的部分值,则在分解元组时使用下划线 (_
) 忽略不需要的部分
分解元组
1
2
3
4
5let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 打印 "The status code is 404"
print("The status message is \(statusMessage)")
// 打印 "The status message is Not Found"可以在定义元组时为元组中的各个元素命名:
1
let http200Status = (statusCode: 200, description: "OK")
然后可以使用元素名访问:
1
2
3
4print("The status code is \(http200Status.statusCode)")
// 打印 "The status code is 200"
print("The status message is \(http200Status.description)")
// 打印 "The status message is OK"也可以直接使用从零开始的索引来访问, e.g.
http200Status.0
可选: 存储这种类型的值或者
nil
.提供后备值:
??
1
2
3
4let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// 打印 "Hello, friend!"- 如果
??
之前的值不是nil
, 就会正常解包, 否则选择后备值; - 使用
()
包裹.
- 如果
隐式解包可选: 安全假定一直都有值时使用
1
2
3
4
5let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要显式解包
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 隐式解包
错误处理
函数在声明中包含 throws
关键字,表示它可以抛出错误。调用可以抛出错误的函数时,要在表达式前加上 try
关键字.
Swift 会自动将错误传播到当前作用域之外,直到它们被 catch
子句处理为止。
1 | do { |
细节部分在后面补充
断言和先决条件
使用断言进行调试
1 | let age = -3 |
断言的第一个参数是预期的、正确的结果, 如果不满足条件就会显示报错. 但是不会阻止程序继续运行.
强制执行先决条件
当条件有可能为假,但必须为真才能继续执行代码时,请使用先决条件.
向该函数传递一个计算结果为 true
或 false
的表达式,以及一条在条件结果为 false
时显示的信息:
1 | // 在下标的实现中... |
运算符
基本运算符
与 C 和 Objective-C 中的赋值运算符不同,Swift 中的赋值运算符本身不返回值。以下语句无效:
1
2if x = y { // 这是无效的,因为 x = y 不返回值。
}- 可以防止不小心使用赋值运算符(=) 而非等于运算符(==).
基本的四则运算不允许值的溢出.
[!NOTE]
在 Swift 中对负数的处理与模运算符有所不同:
为了确定
a % b
的答案,%
运算符计算以下等式并返回余数
作为输出:
1 a` = (`b` x `某个乘数`) + `余数其中
某个乘数
是b
在a
中能容纳的最大倍数。
1
2
3 9 % 4 // 等于 1
-9 % 4 // 等于 -1
数值的正负号可以使用前缀
-
切换,称为一元负号运算符.中间没有任何空格.
1
2let three = 3
let minusThree = -three // minusThree 等于 -3
元组的计算
- 前提: 如果两个元组具有相同的类型和相同数量的值,则可以比较它们.
- 规则:
- 元组是从左到右逐个值进行比较的,直到比较发现两个不相等的值为止。
- 这两个值将进行比较,并且该比较的结果决定了整个元组比较的结果。
- 如果所有元素都相等,那么这两个元组本身就相等。
[!NOTE]
只有当给定的运算符可以应用于各自元组中的每个值时,元组才能与该运算符进行比较.
1 | ("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔值 |
空合并运算符
a ?? b
的结果与下面的运算相同:
1 | a != nil ? a! : b |
区间运算
闭区间运算符(
a...b
)定义了一个从a
到b
的范围,包括a
和b
的值。a
的值不能大于b
;在需要使用所有值的情况下很有用
e.g.
1
2
3for index in 1...5 {
print("\(index) 乘以 5 等于 \(index * 5)")
}
半开区间运算符(
a..<b
)定义了一个从a
到b
但不包括b
的范围.对于处理从基数 0 开始的列表(如数组)时特别有用,因为它可以计数到列表长度(但不包括列表长度).
e.g.
1
2
3
4
5let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 个人叫 \(names[i])")
}
闭区间运算符有一种替代形式,用于一直延伸到尽可能远的区间 —— 例如,一个包含从索引 2 到数组末尾所有元素的区间。
1
2
3for name in names[2...] { print(name) }
// Brian
// Jack半开区间运算符也有一种只写最后一个值的单侧形式
1
2
3for name in names[..<2] { print(name) }
// Anna
// Alex
逻辑运算
[!NOTE]
Swift 逻辑运算符
&&
和||
遵循从左到右的结合顺序,这意味着带有多个逻辑运算符的复合表达式会首先评估最左边的子表达式.
控制流
协议
- 如果类需要继承, 需要将父类写在所有的协议之前
- 不能在协议定义中为方法参数指定默认值。
- 协议也可以要求遵循协议的类型 实现指定的构造器, 和协议内部的方法一样, 不需要写花括号和构造期的实体
- 如果是类, 必须在构造函数的开头加上
required
修饰符. 这是为了确保所有继承的子类也提供这个构造函数的实现, 从而确保遵守协议; - 但是如果一个类被声明为了
final
, 也就是无法被继承, 那么就不需要required
的声明
- 如果是类, 必须在构造函数的开头加上
协议的基本用法
协议可以规定属性类型以及属性被操作的权限, 通常和类、结构体和枚举进行绑定, 作为一种强制的约束.
1 | protocol Tax{ |
同样可以在协议中使用 mutating
来声明一个改变自身属性的方法:
1 | protocol Tax{ |
需要注意的是, 结构体内部的方法如果要修改自身属性, 也需要声明
mutating
, 但是类则不需要额外的声明.1
2
3
4
5
6
7
8struct Taxas: Tax{
var national: Double
var individual: Double
mutating func changeTax(newValue: Double){
national = newValue
}
}
补充协议
使用
,
连接不同的协议extension
可以为协议的函数设置默认方法, 就不需要继续在每一个类、结构体或枚举中重新定义同样可以补充数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14extension Int {
var abs: Int {
get {
if self >= 0 {
return self
}else{
return -self
}
}
}
}
print((-3).abs);
// 3
有条件地遵循协议
让 Array
类型只要在存储遵循 TextRepresentable
协议的元素时,就遵循 TextRepresentable
协议:
1 | extension Array: TextRepresentable where Element: TextRepresentable { |
扩展里声明协议遵循
当一个类型已经遵循了某个协议中的所有要求,却还没有声明遵循该协议时,可以通过空的扩展来让它遵循该协议:
1 | struct Hamster { |
Error handling
系统提供了 Error
协议用于错误处理, 主动给予错误的捕捉情况.
使用方法:
- 定义遵循相关协议的枚举类型, 作为错误的类型;
- 定义可能抛出错误的函数;
- 使用
do...catch
块来结构化地处理错误.
e.g.
1 | // 定义一个错误类型,遵循 Error 协议 |
- throws:在函数声明中标注该函数会抛出错误;
- try:在调用可能抛出错误的函数时使用.
其他的协议
CaseIterable
用于获取枚举的属性个数, 从而进行遍历.
1 | enum Status:CaseIterable{ |
for _ in 0..<
当中的_
表示忽略遍历时候的循环变量的值.
闭包
闭包的简化推导
相当于匿名函数与 lambda
. 接下来从普通函数的写法开始简化:
普通函数:
1
2
3
4
5
6
7
8func changeSign(op: Double) -> Double {
return -op
}
var operation: (Double) -> Double
operation = changeSign
let result = operation(4.0) // result = -4.0将函数的定义下移:
1
2
3
4var operation: (Double) -> Double
operation = (op:Double) -> Double { return -op}
let result = operation(4.0)将
{
提前,并在原来的位置添加in
1
2
3var operation:(Double) -> Double
operation = {(op: Double) -> Double in return -op}
...系统可以推断类型, 所以根据输入的类型简化返回值的类型定义
1
2
3var operation:(Double) -> Double
operation = {(op: Double) in return -op}
...可以进一步省略传入的类型
1
2
3var operation:(Double) -> Double
operation = { (op) in return -op}
...省略返回的标记
return
:1
2
3var operation:(Double) -> Double
operation = { (op) in -op}
...
最后, 我们可以直接用 $0
等替代传入的参数, 也就是省略了参数的名称!
1 | var operation:(Double) -> Double |
闭包的常见使用
e.g.
Trailing Closure
当闭包是函数的最后一个参数时,
1 | let result = applyTwice(3, operation: { $0 * 2 }) |
可以改写成:
1 | let result = applyTwice(3) { $0 * 2 } // result = 12 |
慕课学习杂记
something
去官网学习新出现的技术
- codeML
函数也可以赋值给变量
省略外部参数名
- 外部参数名: 在函数调用的时候使用, 提高可读性;
- 内部参数名: 在函数体的内部使用
如果如此定义:
1 | func greet(person name: String){ |
那么在调用的时候必须显示声明外部参数名:
1 | greet( person: "Alice") |
如果我们希望省略外部参数名, 就可以在定义函数的时候用 _
来代替:
1 | func greet(_ name: String){ |
高阶函数
我们可以让函数作为另一个函数的输入参数:
1 | func addTwoInts( _ a: Int, _ b: Int) -> Int{ |
注意函数作为参数的时候, 类型的定义就是输入类型和返回类型, 也是用
,
来分隔不同的参数.
内置的库
AVFoudation
: 音频播放
枚举内部也可以设置方法;
结构体本身不需要构造函数(因为swift存在对于结构体的默认构造) , 但是如果结构体内部的属性存在这样的属性:
- 它可能是枚举内部的方法, 跟枚举的属性有关, 可能使用了
switch
来根据枚举属性赋值.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25enum Type{
case Cike
case ...
func blood()-> Double{
switch self{
case .Cike: return 10
case .Fashi: return ...
...
}
}
}
struct Card {
var country: Country
vat type: Type
var blood: Double
init (country: Country, type: Type){
self.country = country
self.type = type;
blood = type.blood
}
}- 它可能是枚举内部的方法, 跟枚举的属性有关, 可能使用了
结构体和枚举属于 值类型, 如果赋值的时候进行拷贝操作;
- 如果结构体声明为
let
, 即使属性是变量, 那么也无法修改内部的属性,
- 如果结构体声明为
类是引用类型, 赋值的时候使得左值指向了同样的内存区域, 也就是信息保持一致, 更改同步
- 如果我们将类声明为常量, 相当于cpp的指针常量, 也就是说类内部的属性可以更改, 但是无法修改这个量指向的内存区域.
计算属性
访问的时候动态计算得到.
下面通过一个矩形的例子来说明:
1 | struct Point { |
上述完成了结构体的计算属性的定义, 其中 get
部分也可以优化为:
1 | get{ |
这是因为, 如果
get
部分只存在一个表达式, 就会自动将其返回, 不需要显式声明return
关键字.
然后可以如此应用:
1 | var currCenter = rectElement.center |
set
提供了语法糖, 也就是可以直接访问oldValue 和 newValue, 因此我们可以如此改写:
1 | set{ |
[!NOTE]
对于只读的计算属性, 由于不存在
set
, 我们可以直接在花括号内定义返回内容.
声明
static
, 表示这个属性或者方法属于整个类型而非某个实例. 此时相应的, 我们使用<tyepName>.<strtic attribute>
的方式来访问.子面量本身是不可修改的, 下面的拓展中, 如果写作
var someInt = 3.square()
就会报错
1 | extension Int{ |
控制器存在5种状态:
- 未加载
- 将要出现
- 出现
- 将要消失
- 已经消失
APP的状态:

UiSceneDelegate
用于响应基于 scene
的生命周期事件.
可以使用属性的 didset
来便捷地检测变化并快速修改:
1 | var score = 0{ |
viewController
是新的页面;view
是视图, 可以叠加在页面上.UIAlertController
组件相当于警示的弹窗组件
作图和绘画
CG
CGFloat
用于二维坐标系中的坐标数据:let coor_x = CGFloat(10.5)
CGPoint(x: .. , y:...)
CGSize
包含width和height属性的结构体;CGRect
包含点和尺寸的矩形1
2
3
4struct CGRect{
var origin: CGPoint
var size: CGSize
}其他属性
e.g.
1
2
3
4var minX: CGPoint
var midY: CGPoint
intersects(CGRect) -> Bool // 判断是否存在交集
contains(CGPoint) -> Bool // 是否包含点.
最小单元是 Point
而非像素点.
bound表示视图内部允许绘制的区域:
1 | var bounds: CGRect // 也就是一个矩形 |
frame 视图在父视图中的位置:
1 | var frame: CGRect |
自定义视图
绘制自定义视图通常通过创建一个自定义的 UIView 子类,并重写 draw(_:) 方法来实现.
playGround中的实例:
1 | import UIKit |
touchesBegan
–WWDCSCNVector3
是三维向量DispathchQueue
表示创建一个异步的进程
传感器
- 加速度的方向伴随手机的头部旋转保持不变;
- 以
g
为描述单位; - 面向使用者的方向是
z
轴.
- 以
- 陀螺仪
- 记录对应用三个轴
- roll, pitch, yaw
动画
结绳记事
操作演示.
按住
ctrl
将视图中的组件拖放到代码中.右下角的几个功能:
- 约束;
- 选择视图
ctrl + option + cmd + enter
快速显示代码区域- 50:00 左右介绍了两种类型和交互
按住
optional
然后 hover 在类上, 可以显示对应的基础操作.可选值的本质是枚举类型!
1
2
3
4enum Optional<T>{
case none
case some(<T>)
}在项目中显示Md格式:
1
2
3/*:
...
*/选择在运行时隐藏实际存在的代码:
1
2
3
4
5//#-hidden-code
import PlaygroundSupport
...
//#-end-hidden-code
Swift UI
AR
动画
缩放变换
1 | NavigationLink{ |
Symbol 6
动画
- 使用
晃动
在复杂的UI中提示可交互性. 旋转
动画来表示正在进行的进程
基本语法
磨砂效果
1 | VStack { |
搜索栏
状态管理:
1
2
3
4
5// 存储搜索文本
private var searchText = ""
// 可选:跟踪搜索是否处于活动状态
private var isSearching = false数据过滤模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// 基本过滤计算属性模板
var filteredItems: [ItemType] {
if searchText.isEmpty {
return originalItems
} else {
return originalItems.filter { item in
// 根据需要自定义过滤条件
item.name.localizedCaseInsensitiveContains(searchText) ||
item.description.localizedCaseInsensitiveContains(searchText)
}
}
}
// 处理嵌套数据结构的过滤模板
var filteredNestedItems: [ParentType] {
if searchText.isEmpty {
return originalParentItems
} else {
return originalParentItems.compactMap { parent in
let matchedChildren = parent.children.filter { child in
child.name.localizedCaseInsensitiveContains(searchText)
}
if matchedChildren.isEmpty {
return nil
} else {
// 创建包含匹配子项的新父项
return ParentType(id: parent.id, name: parent.name, children: matchedChildren)
}
}
}
}搜索UI中的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16NavigationStack {
List {
// 使用过滤后的数据源
ForEach(filteredItems) { item in
// 列表项视图
}
}
.navigationTitle("标题")
.searchable(text: $searchText, prompt: "搜索提示文字")
// 可选:添加搜索建议
.searchSuggestions {
ForEach(suggestions, id: \.self) { suggestion in
Text(suggestion).searchCompletion(suggestion)
}
}
}
左右适应的外边距
通过 HStack
与 space
实现卡片的自适应扩展, 同时利用 .frame(maxwidth:...)
来设置一个最大的卡片宽度
1 | HStack{ |
Spacer(minLength: 10)
表示保证自己不会小于 minLength.
上述的 Spacer
会压缩卡片的内容, 如果希望直接设置卡片在父容器中的左右外边距, 应该在卡片的内部使用 padding
:
1 | Text(item.description) |
全局统一样式
1 | @main |
参数标签和参数名
- 参数标签用于函数调用时;
- 参数名用于函数内部的参数名称.
e.g.:
1 | func greet(person atName: String) { |
也可以使用 _
来省略调用时的参数标签:
1 | func greet(_ name: String) { |
Alert
swift UI中的弹窗提示同样通过本地的 @State
变量来实现:
1 | private var showingPaymentAlert = false |
设置按钮来改变可见的状态:
1 | Button("Confirm order") { |
自定义绑定
我们也可以使用 Binding
类型手动创建绑定,该类型可以提供自定义 get
和 set
闭包,以便在读取或写入值时运行。
Foreach
1 | ForEach(item.restrictions) { restriction in |
此时, 要求 item.restrictions
具有可唯一标识的 id
字段.
如果内容本身就是唯一标识, 比如说遍历的内容是字符串数组, 那么可以如此声明:
1
2
3ForEach(item.restrictions, id: \.self) { restriction in
Text(restriction)
}
Spacer()
用于填充剩余的空间
- 使用
offset(x:.., y:...)
来调节位置, 左上角是原点.
环境变量
作用: 用于存储独立于视图的、长期存在的数据;
e.g.
1
2// App.swift
var order = Order()@StateObject
属性包装器负责在应用程序的整个生命周期中保持对象处于活动状态。需要在创建视图结构体的时候传递:
1
2
3
4WindowGroup {
ContentView()
.environmentObject(order)
}为了让swift知道什么时候更新视图, 常用的是声明
@Published
属性包装器——足以让它更新任何正在监视更改的 SwiftUI 视图.同时声明对应的对象遵循可观测协议: ObservableObject.
我们可以使用 @EnvironmentObject
来访问环境中的共享数据, 也就是传递上一步已经在父视图中创建和管理的对象.
e.g.
1 | class UserData: ObservableObject { |
使用 @State
来声明简单的本地值——比如整数和字符串.
建议将其声明为
private
, e.g.1
private var paymentType = "Cash"
菜单视图
为了将菜单视图存放在一个选项卡当中, 我们需要新建一个视图, 用来作为容器:
1 | struct MainView: View { |
页面级别的切换.
使用枚举与子页面的内容分区:
1 | import SwiftUI |
合适的修饰符
图像自动调节尺寸:
1
2
3Image(item.mainImage)
.resizable()
.scaledToFit()
设置阴影
1 | .shadow(color: .black.opacity(0.2), |
为按钮设置动画
1 | struct ContentView: View { |
可以进一步设置, 比如弹簧的渐入渐出:
1 | Toggle("Toggle label", isOn: $showingWelcome.animation(.spring())) |
基本操作
快捷键
ctrl
按住后点击VStack
可以快速地将其添加到ZStack
当中- 颜色的设置需要通过
ZStack
来实现.
- 颜色的设置需要通过
option
可以显示当前类的介绍
推荐资源:
- Raywenderlich 的 SwiftUI 教程: Raywenderlich - SwiftUI Apprentice
- Big Mountain Studio 的免费电子书: SwiftUI Views Quick Start
- 标题: Swift学习摘记
- 作者: ffy
- 创建于 : 2025-04-17 20:21:21
- 更新于 : 2025-05-10 10:25:38
- 链接: https://ffy6511.github.io/2025/04/17/编程语言/swift/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。