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

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

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)

评论