zoukankan      html  css  js  c++  java
  • SwiftUI 结构体自动生成可编辑界面

    2个关键点:一个是读取、一个是写入。
    显示界面:读取结构体的字段名,字段类型,即可判断、显示相应的UI控件;
    用户写入数据:需要知道每个UI控件和哪个字段进行绑定,另外,对字段需要有写的权限。

    尝试Mirror【失败】

    第一个尝试的方案是运用反射技术,在Swift中,使用的是Mirror。Mirror可以读取结构体的属性,但不能修改结构体。

    尝试Dictionary【失败】

    第二个尝试的方案是使用字典Dictionary。即可读取,也能写入。
    具体步骤是:

    1. 初始化结构体
    2. 通过JSONSerialization序列化成字典
    3. 遍历字典显示界面。。。[卡住]

    单单是在读取这里,就遇到一个棘手的问题。Swift是强类型的语言,所以读取Dictionary的嵌套字段,需要一长串的类型转换。这就很难写出简洁易懂的代码。
    比如:

        var dic:[String: Any] = ["a": [1,2]]
        var value = (dic["a"] as! [Int])[0] 
    

    难点是,我们如何去解析包含自定义类型的数组呢?
    要知道 [Location] 、[User]和 [String] 可是不同类型.
    原始类型毕竟数量有限,每个类型判断一次没问题。
    但对于自定义的类型。以我目前的能力,不知道如何写出兼容不同字典的解析代码?很是头大。

    if let dic["a"] as [Location] {
        //...
    } else if dic["a"] as [User] {
        ....
    }
    // ...无穷无尽的自定义类型
    

    新的思考

    能否像弱类型语言那样(如JavaScript),我可以这样去访问一个字段 dic["a"][0]
    而不用在乎 dic["a"]是不是数组,也不关心它是个什么类型的数组,假如它是个数组,那就返回相应索引的值;假如不是个数组,就返回nil。
    这样的话,有2个好处:

    1. 对于读取,因为我们可以不必在乎是数组类型,管你是[Location]还是[User],我知道你是个数组就行。这样可以写出通用的遍历代码。
    2. 对于写入,我们可以很容易的通过拼接的方式来修改嵌套字段。

    站在巨人的肩膀

    在网上查阅资料,发现SwiftyJSON框架。
    官方使用示例:

    let json = JSON(data: dataFromNetworking)
    if let userName = json[0]["user"]["name"].string {
      //Now you got your value
    }
    

    看起来符合我的预期。

    它的原理是把字典封装到它自定义的JSON结构体里,
    通过JSON.object属性进行get、set操作。
    这个object实际上通过类型判断,返回、修改不同的成员变量。

    以下是SwiftyJSON的部分源码:

        fileprivate var rawArray: [Any] = []
        fileprivate var rawDictionary: [String: Any] = [:]
        fileprivate var rawString: String = ""
        fileprivate var rawNumber: NSNumber = 0
        fileprivate var rawNull: NSNull = NSNull()
        fileprivate var rawBool: Bool = false
           /// Object in JSON
        public var object: Any {
            get {
                switch type {
                case .array:      return rawArray
                case .dictionary: return rawDictionary
                case .string:     return rawString
                case .number:     return rawNumber
                case .bool:       return rawBool
                default:          return rawNull
                }
            }
            set {
                //...    
            }
        }
    

    最终可行方案

    初始化结构体,并转为JSON,
    使用KeyPath,作为字段的唯一索引。格式如:"user.name"、"user.groups.0"。

    并且,我为KeyPath方案写了个工具函数,1.可获取JSON所有字段的KeyPath,2. 可通过JSON[KeyPath]的方式修改字段值。

    这样,就完全攻克读、写的技术难点了

    1. 遍历JSON中的字段值,判断类型显示相应UI;【其实就是遍历KeyPath】
    2. 能通过UI控件修改原JSON相应字段;[其实就是把JSON和KeyPath传给UI]

    源码

    https://gitee.com/tamiapp/swift-ui-struct-to-view

  • 相关阅读:
    Jenkins
    ssh 免登录
    linux 远程执行命令
    Java WEB 笔记
    如何用新安装的jdk替换掉Linux系统默认jdk
    修改 File --> New 菜单内容
    java.security.NoSuchAlgorithmException: AES KeyGenerator not available
    JDK历史版本下载地址
    maven 核心概念
    spring boot: ConfigurationProperties
  • 原文地址:https://www.cnblogs.com/ZJT7098/p/swiftui-jie-gou-ti-zi-dong-sheng-cheng-ke-bian-ji.html
Copyright © 2011-2022 走看看