基础整合记录,方便下次梭哈。
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
|
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)
|