iOS省市二级联动的数据组织PHP版
之所以要发表这篇博客,还源于最近的开发工作所实现的一个小的demo, 当然这个demo不会涉及工作中app的一些内容,下方要实现的demo是通用的。因为项目需求的迭代,要求在银行卡绑定中添加支行所在的省市信息。在ios中选择这种省市信息的一个比较不错的方式当时是使用uipickerview进行显示了。当然在pickerview上的省市信息是联动显示的,我们在此因为需求定的是让用户选择省市信息,所以我们进行二级联动,当然多级联动的原理也是一样的。由于之前的老项目是使用objective-c写的,虽然现在是swift与oc混编,不过要在oc实现vc上添加新的功能还得用oc来实现呢,所以今天的博客的demo咱就不用swift来实现了,不过原理上是一样的。
下方的的截图就是我们今天博客中要介绍的demo的运行效果,我们今天的博客就是生成pickerview所需的数据,以及对下方这个pickerview进行封装。从下方的动画中我们不难看出,在第一列选择省后,第二列会自动的显示该省下的所有地级市。点击完成后,会在上方相应的label中显示出你所有选择的省市以及该省市所对应的编号。具体的请看下方这个粗劣的动画。
一、数据源的生成(从excel到plist)
1.组织数据的前奏
在封装上述pickerview控件之前,我们得有数据不是,也就是我们得有省市的名称,各个省市所对应的编码,以及省与市的对应关系。当然这些数据在网上一抓一大把,权威的数据要看"国家统计局"所提供的数据了。下方这两个截图是一个excel表格中的两个sheet,是我们服务端的一个程序媛给的,算是客户端与服务端的一个标准吧,估计也是从网上下载的。下方的省市信息以及编码当然与国家统计局提供的一致了,这个毋庸置疑。
我拿到这个excel表格怎么用呢?我就想通过oc或者swift来直接解析excel表格来读取数据,然后处理成我想要的格式。不过经过一番了解后,感觉该解决方案颇为复杂,于是乎就另寻他路。又于是乎,想起了之前用过的phpexcel这个框架,因为之前做php开发的时候使用过phpexcel来读取excel文件。这个phpexcel使用起来还是蛮顺手的,用起来也不复杂,于是乎我就决定使用phpexcel来读取下方这两个sheet中的数据。
使用phpexcel读取数据后,重新将数据进行关联组织并生成json提供给ios这边使用。ios这边获取到json后,将其进行解析后存储到plist文件中,这样我们就可以从plist文件中来获取“省市”相关数据了,然后我们就可以封装我们的pickerview了。今天博客就一步一步的来完成这个东西。当然你也可以使用sqlite数据库来存储下方excel中的数据,create两张表,一张放省,一张放市,使用外键进行一对多的关联即可。使用sqlite数据库是另一种解决方案,在此我们使用的是plist文件,因为相对简单吗,因为数据少,plist文件度过了就可以在我们的pickerview上使用了,如果你想使用sqlite也是相当ok的,此篇博客值提供plist文件这种解决方案。
2.使用phpexecl读取省市excel数据
在上面的excel数据中第一个sheet中存储的是每个省以及每个省所对应的编码,而第二个sheet中是存储的每个市和市的编码,并给出了每个市所在的省。接下来我们要使用phpexcel这个第三方框架对上述excel的数据进行读取,关于phpexcel的东西请看其官方文档,地址为:https://phpexcel.codeplex.com/。下方代码就是我们使用phpexcel读取上述excel文件的代码了,并且将上述数据进行处理,将处理后的数据进行json编码。下方我们将介绍相关的php代码。
(1)加载phpexcel框架以及省市excel文件--province.xls
下方的php代码片段就是加载phpexcel框架,以及通过phpexcel_iofactory来创建文件读取器对象$objreader, 并公告$objreader对象来加载我们的province.xls文件。打开后会返回一个操作excel文件的一个文件句柄对象$objphpexcel,我们可以通过$objphpexcel来操作已经打开的excel文件。具体代码如下所示。
(2)通过上述$objphpexcel对象来读取excel文件内容
接下来我们要通过$objphpexcel这个操作文件的对象来获取province.xml中的数据。下方对主要的代码添加的注释,应该还算是清晰。在下方代码片段中$dataarray数组我们用来存储province.xml中所有sheet的数据。我们循环了两次来打开该excel中的两个sheet,通过$objphpexcel对象的setactivesheetindex()方法通过索引来选择相应sheet(从左到右,从0到n),并通过该对象的getactivesheet()方法来获取当前选择的sheet,选择后返回一个$objworksheet对象,我们可以通过$objworksheet对象来读取当前sheet中每行每列的数据。
我们通过foreach来迭代当前sheet中的每行数据,同样适用foreach来迭代一行中每列的数据。我们将每列的数据存入$temprowarray数组中,然后在将每行的数据即$temprowarray存入到sheet数据的$tempsheetarray中。最后将当前的sheet的数据$tempsheetarray存入到$dataarray中。具体实现如下:
3.数据的验证
通过上述步骤,我们就可以将excel中的每个sheet中的数据存入到我们的数组中。其中的数据结果是这样的: $dataarray中存储的是每个sheet($tempsheetarray)的数据,sheet($tempsheetarray)中又有多行($temprowarray),每行($temprowarray)中又有多列。所以dataarray就对应着整个excel表格。我们也就获取了所有的excel数据。经过上述代码,$dataarray中就存储了excel的数据。为了保险起见我们将$dataarray中的数据进行打印,下方是我们的测试代码。
还有在我们写程序时呢,为了减少bug量,以及减少调试bug的难度,我们一定要养成一边写代码一遍调试的好习惯。这样会及时发现bug并修正,写好一个小的功能模块我们就对其进行测试,如果出了问题就很容易定位bug的所在之处。下方代码就是对上述代码的测试:
上述代码对$dataarray中存储的数据进行了一个打印,可以帮助我们查看我们$dataarray中所存储的数据是否符合我们的预期。下方的输出结果就是我们上述的测试用例所输出的结果,上面红框中是第一个sheet中的数据,下方的是第二个sheet中的数据,我们大体上一看符合我们的预期,就说明我们之前的代码没有什么问题,我们就可以对$data中的数据进行关联并生成json数据了。
4.省市数据进行关联
上面我们已经将数据从excel中读取出来了,并且将量sheet中的数据存入了不同的数组,接下来我们将要对数据进行处理。该部分就是将省市的数据进行关联,也就是将两个sheet中的数据合并成一个数据块。下方就是我们要存储数据的一个结构图。整个是一个数组,数组中是一个字典,每个字典就代表一个省。每个省的字典又省编码code、省名name、所有市citys组成。citys中存储的又是一个数组。该数组中的每一项又是一个字典,此处的每个字典代表着一个市,每个市的字典中有包括市名name和市编码code。数据结构如下所示。
参考上图,我们要对读取的数据进行处理,将数据重新组织成上述结构。下方代码段就是对读取的excel表格中的数据进行重组。经过下方代码的处理我们就可以得到上述结构的数据了。下面的$alldataarray就存储的是所有的数据信息,$provincetempdata中暂存着每次省的所有信息,$currentprovincecitys中存储的就是当前省中所有市的信息。第二个循环中的if语句则负责管理省市间的关系了,具体代码以及代码注释如下所示。
经过上面的代码我们所有的数据就会存入到$alldataarray中,上面对$alldataarray进行了json编码并输出,下方就是处理后输出的josn数据。在此我们以河北省为例。下图中的结构是与上面我们数据结构图一一对应的,这正是我们想要的数据。到此我们数据处理的任务就完成一大半了,因为我们得到了我们想要的json。
5. 将上述json数据进行解析并存入plist文件
经过上述步骤,php的工作算是告一段落。接下来我们就是要使用ios客户端来访问上述地址,获取上述生成的json数据。获取到json数据后,我们将json数据进行解析,并存储到沙盒中的plist文件。这样我们就可以从plist文件中来加载我们的省市数据了。
下方代码段是我们的ios客户端的代码,该代码通过nsurlsessiondatatask来请求上述php代码所在的文件获取省市的json数据。请求到json数据后对数据进行解析,将json数据解析成数组后在通过nsfilemanager存储到沙盒中的plist文件中。如果你要在外部使用,只需要找到模拟器中的沙盒路径拷贝出plist文件即可。下方代码就是网络请求+json解析+plist文件存储的的代码。
经过上述代码的执行,你会在你的模拟器中上述app的沙盒中发现一个叫province.plist的文件,该文件中存储的就是我们要使用的省市数据。该plist文件的数据存储结构是我们上面的介绍过的数据结构,下方就是该plist文件中数据的部分截图。至此我们就获得了一个按我们的预期存有省市数据plist文件了。
二、封装选择省市的pickerview的使用方式
封装当然不是简单的将pickerview的简单使用,在封装代码时我们要考虑到用户的易用性和可扩展性。在此我只对pickerview做了一个简单的封装,不过干货还是有的,主要是思想呢。经过上面一大模块的数据组织呢,我们就可以将之前服务端所给的excel文档中的数据组织成我们想要的plist数据。本部分所做的主题就是读取plist文件中的数据,将该数据显示在二级联动的pickerview上供用户选择。用户选择完成后返回用户选择的省市名以及省市所对应的编码。开始我们控件的封装。
1.所封装控件的目录结构
首先我们先整体的看一下我们所封装控件的目录结构是怎样的,先整体的了解一下我们这个封装的控件。下方截图中就是我们所封装控件的目录结构了,因为我们是对显示省市信息的uipikcerview进行的封装,所以在此我们称其为provincepickerview,provincepickerview就是我们所封装的组件了。用户只需要对其进行实例化并添加到其视图上就可以进行使用了。下方的province.plist数据就是我们上面所生成的存有省市信息是数据,我们provincepickerview中的数据源就是province.plist文件。
provincemodel就存储着当前选中的省市的名称以及编码,下面第二张截图就是provincemodel中的内容了。provincecode存储的是当前选中的省的编码,provincename存储的就是当前所选省的名称,citycode存储的是所选市的编码,cityname存储的是所选市的名称。具体代码如下所示。
2. 所封装控件的初始化以及调用方式
接下来我们看一下我们封装的这个provincepickerview的使用方式,使用起来还算简单。下方代码段就是provincepickerview初始化方式,将provincepickerview进行初始化然后添加到所要显示provincepickerview的视图上,然后设置provincepickerview对象的block回调。该回调会在用户点击provincepickerview上的完成按钮时执行,并返回当前用户选中的省市信息的model数据。
上面是初始化provincepickerview,并且设置数据回调block。下方的代码段就是用来显示provincepickerview的,调用showpickerview方法就会在下方弹出provincepickerview,因为provincepickerview本身就有取消按钮,取消后就会自动收回provincepickerview,所以我们不需要为用户提供收回provincepickerview的方法,所以使用起来还是比较简单的。
三、代码分享
由于博客篇幅有限,至于provincepickerview中封装的代码在此就不一一的往上粘贴了。说白了最核心就是对uipickerviewdelegate和uipickerviewdatasource两个代理中的相应的方法的封装。还有就是如何显示和隐藏pickerview,换一句话说,就是讲pickerview放在什么地方进行显示。有感兴趣的小伙伴可以从下方的github中分享的代码来自行分析呢。事无巨细,所以在此就不做过多的赘述了。
下方的代码截图就是在github上分享的部分代码,可以说是添加了详细的注释,有感兴趣的小伙伴可以进行自行阅读。代码中如有偏颇之处,还望指出。
上述所有的代码都是使用截图的方式呈现的,这无关紧要,在博客的结尾会给出所有相关的代码,当然也包括上述的php代码,以及pickerview的具体实现呢。好了,由于博客篇幅有限,今天就先到这儿吧。下方就是本篇博客相关代码的分享链接。
github分享地址:https://github.com/lizelu/provincepickerdemo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。