SwiftUI 属性包装器

@Lazy:参见惰性属性

自定义属性包装器,用于延迟初始化属性,只有在首次访问时才进行初始化。这对于代价昂贵的初始化操作非常有用。

@NSCopying

自定义属性包装器,用于确保属性始终是一个副本,而不是引用。这对于处理可变对象时非常有用。

@AutoClosure

自定义属性包装器,用于自动封装表达式为闭包,使其在需要时被延迟执行。

@available

用于标记属性的可用性,以指定在不同版本的 Swift 或操作系统中是否可用。

@escaping

在Swift中,将 @escaping 关键词添加到函数参数中,表示该闭包可以超出函数调用范围而继续存在。

这意味着闭包可能会在函数执行完毕后继续存在,并且可能被存储在其他地方以供稍后调用。这种情况通常发生在需要异步操作的函数中,例如回调函数。通过使用@escaping关键词,告诉函数调用者闭包的生命周期可能会超出函数范围,因此调用者需要注意避免保留循环和内存泄漏。这也告诉Swift编译器这是有意为之的。在闭包可能会超出函数作用域的情况下,必须使用 @escaping 关键词。

init(location: Location, onSave: @escaping (Location) -> Void) {
    self.location = location
    self.onSave = onSave
    _name = State(initialValue: location.name)
    _description = State(initialValue: location.description)
}

<aside> 💡 在使用 @escaping 闭包时,需要注意避免循环引用和内存泄漏问题。

</aside>


@State :参见 @State

@State 关键词:是一个特殊的属性或特殊的数据,在宣告属性为 @State 時,SwiftUI 会管理 该参数的儲存方式,无论在哪里改变它,都会刷新body,这样一来,body和状态将始终同步。SwiftUI就是通过这样的方式,让用户界面和状态总是一致。

@Binding :参见 @Binding 和 @Bindable

@Binding 关键词:用于组件视图和主视图之间的数据沟通。有时候,当抽出子视图后,有些变量会放到子视图,而不是主视图 ContentView 。那子视图 和 主界面之间的数据如何绑定呢?这时就要用到 @Binding 来和主界面的参数绑定在一起了。

@Environment :参见 @Environment

用于 SwiftUI 中,用于获取环境变量的值,例如主题、字体设置等。通过 @Environment 属性包装器,你可以轻松访问这些环境值。


以下几个已逐渐被弃用,参见:@EnvironmentObject

**@Published:**将属性标记为 @Published 以便在数据发生更改时发布通知,通常与 ObservableObject 一起使用,用于视图中的数据绑定

**@ObservedObject:**在视图中引入遵循 ObservableObject 协议的数据模型。它创建一个与数据模型的绑定,以便在数据变化时触发视图的更新

**@EnvironmentObject:**将 ObservableObject 对象注入环境


自定义属性包装器

属性包装器实际上只是一些技巧:它们将一个简单的值包装在另一个值中,以便可以添加一些额外的功能。不管是通过 @State 在其他地方存储值,还是使用 @Environment 从共享数据源读取值,其原理是相同的。

我们甚至可以自定义自己的属性包装器。例如从包装某种 BinaryInteger 值的简单结构开始,在设置其包装值时,我们将给这个包装器一些自定义代码(如果新值低于 0,就会将其设置为 0,这样该结构就永远不会为负数),代码如下所示:

struct NonNegative <Value: BinaryInteger> {
    var value: Value

    init(wrappedValue: Value) {
        if wrappedValue < 0 {
            value = 0
        } else {
            value = wrappedValue
        }
    }

    var wrappedValue: Value {
        get { value }
        set {
            if newValue < 0 {
                value = 0
            } else {
                value = newValue
            }
        }
    }
}

//现在可以用一个整数来创建它,但如果该整数低于 0,那么它将被限制为 0
var example = NonNegative(wrappedValue: 5)
example.wrappedValue -= 10
print(example.wrappedValue)  //打印 0

属性包装器可以将其用于结构或类中的任何类型的属性。要实现这个只需要一步:

//1. 在 NonNegative 结构体之前写入 @propertyWrapper ,即可拥有自己的属性包装器!
@propertyWrapper
struct NonNegative <Value: BinaryInteger> {
	...
}

//2. 具体使用
//属性包装器只能用于属性,而不能用于普通变量或常量,因此为了尝试包装器,必须将它放在像这样的 User 结构中
struct User {
    @NonNegative var score = 0
}
var user = User()
user.score += 10
print(user.score)
user.score -= 20    //打印 0
print(user.score)

这里没有什么神奇之处:属性包装器只是将一条数据包裹在另一条数据周围的语法糖。如果需要我们也可以自己制作它们。


SwiftUI 环境值 @Environment

SwiftUI 中常用的环境值有以下这些,我们可以从环境中获取它们的值,赋予变量:@Environment(\\.colorScheme) var colorScheme

名称 用途说明 枚举值或示范用例
colorScheme 当前界面的颜色模式(浅色或深色) .dark, .light
locale 当前应用的语言环境设置 Locale.current
calendar 当前系统使用的日历 Calendar.current
timeZone 当前系统使用的时区 TimeZone.current
displayScale 获取当前显示屏的缩放比例 2.0, 3.0
sizeCategory 当前的内容大小类别(字体大小) .extraSmall, .small, .medium, .large, .extraLarge, .extraExtraLarge, .extraExtraExtraLarge, .accessibilityMedium, .accessibilityLarge, .accessibilityExtraLarge, .accessibilityExtraExtraLarge, .accessibilityExtraExtraExtraLarge
horizontalSizeClass 当前界面的水平尺寸类别(紧凑或常规) .compact, .regular
verticalSizeClass 当前界面的垂直尺寸类别(紧凑或常规) .compact, .regular
layoutDirection 当前界面的布局方向(从左到右或从右到左) .leftToRight, .rightToLeft
deviceOrientation 当前设备的方向 .unknown, .portrait, .portraitUpsideDown, .landscapeLeft, .landscapeRight, .faceUp, .faceDown
accessibilityEnabled 当前是否启用了辅助功能 true, false
colorSchemeContrast 当前颜色模式的对比度设置 .standard, .increased
presentationMode 当前视图的呈现模式 presentationMode.wrappedValue.dismiss()
dismiss 获取“关闭当前视图”的方法 dismiss()
undoManager 当前视图的撤销管理器 UndoManager()
redactionReasons 当前视图的隐蔽理由 .placeholder, .privacy
editMode 当前编辑模式的状态 .inactive, .active, .transient
编辑模式可以代表一个页面是否处于编辑状态;您可以通过绑定设置编辑模式,也可以配合 EditButton 按钮来自动完成此操作。即点击 EditButton 的时候,就会将 editMode 的状态改变。
scenePhase 当前应用场景的生命阶段(活动、非活动或后台) .active, .inactive, .background
openURL 获取“在Safari应用中打开 URL” 的方法 openURL(URL(string: "<https://www.apple.com>")!)
managedObjectContext Core Data 的托管对象上下文 NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

自定义环境值

SwiftUI 的 @Entry 宏使为环境创建自定义值变得简单,具体做法如下

https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-and-use-custom-environment-values