SwiftUI基础篇Property Wrappers - Items(下)

Property Wrappers - tems

概述

文章主要分享SwiftUI Modifier的学习过程,将使用案例的方式进行说明。内容浅显易懂,Property Wrappers items介绍具体属性包装器,偏向理论,可以移步Github下载code -> github案例链接

1、@FocusState属性包装器

SwiftUI提供了一个特定的属性包装器来跟踪当前接受用户输入的视图,称为@FocusState。这可以绑定到一个Bool值以控制单个字段,或绑定到一个枚举以在多个字段之间进行控制。

1.1、单焦点

Swift 复制代码
//控制单个输入字段是否具有键盘焦点
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()在他们之间进行移动。

Swift 复制代码
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快得多。

Swift 复制代码
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托管对象上下文注入到环境中,

Swift 复制代码
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。

Swift 复制代码
//监听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中经常看到的复杂多场景设置中非常好用。

Swift 复制代码
//如果有一个文本编辑器,并希望存储用户正在输入的内容
struct FFPropertyWrapperSceneStorage: View {
    @SceneStorage("text") var text = ""
    var body: some View {
        NavigationStack {
            TextEditor(text: $text)
        }
    }
}

因为使用了@SceneStorage,SwiftUI将自动确保每个场景实例都有其自己的文本,如果同时运行多个应用程序,都可以正确保存和恢复其数据。

在使用@SceneStorage之前,有一些来自Apple的重要警告:

  • 不要保存大量数据:只保存状态恢复的所需内容
  • 永远不要将敏感数据存储在场景存储中,因为是不安全的。
  • 如果用户转到应用程序切换器并销毁应用程序,场景存储也将被销毁(iOS17上发现未销毁)。

调试结果

6、@ScaledMetric属性包装器

SwiftUI提供了@ScaleMetric属性包装器,用于定义根据用户的动态类型设置自动缩放的数字。

Swift 复制代码
//在其基本用法中,为属性提供一个默认值,@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 方法,可以使用以下代码

Swift 复制代码
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 知道创建和管理您的应用委托类:

Swift 复制代码
@main
struct FFModifierApp: App {
    
    //在你的应用场景中,使用UIApplicationDelegateAdaptor属性包装器来告诉SwiftUI它应该使用你的AppDelegate类作为delegate
    @UIApplicationDelegateAdaptor(Appdelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            FFPropertyWrapperSceneStorage()
        }
    }

FFPropertyWrapperSceneStorage这是我的某个视图,不受它影响,可以是任何视图。

terminal日志

terminal 复制代码
applicationDidFinishLaunching
相关推荐
IT猿手2 天前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解GLSMOP1-GLSMOP9及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·算法·机器学习·matlab·强化学习
凳子花❀3 天前
强化学习与深度学习以及相关芯片之间的区别
人工智能·深度学习·神经网络·ai·强化学习
我爱C编程4 天前
基于Qlearning强化学习的机器人路线规划matlab仿真
matlab·机器人·强化学习·路线规划·qlearning·机器人路线规划
东坡肘子4 天前
肘子的 Swift 周报 #063|异种肾脏移植取得突破
swiftui·swift·apple
恋猫de小郭5 天前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
IT猿手5 天前
基于PWLCM混沌映射的麋鹿群优化算法(Elk herd optimizer,EHO)的多无人机协同路径规划,MATLAB代码
算法·elk·机器学习·matlab·无人机·聚类·强化学习
靴子学长6 天前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui
IT古董11 天前
【机器学习】机器学习的基本分类-强化学习(Reinforcement Learning, RL)
人工智能·机器学习·分类·强化学习
hxx22112 天前
iOS swift开发系列--如何给swiftui内容视图添加背景图片显示
ios·swiftui·swift
胖虎112 天前
SwiftUI - (十九)组合视图
ios·swiftui·swift·组合视图