之所以要发表这篇博客,还源于最近的开发工作所实现的一个小的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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持Devmax。

iOS省市二级联动的数据组织PHP版的更多相关文章

  1. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  2. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  4. ios – Testflight无法安装应用程序

    我有几个测试人员注册了testflight并连接了他们的设备……他们有不同的ios型号……但是所有这些都有同样的问题.当他们从“safari”或“testflight”应用程序本身单击应用程序的安装按钮时……达到约90%并出现错误消息…

  5. ibm-mobilefirst – 在iOS 7.1上获取“无法安装应用程序,因为证书无效”错误

    当我的客户端将他们的设备更新到iOS7.1,然后尝试从AppCenter更新我们的应用程序时,我收到了上述错误.经过一番搜索,我找到了一个类似问题的帖子here.但是后来因为我在客户端使用AppCenter更新应用程序的环境中,我无法使用USB插件并为他们安装应用程序.在发布支持之前,是否有通过AppCenter进行下载的解决方法?

  6. ios – 视图的简单拖放?

    我正在学习iOS,但我找不到如何向UIView添加拖放行为.我试过了:它说“UIView没有可见的接口声明选择器addTarget”此外,我尝试添加平移手势识别器,但不确定这是否是我需要的它被称为,但不知道如何获得事件的坐标.在iOS中注册移动事件回调/拖放操作的标准简单方法是什么?

  7. ios – 什么控制iTunes中iPhone应用程序支持的语言列表?

    什么控制iPhone应用程序的iTunes页面中支持的语言?

  8. ios – 获得APNs响应BadDeviceToken或Unregistered的可能原因是什么?

    我知道设备令牌在某些时候是有效的.用户如何使其设备令牌变坏?从关于“未注册”的文档:Thedevicetokenisinactiveforthespecifiedtopic.这是否意味着应用程序已被删除?.您应该看到四种分发方法:如果您选择AppStore或Enterprise,您将在后面的对话框中看到Xcode将APNS权利更改为生产:如果选择AdHoc或Development,则aps-environment下的文本将是开发,然后应与后端的配置匹配.

  9. ios – 当我关闭应用程序时,我从调试器获得消息:由于信号15而终止

    我怎么能解决这个问题,我不知道这个链接MypreviousproblemaboutCoredata对我的问题有影响吗?当我cmd应用程序的Q时,将出现此消息.Messagefromdebugger:Terminatedduetosignal15如果谁知道我以前的问题的解决方案,请告诉我.解决方法>来自调试器的消息:每当用户通过CMD-Q(退出)或STOP手动终止应用程序(无论是在iOS模拟器中还是

  10. ios – NSUbiquityIdentityDidChangeNotification和SIGKILL

    当应用程序被发送到后台时,我们会删除观察者吗?我遇到的问题是,当UbiquityToken发生变化时,应用程序终止,因为用户已经更改了iCloud设置.你们如何设法订阅这个通知,如果你不这样做,你会做什么来跟踪当前登录的iCloud用户?

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部