奎星楼 發表於 2022-11-22 16:37:42

Swift使用enum抹平数组元素差异实例详解

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>业务场景</li><li>用什么控件</li><ul class="second_class_ul"><li>使用UIScrollView的分析</li><li>使用UITableView的分析</li></ul><li>加工数据</li><ul class="second_class_ul"></ul><li>总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>前言</h2>
<p>通过Protocol去封装入参,抹平了入参之间的差异。</p>
<p>今天这篇依然围绕一个我遇到的业务场景,给大家提供一种思路&mdash;&mdash;使用enum抹平数组元素差异。</p>
<div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t1840/116/1507339532/217453/29fbde1b/5667fa5cN164bc29e.jpg" data-name="Swift程序设计实战入门" data-owner="京东自营" data-price="40.2" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&amp;p=JF8BAMcJK1olXwUCVFxaDE4XBV8IG1IWVQMAUW4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYCXV1VDUkSHDZNRwYlAFBCUC0uDRB1exVgXT9rH19gASM7XkcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD0wfA24MHVgVXQMCZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGVQYBVQxbZhZHQCdeEhIVMwABVF9bCnsVAm4KGWsl"></div></div>
<p class="maodian"></p><h2>业务场景</h2>
<p>我先说明一下业务场景:</p>
<p>页面是一个有限可以滑动的的页面(后面我们会分析到其实无限或者有限都无所谓)</p>
<p>页面的每一个子View都是和后台返回的数据绑定,其JSON大致可以理解为下面这种形式:</p>
<div class="jb51code"><pre class="brush:json;">{
    "aPart":{
      "some":""
    },
    "bPart":[
      {
            "label":"",
            "value":""
      },
      {
            "label":"",
            "value":""
      }
    ],
    "cPart":[
      {
            "iconUrl":"",
            "text":"",
            "functionType":""
      },
      {
            "iconUrl":"",
            "text":"",
            "functionType":""
      }
    ],
    "dPart":{
      "name":"",
      "age":""
    },
    "deviceType":"",
    "serviceAble":""
}
</pre></div>
<p>业务需求如下:</p>
<ul><li>aPart对应aView,bPart对应bView,cPart对应cView,dPart对应dView,可以如此穷举下去</li><li>每个part的JSON结构可能都不相同</li><li>后台下发JSON的时候,会根据用户账号的情况,返回不同数据,比如aPart的业务没有,后台会返回<code>&quot;aPart&quot;:null</code>,App的aView需要隐藏,存在可能多个part都返回为null的情况,比如<code>&quot;bPart&quot;</code>和<code>&quot;cPart&quot;</code>都为null的情况,那么App的bView和cView需要隐藏。</li></ul>
<p>那么问题来了,iOS端如何构建一个可以灵活配置的界面?</p>
<p class="maodian"></p><h2>用什么控件</h2>
<p class="maodian"></p><h3>使用UIScrollView的分析</h3>
<p>作为开发App页面的第一点,选取合适的控件是非常重要,因为控件决定了最终数据源的形式。</p>
<p>因为后台数据返回的并不是一个JSON数组,同时又是有限的数据,很多人优先会考虑通过<code>UIScrollView</code>去进行页面的构建,我一开始也是这么想的,但是麻烦的是这一点:</p>
<p>后台下发JSON的时候,会根据用户账号的情况,返回不同数据,比如aPart的业务没有,后台会返回<code>&quot;aPart&quot;:null</code>,App的aView需要隐藏。</p>
<p>也就是说你把<code>aView</code>、<code>bView</code>、<code>cView</code>、<code>dView</code>贴在了scrollView上面之后,需要根据后台的数据隐藏页面,甚至更新布局逻辑,每一个view都有2种情况,假设后台有4个业务数据,那么那就是2的4次方&mdash;&mdash;32种可能。</p>
<p>其实仅隐藏还是显示非常简单,但是如果是使用<code>SnapKit</code>布局,那么更新view的布局,可能就并不是特别好了,同时如果这个页面后续还有新的业务数据,那么就子view会继续增加,维护成本也会越来越高。</p>
<p>所以,到此使用UIScrollView的方案,别否决了,并不是说它不能构建,而是成本有些高,而且不够灵活。</p>
<p>所以剩下的只剩下一种选择了&mdash;&mdash;使用通过数据源绑定UI的<code>UITableView</code>。(备注:其实使用<code>UICollectionView</code>和<code>UITableView</code>都一样,只是多一个瀑布流布局而已)。</p>
<p class="maodian"></p><h3>使用UITableView的分析</h3>
<p>使用<code>UITableView</code>的优势:</p>
<p>完完全全通过数据去驱动页面,页面是否显示完全通过有无数据决定。</p>
<p>但是数据源相较普通模型有着更加严格的数据格式&mdash;&mdash;数组!</p>
<p>而且数组的每个元素最好都是相同的数据类型,因为如果使用<code></code>这样去表达一个数据源,成本太高。</p>
<p>好了,既然我们定下了使用<code>UITableView</code>来进行页面构建,那么剩下的难点也就来了&mdash;&mdash;如何将后台的数据加工成为一个好用的数据源?</p>
<p class="maodian"></p><h2>加工数据</h2>
<p>将后台数据加工成为一个数组并不难,关键是统和数据类型才是难点。</p>
<p>在上一篇文章里面,我通过Protocol去统和了<code>Model</code>与<code></code>,在这次情况下面可不可行呢?</p>
<div class="jb51code"><pre class="brush:cpp;">protocol EraserConvertible {}
struct Response: Codabel {
    let aPart: APart?
    let bPart: BPart?
    let cPart: CPart?
    let dPart: DPart?
}
struct APart: Codabel, EraserConvertible {}
struct BPart: Codabel, EraserConvertible {}
struct CPart: Codabel, EraserConvertible {}
struct DPart: Codabel, EraserConvertible {}
</pre></div>
<p>我们回头看看这个JSON,你不得不认清这样个现实,每个Part都是独立的,完全看不到任何关联,如果想要做类型一致,<code>EraserConvertible</code>这个协议很难满足。</p>
<p>不如做向上类型统和,也就是说数据源变成<code>let dataSource = ()</code>这种形式,在tableView的数据源方法中获取单个数据,然后在进行分析:</p>
<div class="jb51code"><pre class="brush:cpp;">let dataSource = ()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
    let item = dataSource
    if let aPart = item.aPart {
      /// 构建aCell
      let cell = tableView.dequeueReusableCell(withIdentifier: aCell.className)!
      /// 将aPart赋值给aCell
      cell.aPart = aPart
      return cell
    }
    if let bPart = item.bPart {
      /// 构建bCell,
      let cell = tableView.dequeueReusableCell(withIdentifier: bCell.className)!
      /// 将bPart赋值给bCell
      cell.bPart = bPart
      return cell
    }
    .
    .
    .
}
</pre></div>
<p>嗯,这样看起来非常不错,只是每个item都包含了aPart到dPart,方案的是可行。让我们想想有没有更加优雅的思路呢?</p>
<p>数据源的类型不同决定了不同的Cell。我们可不可以用状态来表示?</p>
<p>于是乎我写下了这样的代码:</p>
<div class="jb51code"><pre class="brush:cpp;">enum BusinessPart {
    case a
    case b
    case c
    case d
}
</pre></div>
<p>数据源变成<code>let dataSource = ()</code>这种形式,那么如何区分每个<code>case</code>不同的数据呢?</p>
<p>Swift的enum是可以带参数的,而且单个case带不带参数,带什么类型的参数都很自由。</p>
<p>于是乎,我们接着改造<code>BusinessPart</code>:</p>
<div class="jb51code"><pre class="brush:cpp;">enum BusinessPart {
    case a(APart)
    case b(BPart)
    case c(CPart)
    case d(DPart)
}
</pre></div>
<p>这样我现在就通过<code>enum</code>抹平的数组元素的差异,接下来只要把后台数据架构成为我想要的格式就好了,这里放处理逻辑:</p>
<div class="jb51code"><pre class="brush:cpp;">private func process(model: Response) -&gt; {
    var array: = []
    /// 非空才加入数组,后台返回null,此时aPart为nil,那么aCell也不会出现
    if let aPart = model.aPart {
      array.append(.a(aPart))
    }
    if let bPart = model.bPart {
      array.append(.b(bPart))
    }
    if let cPart = model.cPart {
      array.append(.c(cPart))
    }
    if let dPart = model.dPart {
      array.append(.d(dPart))
    }
    return array
}
</pre></div>
<p>在<code>TableView</code>的数据源方法中这么使用:</p>
<div class="jb51code"><pre class="brush:cpp;">let dataSource = ()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
    let item: BusinessPart = dataSource
    switch item {
      case .a(let aPart):
      /// 构建aCell
      let cell = tableView.dequeueReusableCell(withIdentifier: aCell.className)!
      /// 将aPart赋值给aCell
      cell.aPart = aPart
      return cell
      case .b(let bPart):
      /// 构建bCell
      let cell = tableView.dequeueReusableCell(withIdentifier: bCell.className)!
      /// 将bPart赋值给bCell
      cell.bPart = bPart
      return cell
      case .c(let cPart):
      /// 构建cCell
      let cell = tableView.dequeueReusableCell(withIdentifier: cCell.className)!
      /// 将cPart赋值给cCell
      cell.cPart = cPart
      return cell
      .
      .
      .
    }   
}
</pre></div>
<p>至于这个<code></code>的dataSource怎么在<code>UITableViewDataSource</code>中如何处理,亦或者在<code>BusinessPart</code>的分类中怎么处理,这个就是一个仁者见仁智者见智的问题了。</p>
<p class="maodian"></p><h2>总结</h2>
<p>到最后,这个页面的数据加工,最后还是变成了向上还是向下统和的思考。</p>
<p>其实就是对于一个差异性特别大的数据,如何比较好的整合为一个在iOS中合适的数组问题,通过字段的全覆盖达到模型的统合,抑或通过Swift中枚举带参的特点,抹平差异。</p>
<p>从代码层面上看,两者的代码量差不多,但是使用enum抹平数组元素差异为我们提供一个新的解题思路,至少这是我自己思考的成果。</p>
<p>另外一个角度就是通过布局控件来展开,因为我看Android就是直接用一个ScrollView撸起的:</p>
<p>Android开发中,大部分控件都有visibility这个属性,其属性有3个分别为&ldquo;visible &rdquo;、&ldquo;invisible&rdquo;、&ldquo;gone&rdquo;。主要用来设置控制控件的显示和隐藏。</p>
<p>invisible当控件visibility属性为invisible时,界面保留了view控件所占有的空间;而控件属性为gone时,界面则不保留view控件所占有的空间。</p>
<p>因为Android这个三个属性可以非常便利的完成隐藏还是不隐藏,以及是否保留控件所占有的空间。</p>
<p>而iOS可能需要不仅改变isHidden属性,甚至连view的frame也要进行改变,成本太大了。在使用UITableView的分析已经提到。</p>
<p>但是也不是不可能,比如使用FlexLib应该可以实现。(备注:我自己没用过FlexLib库,这个有待考证哈😁),但是也增加了一定的学习成本与引入了更多的库。</p>
<p>参考文档</p>
<p>Swift:enum你会用吗?</p>
<p>以上就是Swift使用enum抹平数组元素差异实例详解的详细内容,更多关于Swift enum抹平数组元素差异的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Swift中的HTTP请求体Request&nbsp;Bodies使用示例详解</li><li>在 Swift 中测试 UIAlertController的方法</li><li>Framework中实现OC和Swift的混编方案</li><li>Swift设计思想Result&lt;T&gt;与Result&lt;T, E: Error&gt;类型解析</li><li>Swift Error重构的基础示例详解</li><li>Swift enum枚举类型使用详解</li><li>Swift中的HTTP模拟测试示例详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Swift使用enum抹平数组元素差异实例详解