基础整合记录,方便下次梭哈。
1.前提
手册基于以下环境编写:
本篇文章基于以下IDE、SDK、插件及版本。
Xcode - 14+
SwiftUI - 4+
iOS - 15+
2.官方文档&参考网站
3.常用注解
3.1.@State
通过@State注解的变量,用来表示管理页面的“状态”。
3.2.@Binding
通过@Binding注解的变量,用于子组件接收父组件的状态,
子组件的状态使用@Binding注解,父组件调用子组件时传参需要使用$符号。
3.3.@Environment
通过@Environment注解的变量可获取软件当前的环境变量,例如:
// 获取当前系统的主题模式
@Environment(\.colorScheme)
// 获取当前系统的字体大小
@Environment(\.sizeCategory)
似乎在iOS16.4中会废弃,并被dynamicTypeSize代替
// 获取当前控制器的尺寸
@Environment(\.controlSize)
// 获取销毁当前模态视图的方法
@Environment(\.dismiss)
等于presentationMode.wrappedValue.dismiss()
3.4.@AppStorage
@AppStorage注解声明一个持久化存储的变量,变化的值即使在App关闭后重新打开,
也能保持最后的值。
3.5.@Namespace
@Namespace声明一个命名空间,一般用来同步动画等。
// 使用命名空间实现动画 类似于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预先注入。
// 声明
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.修饰器
// 对齐方式
.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等进行页面导航。
// NavigationView常用修饰
// 搜索
.searchable
// 标题
.navigationTitle
// 操作栏
.toolbar
4.3.List
List声明一个列表,列表中可以使用Section进行分组。
4.4.分组
// 分组
Group{}
// 水平视图(Row)
HStack{}
// 垂直视图(Column)
VStack{}
// 层叠视图
ZStack{}
4.5.颜色与形状
颜色使用Color类,颜色中常用Color(.clear)实现无色区域,
可用来实现占位、注册组件等。
// 圆形
Circle
// 矩形
Rectangle
// 圆角矩形
RoundedRectangle
// 使用.fill实现填充
Shape.fill(.linearGradient, .angularGradient)
4.6.Text
// Text文本组件常用修饰器
// 字体
.font
// 轻重
.fontWeight
// 行数限制
.lineLimit
4.7.Image
// Image图片组件常用修饰器
// 重定义尺寸
.resizable
// 缩放至fit
.scaledToFit
// 缩放至fill
.scaledToFill
4.8.Button
// Button图片组件常用修饰器
// 按钮样式
.buttonStyle
// 颜色
.tint
// 尺寸
.controlSize
4.9.TextField与SecureField
TextField与SecureField都是文本输入器,
其中TextField为明文,SecureField是密文(可用来作为密码的输入)。
// TextField与SecureField输入组件常用修饰器
// 输入类型
.textContentType
// 键盘类型
.keyboardType
// 自动大写
.autocapitalizatior
// 关闭自动纠正
.disableAutocorrection
// 焦点设置
.focused(field, equals)
4.10.Spacer
Spacer是占位符。它会将剩余空间全部填满,以实现撑满父容器。
4.11.Image
Image可调用系统预先设置好的图片,亦可使用苹果的SF符号(系统自带图标)。
// 预设图片
Image("image")
// 系统图标
Image(systemName: "icon")
4.12.AsyncImage
AsyncImage是异步图片加载组件。可以对其状态不同的情况,
设置占位符、加载失败等情况所显示的内容。
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中的枚举使用很方便,可以用来做状态、类型判断。
// 声明
enum Field: Hashable {
case username
case password
}
// 使用
equals: .username
equals: .password
5.2.URLSession
使用URLSession进行接口调用。
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: …)修饰符声明。
// 参照
.coordinateSpace(name: ...)
// 对照
GeometryReader{proxy in proxy.frame(in: .named(...))}
5.4.PreferenceKey
PreferenceKey是自定义键,搭配.preference修饰符及.onPreferenceChange修饰符,
可以用来实现监视效果。
// 使用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中的循环使用以下格式:
// Array([].enumerated())将数组变换为可循环
Foreach(Array([].enumerated()), \.offset) {index, item in ...}
5.5.Identifiable
实体类可继承自Identifiable实现唯一性,需要实现id成员。
// 使用UUID()作为唯一ID
struct Entity: Identifiable {
let id = UUID()
}
5.6.自定义修饰器
自定义修饰器可实现与原生修饰器一样的使用效果以及便捷性。
自定义修饰器:
// 声明
struct TmpModifier: ViewModifier {
func body(content: Content) -> some View {
content...
}
}
// 注册
extension View {
func tmpStyle() -> some View {
modifier(TmpModifier())
}
}
// 使用
View.tmpStyle()
自定义动画效果:
// 声明
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()
自定义按钮样式:
// 声明
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)
自定义动画(包装使用):
// 注册
extension Animation {
static let tmpAnimation = Animation.spring(response: 0.5, dampingFraction: 0.7)
}
// 使用
withAnimation(.tmpAnimation) {...}
5.7.Gesture
Gesture可实现手势操作。常规用法如下:
// 声明
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画布,可画出精美的形状、图标。它比直接导入图片来占用的体积小得多,
并且自定义性很强,增强性能。
// 使用画布绘出文字、图形、图片、图标
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语法。
// 倾斜
Text("_string_")
// 加粗
Text("__string__")
6.6.限制系统对组件的影响
.dynamicTypeSize可设置组件受系统设置的字体大小的影响范围,
增加一定的局限性,例如限制后文字不会因为系统设置的文字过大,
而导致应用文字过大而超出组件边框的范围。
6.6.Accessibility
合适的Accessibility相关设置可能不会让你的App更好看,
但可以使残障认识更易于使用系统。
// 隐藏组件(用于隐藏系统对背景的识别)
.accessibility(hidden: false)
// 定义为组件
.accessibilityElement()
// 定义为组件(将自身及子组件定义为一个整体)
.accessibilityElement(children: .combine)
// 定义组件标题(用于朗读)
.accessibilityLabel("")
// 定义组件类型
.accessibilityAddTraits(.isButton,.isModal)