Property Wrappers – tems
- @FocusState特点包装器
- @GestureState特点包装器
- @FetchRequest特点包装器
- @AppStorage特点包装器
- @SceneStorage特点包装器
- @ScaledMetric特点包装器
- @UIApplicationDelegateAdaptor特点包装器
概述
文章首要共享SwiftUI Modifier的学习进程,将运用事例的方式进行说明。内容浅显易懂,Property Wrappers items介绍具体特点包装器,倾向理论,能够移步Github下载code -> github事例链接
1、@FocusState特点包装器
SwiftUI供给了一个特定的特点包装器来盯梢当时接受用户输入的视图,称为@FocusState
。这能够绑定到一个Bool值以操控单个字段,或绑定到一个枚举以在多个字段之间进行操控。
1.1、单焦点
//操控单个输入字段是否具有键盘焦点
struct FFPropertyWrapperFocusState: View {
@FocusState private var isUsernameFocused: Bool
@State private var username = "meta BBLv"
var body: some View {
VStack {
TextField("输入你的用户名", text: $username)
.focused($isUsernameFocused)
Button("切换焦点") {
isUsernameFocused.toggle()
}
}
}
}
1.2、多焦点
假如想在多个视图之间移动键盘焦点,应该运用可选的枚举。能够将其设置为枚举中的一个事例来激活特定的输入字段,或者将其设置为nil以使没有任何字段具有焦点。在iOS上实际上是取消键盘的显示。
因而能够创立两个文本字段来存储用户名和密码,然后运用@FocusState和onSubmit()在他们之间进行移动。
struct FFPropertyWrapperFocusStateEnum: View {
enum FocusedFieldEnum {
case username, passward
}
@FocusState private var focusedField: FocusedFieldEnum?
@State private var username = "meta BBLv"
@State private var password = "123456"
var body: some View {
VStack {
TextField("请输入用户名", text: $username)
.focused($focusedField, equals: .username)
SecureField("请输入密码", text: $password)
.focused($focusedField, equals: .passward)
}
.onSubmit {
if focusedField == .username {
focusedField = .passward
} else {
focusedField = nil
}
}
}
}
2、@GestureState特点包装器
SwiftUI为咱们供给了一个专门用于盯梢手势状况的特点包装器,称为@GestureState
。虽然能够运用简单的@State特点包装器完成相同的效果。但@GestureState具有额外的功用,他在手势完毕时将主动将特点设置回其初始值,而且通常比运用@State快得多。
struct FFPropertyWrapperGestureState: View {
//创立一个手势,能够拖动视图。为此,先创立一个@GestureState特点,以存储视图一移动多少
@GestureState var dragAmount = CGSize.zero
//这具有CGSize.zero的默许值,代表当手势完毕时,将主动设置会.zero
var body: some View {
Image(.fullEnglish)
.offset(dragAmount)
.gesture(
DragGesture().updating($dragAmount, body: { value, state, transcation in
state = value.translation
})
)
}
}
代码分解:
- DragGesture().updating()创立了一个新的拖动手势,要求它修正存储在dragAmount中的值,这是咱们的CGSize。
- 采用了一个带有三个参数的闭包:value、state和transaction
- value参数时拖动的当时数据,在哪里开端,移动了多远,猜测在哪里完毕等等。
- state参数是一个inout值,是咱们的特点。因而,在此闭包内,咱们应该修正state,而不是直接读取或写入dragAmount
- transaction参数是一个inout值,存储整个动画上下文,为此咱们供给一些关于正在产生的情况的信息,比方这是否是一个接连或瞬间动画。接连动画可能是通过拖动滑块产生的,而瞬时动画可能是通过点击产生的。
为了使视图能够拖动,我所做的便是将当时的拖动翻译直接分配给state(在这种情况下,实际上是dragAmount),然后在offset()修正器中运用它来移动视图。
@GestureState的长处之一是它会在手势完毕时主动将特点的值设置回初始值。在这种情况下,能够随意的拖动视图,一旦松开就会回归原位。
调试成果
3、@FetchRequest特点包装器
SwiftUI为咱们供给了一个专门用于处理CoreData获取恳求的特点包装器,能够将数据直接嵌入到SwiftUI视图中,而无需编写额外的逻辑。
运用@FetchRequest
至少供给一个值,即用于摆放数据的排序描述符数组,还能够依据需求选择性供给参数来过滤数据。
在运用@FetchRequest之前,必须将CoreData托管目标上下文注入到环境中,
struct FFPropertyWrapperFetchRequest: View {
@Environment(\.managedObjectContext) var managedObjectContext
// SortDescriptors参数是一个数组,所以能够供给尽可能多的排序选项
@FetchRequest(
sortDescriptors: []
) var languages: FetchedResults<ProgrammingLanguage>
var body: some View {
Text("Hello, World!")
}
}
这里就不做演示了,参阅前面 – SwiftUI基础篇CoreData
4、@AppStorage特点包装器
SwiftUI为从UserDefaults
读取值供给了一个专门的特点包装器,当值产生更改时,它主动从头调整视图的body特点。这个特点包装器实际上会监听UserDefaults中的一个键,并在该键产生更改时刷新UI。
//监听UserDefaults的“username”key,在按下时set
struct FFPropertyWrapperAppStorage: View {
@AppStorage("username") var username: String = "meta BBLv"
//默许情况下,@AppStorage会监听UserDefaults.standard,也能够监听特定的应用程序组
@AppStorage("username", store: UserDefaults(suiteName: "group.com.metaBBLv.unwrap")) var hobby: String = "metaBBLv"
//@AppStore将数据写入UserDefaults,这不是安全的存储,因而,不能够运用@AppStore存储
//个人数据等灵敏信息,非常简单提取。
var body: some View {
VStack {
Text("欢迎:\(username)同学")
Button("登陆") {
username = "@metaBBLv"
}
Button("旧的存储方式") {
//上述代码更改用户名将立即写入UserDefaults,并一同更新视图,假如运用旧的方式
UserDefaults.standard.setValue("@metaBBLv", forKey: "username")
}
}
}
}
5、@SceneStorage特点包装器
假如想为每个屏幕保存共同的数据,应该运用SwiftUI的@SceneStorage
特点包装器。这与@AppStorage有些相似,需要为它供给一个名称来保存数据以及一个默许值,但与运用UserDefaults不同,它用于状况恢复,而且它甚至在iPadOS中经常看到的复杂多场景设置中非常好用。
//假如有一个文本编辑器,并希望存储用户正在输入的内容
struct FFPropertyWrapperSceneStorage: View {
@SceneStorage("text") var text = ""
var body: some View {
NavigationStack {
TextEditor(text: $text)
}
}
}
由于运用了@SceneStorage
,SwiftUI将主动确保每个场景实例都有其自己的文本,假如一同运行多个应用程序,都能够正确保存和恢复其数据。
在运用@SceneStorage之前,有一些来自Apple的重要正告:
- 不要保存很多数据:只保存状况恢复的所需内容
- 永远不要将灵敏数据存储在场景存储中,由于是不安全的。
- 假如用户转到应用程序切换器并毁掉应用程序,场景存储也将被毁掉(iOS17上发现未毁掉)。
调试成果
6、@ScaledMetric特点包装器
SwiftUI供给了@ScaleMetric
特点包装器,用于定义依据用户的动态类型设置主动缩放的数字。
//在其根本用法中,为特点供给一个默许值,@ScaledMetric将处理其余部分。例如,依据用户的设置,
//以下代码将以不同的size制作相同的图画
struct FFPropertyWrapperScaledMetric: View {
@ScaledMetric var imageSize = 100.0
//假如需要使缩放与特定的其他文本匹配,能够为特点包装器运用relativeTo参数,
//该参数能够指定要匹配的字体巨细。例如,与大标题巨细一同缩放
@ScaledMetric(relativeTo: .largeTitle) var titleSize = 100.0
var body: some View {
Image(systemName: "cloud.sun.bolt.fill")
.resizable()
.frame(width: imageSize, height: imageSize)
}
}
7、@UIApplicationDelegateAdaptor特点包装器
假如需要在SwiftuI中访问AppDelegate
的功用,要创立一个class,而且要承继NSObject和UIApplicationDelegate,并增加你想要的功用。
7.1、AppDelegate
假如想要完成旧的 didFinishLaunchingWithOptions 方法,能够运用以下代码
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("applicationDidFinishLaunching")
return true
}
}
7.2、FFModifierApp
一旦创立了这个类,您能够在主 App 中运用 ApplicationDelegateAdaptor
特点包装器,以便 SwiftUI 知道创立和管理您的应用托付类:
@main
struct FFModifierApp: App {
//在你的应用场景中,运用UIApplicationDelegateAdaptor特点包装器来告诉SwiftUI它应该运用你的AppDelegate类作为delegate
@UIApplicationDelegateAdaptor(Appdelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
FFPropertyWrapperSceneStorage()
}
}
FFPropertyWrapperSceneStorage这是我的某个视图,不受它影响,能够是任何视图。
terminal日志
applicationDidFinishLaunching