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的长处之一是它会在手势完毕时主动将特点的值设置回初始值。在这种情况下,能够随意的拖动视图,一旦松开就会回归原位。

调试成果

iShot2023-09-06 18.26.26.gif

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上发现未毁掉)。

调试成果

iShot2023-09-06 18.38.37.gif

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

截屏2023-09-06 18.52.08.png