抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

基础整合记录,方便下次梭哈。

1.前提

手册基于以下环境编写:

1
2
3
4
本篇文章基于以下IDE、SDK、插件及版本。
Xcode - 14+
SwiftUI - 4+
iOS - 15+

2.官方文档&参考网站

3.常用注解

3.1.@State

通过@State注解的变量,用来表示管理页面的“状态”。

3.2.@Binding

通过@Binding注解的变量,用于子组件接收父组件的状态,
子组件的状态使用@Binding注解,父组件调用子组件时传参需要使用$符号。

3.3.@Environment

通过@Environment注解的变量可获取软件当前的环境变量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取当前系统的主题模式
@Environment(\.colorScheme)

// 获取当前系统的字体大小
@Environment(\.sizeCategory)
似乎在iOS16.4中会废弃,并被dynamicTypeSize代替

// 获取当前控制器的尺寸
@Environment(\.controlSize)

// 获取销毁当前模态视图的方法
@Environment(\.dismiss)
等于presentationMode.wrappedValue.dismiss()

3.4.@AppStorage

@AppStorage注解声明一个持久化存储的变量,变化的值即使在App关闭后重新打开,
也能保持最后的值。

3.5.@Namespace

@Namespace声明一个命名空间,一般用来同步动画等。

1
2
// 使用命名空间实现动画 类似于flutter中的hero动画
.matchedGeometryEffect(effectId, namespace)

3.6.@Published

@Publish声明的值,会在该值发生改变时,同步更新UI。

3.7.@EnvironmentObject

@EnvironmentObject注解声明一个App运行时的状态;
与@AppStorage不同, 它不是持久性的状态;
也与@State不同,它可以在全局使用。

使用@EnvironmentObject时,Xcode中预览preview,
需要带上.environmentObject实例化一个使用@EnvironmentObject注解相同的对象。

使用@EnvironmentObject注解的对象,需要在App初始化时,
即在入口类(:App)中,使用@StateObject预先注入。

1
2
3
4
5
6
7
8
9
10
11
// 声明
import Combine
class Model: ObservableObject {
@Published var param: Bool = false
}

// 注入
@StateObject var model = Model()

// 使用
@EnvironmentObject var model: Model

3.8.@StateObject与@ObservedObject

@StateObject是@State的进阶,声明的变量如果是对象时,则该使用@StateObject,
声明的变量不会因为UI的重新创建而重复创建。

@ObservedObject相对于@StateObject,它会随着UI的重新创建而重新初始化。

无论使用@StateObject还是@ObservedObject,注解的变量都需要继承ObservableObject。

3.9.@FocusState

@FocusState注解声明一个Field对象,可以用来判断当前页面上的焦点聚焦在哪一个Input中。

3.10.@MainActor

@MainActor注解声明在方法或者结构体之上,声明的方法必须是异步方法,使用async关键词。

4.常用组件

4.1.修饰器

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 对齐方式
.alignment
// 间距
.spacing
// 内边距
.padding
// 组件大小
.frame
// 浮层
.overlay
// 背景
.background
// 前景样式
.foregroundStyle
// 无视安全区域
.ignoresSafeArea
// 混合模式
.blendMode
// 模糊
.blur
// 透明度
.opacity
// 阴影
.shadow
// 偏移
.offset
// 旋转效果
.rotationEffect
// 3D旋转效果
.rotation3DEffect
// 色调
.hueRotation
// 禁用操作
.allowsHitTesting
// 显示时
.onAppear
// 消失时
.onDisappear
// 执行任务
.task
// 刷新时执行任务
.refreshable
// 定义模态视图
.sheet
// 布局优先度
.layoutPriority

4.2.Navigation

NavigationView声明一个带导航功能的页面,
可以使用NavigationLink、Link等进行页面导航。

1
2
3
4
5
6
7
// NavigationView常用修饰
// 搜索
.searchable
// 标题
.navigationTitle
// 操作栏
.toolbar

4.3.List

List声明一个列表,列表中可以使用Section进行分组。

4.4.分组

1
2
3
4
5
6
7
8
// 分组
Group{}
// 水平视图(Row)
HStack{}
// 垂直视图(Column)
VStack{}
// 层叠视图
ZStack{}

4.5.颜色与形状

颜色使用Color类,颜色中常用Color(.clear)实现无色区域,
可用来实现占位、注册组件等。

1
2
3
4
5
6
7
8
// 圆形
Circle
// 矩形
Rectangle
// 圆角矩形
RoundedRectangle
// 使用.fill实现填充
Shape.fill(.linearGradient, .angularGradient)

4.6.Text

1
2
3
4
5
6
7
// Text文本组件常用修饰器
// 字体
.font
// 轻重
.fontWeight
// 行数限制
.lineLimit

4.7.Image

1
2
3
4
5
6
7
// Image图片组件常用修饰器
// 重定义尺寸
.resizable
// 缩放至fit
.scaledToFit
// 缩放至fill
.scaledToFill

4.8.Button

1
2
3
4
5
6
7
// Button图片组件常用修饰器
// 按钮样式
.buttonStyle
// 颜色
.tint
// 尺寸
.controlSize

4.9.TextField与SecureField

TextField与SecureField都是文本输入器,
其中TextField为明文,SecureField是密文(可用来作为密码的输入)。

1
2
3
4
5
6
7
8
9
10
11
// TextField与SecureField输入组件常用修饰器
// 输入类型
.textContentType
// 键盘类型
.keyboardType
// 自动大写
.autocapitalizatior
// 关闭自动纠正
.disableAutocorrection
// 焦点设置
.focused(field, equals)

4.10.Spacer

Spacer是占位符。它会将剩余空间全部填满,以实现撑满父容器。

4.11.Image

Image可调用系统预先设置好的图片,亦可使用苹果的SF符号(系统自带图标)。

1
2
3
4
// 预设图片
Image("image")
// 系统图标
Image(systemName: "icon")

4.12.AsyncImage

AsyncImage是异步图片加载组件。可以对其状态不同的情况,
设置占位符、加载失败等情况所显示的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
AsyncImage(url: URL(string: "https://..."), transaction: Transaction(animation: .easeOut)) { phase in
switch phase {
case .success(let image):
image.resizable()
.transition(.scale(scale: 0.5, anchor: .center))
case .empty:
ProgressView()
case .failure(_):
Color.gray
@unknown default:
EmptyView()
}
}

4.13.ProgressView

ProgressView是一个加载动画,可用作异步等待时的占位符,
例如异步加载图片的AsyncImage。

5.常用功能

5.1.enum与case

与其他语言相似,swift也有枚举。swift中的枚举使用很方便,可以用来做状态、类型判断。

1
2
3
4
5
6
7
8
9
// 声明
enum Field: Hashable {
case username
case password
}

// 使用
equals: .username
equals: .password

5.2.URLSession

使用URLSession进行接口调用。

1
2
3
4
5
6
7
do {
let url = URL(string: "url")!
let (data, _) = try await URLSession.shared.data(from: url)
result = try JSONDecoder().decode(Address.self, from: data)
} catch {
result = ...
}

5.3.GeometryReader

GeometryReader是很有用的工具,它可以读取当前组件的坐标,
可以搭配PreferenceKey实现组件位置监视、滚动检测或者实现视差效果等。

GeometryReader的参照坐标,使用.coordinateSpace(name: …)修饰符声明。

1
2
3
4
// 参照
.coordinateSpace(name: ...)
// 对照
GeometryReader{proxy in proxy.frame(in: .named(...))}

5.4.PreferenceKey

PreferenceKey是自定义键,搭配.preference修饰符及.onPreferenceChange修饰符,
可以用来实现监视效果。

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
// 使用PreferenceKey监视并实现滚动检测
// 声明
struct MyPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
// 使用
GeometryReader { proxy in
Color.clear.preference(
key: ScrollPreferenceKey.self,
value: proxy.frame(in: .named("scroll")).minY
)
}
.frame(height: 0)
.onPreferenceChange(ScrollPreferenceKey.self, perform: { value in
withAnimation(.easeInOut) {
if value < 0 {
hasScrolled = true
} else {
hasScrolled = false
}
}
})

5.4.Foreach

Swift中的循环使用以下格式:

1
2
// Array([].enumerated())将数组变换为可循环
Foreach(Array([].enumerated()), \.offset) {index, item in ...}

5.5.Identifiable

实体类可继承自Identifiable实现唯一性,需要实现id成员。

1
2
3
4
// 使用UUID()作为唯一ID
struct Entity: Identifiable {
let id = UUID()
}

5.6.自定义修饰器

自定义修饰器可实现与原生修饰器一样的使用效果以及便捷性。

自定义修饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明
struct TmpModifier: ViewModifier {
func body(content: Content) -> some View {
content...
}
}
// 注册
extension View {
func tmpStyle() -> some View {
modifier(TmpModifier())
}
}
// 使用
View.tmpStyle()

自定义动画效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 声明
struct AnimatableFontModifier: AnimatableModifier {
var size: Double
var weight: Font.Weight = .regular
var design: Font.Design = .default
func body(content: Content) -> some View {
content.font(.system(size: size, weight: weight, design: design))
}
}
// 注册
extension View {
func animatableFont(size: Double, weight: Font.Weight = .regular, design: Font.Design = .default) -> some View {
self.modifier(
AnimatableFontModifier(size: size, weight: weight, design: design)
)
}
}
// 使用
View.animatableFont()

自定义按钮样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明
struct AngularButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label...
}
}
// 注册
extension ButtonStyle where Self == AngularButtonStyle {
static var angular: Self {
return .init()
}
}
// 使用
Button.buttonStyle(.angular)

自定义动画(包装使用):

1
2
3
4
5
6
// 注册
extension Animation {
static let tmpAnimation = Animation.spring(response: 0.5, dampingFraction: 0.7)
}
// 使用
withAnimation(.tmpAnimation) {...}

5.7.Gesture

Gesture可实现手势操作。常规用法如下:

1
2
3
4
5
6
7
8
// 声明
var drag: some Gesture {
DragGesture
.onChanged{}
.ended{}
}
// 使用
View.gesture(drag)

6.其他

6.1.Type与self

.Type获取变量类型,.self获取变量自身,需在不同使用场景精准使用。

6.2.裁切,修饰符的顺序

.mask、.cornerRadius等修饰会裁切组件,切断父组件与子组件的修饰符联系;
例如父组件设置设置了阴影,但不想应用于子组件,可以使用会裁切的修饰符进行裁切。

6.3.Canvas

灵活运用Canvas画布,可画出精美的形状、图标。它比直接导入图片来占用的体积小得多,
并且自定义性很强,增强性能。

1
2
3
4
5
6
7
// 使用画布绘出文字、图形、图片、图标
Canvas { context, size in
context.draw(Text("text").font(.largeTitle), at: CGPoint(x: 50, y: 20))
context.fill(Path(ellipseIn: CGRect(x: 20, y: 30, width: 100, height: 100)), with: .color(.pink))
context.draw(Image("image"), in: CGRect(x: 0, y: 0, width: 200, height: 200))
context.draw(Image(systemName: "hexagon.fill"), in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
}

6.4.TimelineView

使用TimelineView可以实现绘制动画效果,可用来制作精美的背景等。
TimelineView使用难度略高,可复制大佬的path动画进行修改。

6.5.MARKDOWN

Swift支持MARKDOWN语法。

1
2
3
4
// 倾斜
Text("_string_")
// 加粗
Text("__string__")

6.6.限制系统对组件的影响

.dynamicTypeSize可设置组件受系统设置的字体大小的影响范围,
增加一定的局限性,例如限制后文字不会因为系统设置的文字过大,
而导致应用文字过大而超出组件边框的范围。

6.6.Accessibility

合适的Accessibility相关设置可能不会让你的App更好看,
但可以使残障认识更易于使用系统。

1
2
3
4
5
6
7
8
9
10
// 隐藏组件(用于隐藏系统对背景的识别)
.accessibility(hidden: false)
// 定义为组件
.accessibilityElement()
// 定义为组件(将自身及子组件定义为一个整体)
.accessibilityElement(children: .combine)
// 定义组件标题(用于朗读)
.accessibilityLabel("")
// 定义组件类型
.accessibilityAddTraits(.isButton,.isModal)

评论