这个系列的目录:

用Swift实现一款天气预报APP(一)

用Swift实现一款天气预报APP(二)

用Swift实现一款天气预报APP(三)

通过前面的学习,一个天气预报的APP已经基本可用了。至少可以查看现在当前的天气情况和未来几个小时的天气预报了。但是,还不够完善。如果用户想要知道他要去的地方的天气怎么办。明显我们的APP在目前来说无法满足用户的这个需求。而我们的APP需要获取其他城市的天气却非常的简单。通过查看天气的API,发现只要把城市的名称作为参数就可以获得当地城市的天气预报。API:

  1. api.openweathermap.org/data/2.5/find?q=London&type=like&mode=xml

q=London就是在API中指明地点的参数。但是,从这里也可以单出。这个城市的名称显示是需要英文的,不是“北京”这样的汉字,也就是说在城市列表中显示的是汉字,但是传给API的url使用的时这个城市的英文名称,或者可以说是拼音字母。

在这一篇中,我们主要要实现的功能是切换城市,和刷新天气预报数据。首先在Storyboard中添加一个叫做城市列表的Controller。我们需要在Controller中显示一个城市列表,这里需要用到一个在iOS的开发中很常用的控件:UITableView。添加一个UIViewController之后,拖动一个UITableView到这个ViewController上。这样,在界面上来说就齐全了。

下面,创建代码,选择Source,先在Subclass of选择你的超类为UIViewController,然后命名你的类的名称。这里是"CityListViewController"。最后在语言一项选择Swift。你应该不会选择Objective-C的。如图:

之后一路的Next一直到Done。

很多的教程在讲到UITableView的时候总是喜欢用UITableViewController,这个是对用包含一个UITableView的UIViewController的封装。有的时候,系统封装了过多的东西对于开发者来说并不是什么好事。尤其是开发者单独处理UITableView也不会耗费太多的时间。所以,这里使用的是UIViewController和UITableView的组合。

在你的ViewController文件中添加准备绑定的UITableView对象的属性:

  1. @IBOutlet weak var tableView: UITableView!

之后在Controller里选择之后,在右边栏里选择左数第三个选项,然后在下面的Class里选择你刚刚创建的CityListViewController。一般,在你选择完了

Controller之后,Class下面的Module会自动设定为Current-Swift_Weather。也就是会自动选择你的项目名称。如果没有选择的话,你需要手动添加你的项目名称到Module里。否则,这个ViewController是不可用的。Swift中引入了Module(模块)这个概念。默认的你的APP就是一个Module。类都是从你的应用的Module里查找的。如果没有这个Module名称的话,应用无法找到你给这个ViewController关联的代码。

这些操作完成之后,你已经把Storyboard的ViewController和对应的代码关联在了一起。下面还需要关联上前文提到的UITableView控件。点击你刚刚选中的controller然后在右边栏中选择最右边的箭头按钮你会看到里面会出现一个tableView,他后面的小圆圈还没有和Storyboard的TableView关联起来。鼠标放在小圆圈上,按下Ctrl同时移动鼠标到Storyboard的UITableView上。

到目前为止都很完美,但是这个TableView还不能用。TableView需要知道有多少个TableViewCell要显示出来,每一个Cell上面显示什么内容。每一个Cell有多高,有多少个Section。哪个Cell被选中,哪个Cell从被选中变成了么有被选中等等。。。这些都是通过TableView的代理实现的。这样的代理一共有两个,一个叫做UITableViewDelegate,一个叫做UITableViewDataSource

所以,还需要把UITableView的两个代理和UITableView所在的Controller关联。选中Storyboard的UITableView,然后选择右边栏的最后一个选项。就是最后的那个选项。你会看到

把鼠标放在小圆圈上,同时按下Ctrl键,移动鼠标到这个UITableView所在的Controller上,准确的说是移动到这里:。两个都是这样的操作。完成之后就给这个UITableView关联好了UITableViewDelegateUITableViewDataSource

到目前为止,我们在Storyboard中创建了一个Controller(Scene),在上面放了一个UITableView。创建了一个UIViewController代码,并且和刚刚创建的Controller关联到了一起。并且把UITableView也关联到了一起,同时关联的还有这个UITableView的UITableViewDelegateUITableViewDataSourceUITableViewDelegateUITableViewDataSource在Swift的语法上来说都是protocol,也就是其他的语言,如Java、C#的接口,哪里继承了哪里就要给出实现。既然UITableView的这delegate和datasource都制定在了他所在的Controller上,那么我们代码里的CityListViewController就需要继承UITableViewDelegateUITableViewDataSource并实现这两个protocol。

  1. class CityListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource

下面是UITableViewDelegateUITableViewDataSource中部分必要的方法的实现。注意,这些只是一个UITableView正常显示的必要方法,还有很多的方法暂时没有用到。

第一行,是指定这个UITableView中有多少个section的,section分区,一个section里会包含多个Cell。这里,是只有一个section。

第二行,是指定每一个section里面有多少个Cell的。因为我们只有一个section,所以,有多少个城市可选就有多少个Cell。这个是视不同情况定的。

第三行,初始化每一个Cell。一个Cell长什么样子就由这个方法决定。

第四行,是选中一个Cell后执行的方法。当用户选择了一个Cell的时候,我们需要知道是哪一个,并把这个Cell的城市的英文名称(或者是拼音的字母)发送到主界面中用于获取该城市的天气数据。

这些,就是使用一个UITableView时的全部了。首先创建一个放UITableView的Controller(Scene)然后拖动一个UITableView在上面。二,创建一个对应于这个Scene的Controller的Swift代码,并在代码中添加UITableView属性。关联Scene和Swift的Controller,关联代码的UITableView属性和Storyboard中的UITableView。三,关联UITableView的delegate和datasource到Swift代码的Controller,并在其中继承和实现UITableViewDelegateUITableViewDataSource。这一部分需要多练习并且熟记。因为你会发现没有一个应用不用到UITableView的。如果你找不出UITableView那也可能是开发者对于UITableView的定制比较深,直观上看不出来而已。

这里必须强调的一点,就是第三行的创建Cell的方法。UITableView的Cell不是每次用到都去创建的。手机如今的内存已经有2G的了,CPU也是几核心的。但是,其资源还是相对比较紧缺。如果,每个Cell都新建一个。那么,用户在上下滑动UITableView的时候会非常的卡顿。这对于一个好的APP时绝对不允许的。所以苹果也推荐了一种使用Cell的方法,如果Cell还没有被创建的话就创建一个。如果Cell已经创建了,那么就对这个Cell重新赋值。这里一点很关键,如果一个Cell已经创建了,只重新赋值而不再创建!参见下面的代码:

  1. func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  2. var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier") as? UITableViewCell
  3. if cell == nil {
  4. cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CellIdentifier")
  5. }
  6.  
  7. cell?.textLabel.text = self.cityList.values.array[indexPath.row]
  8.  
  9. return cell!
  10. }

首先按照Cell的Identifier从UITableView的Cell重用队列获取Cell。如果为空则创建一个Cell,并指定这个Cell的Identifier。如果Cell不为空的话给这个Cell的textLabel的text属性重新赋值。但是,既然我们用了Storyboard了,就用的彻底一点。Cell为什么不用Storyboard来创建呢。这样又会省去很多的代码,比如我们这里的重用Cell的部分。在右边栏中找到UITableViewCell,并拖动到UITableView上。给这个Cell的Identifier起个名字就叫"cityCell"。Style选择Custom,表示我们要自定义这个Cell。如图:

接下来,添加为这个Cell添加新的Swift代码文件。首先,source->Cocoa Touch Class。之后选择

给Cell绑定Swift代码类。选中Storyboard的这里

之后->

这个Cell需要一个UILabel来显示城市的中文名称(这个UILabel只是为了表明在Storyboard中自定义一个Cell的时候该如何处理,一般来说Cell中有一个textLabel可以显示文本)。那么先在代码中添加一个label的属性:

  1. @IBOutlet weak var cityNameLabel: UILabel!

添加完属性之后,关联这cityNameLabel和Storyboard中的Cell中刚刚添加的Label。重复上面说到的第一步,选中这个cell。然后选择第二步中最上面选项中的最后一项。你就会看到cityNameLabel和他后面是小圆圈。你应该知道怎么办了。关联属性和Storyboard的Label之后,回到前面说道的创建Cell的方法。

  1. func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  2. var cityListCell = tableView.dequeueReusableCellWithIdentifier("cityCell", forIndexPath: indexPath) as CityListTableViewCell
  3. cityListCell.cityNameLabel.text = self.cityList.values.array[indexPath.row]
  4. return cityListCell
  5. }

看到有什么不同的么。是的,这里不用再从TableView的Cell的复用队列中获取Cell了。因为,这些都在Storyboard中处理好了。我们只要每次给Cell重新赋值就可以了。

到目前为止,这个城市列表还是不能用的。因为,我们还没有把这个列表和天气预报的主界面关联起来。是的,用户从哪里进入这个城市列表呢。现在我们就把主界面和城市列表关联起来。首先,在Storyboard上拖入一个UINavigationController。删掉后面的RootViewController,并把这个UINavigationControllerRootViewController和我们刚刚创建的城市列表Controller关联起来。这一类似操作在第一部分中讲过。不清楚的话可以重新看看第一部分。

之后,找到主界面的City按钮,连接到新添加的UINavigationController上,在弹出的Action Segue中选择modal。这个时候运行APP,点击City就会出现刚刚创建的CityListController了,用户可以点这上面的某一行,但是。。。回不去了。现在就来处理这个问题。开发这个工作就是“逢山开路,遇水搭桥”。在MainViewController中添加如下代码

@IBAction func dismissCityListController(segue: UIStoryboardSegue){

println("dismiss controller")

}

名字可以任意起,但是参数必须是UIStoryboardSegue类型的。然后,在Storyboard的天气预报主界面中右击Exit,在出现的菜单中你会看到刚刚添加的方法dismissCityListController。在这个方法有个小圆圈。Storyboard里就是充满了这样的小圆圈。把这个小圆圈连接到CityListController的Cell上,在弹出的小菜单中选择selection。也就是说在用户选择了UITableView的一个Cell的时候(selection)CityListController就会“Exit”退出。

这个方法就是传说中的“unwind segue”给这个segue设定一个identifier为“backToMain”。连接好以后再运行APP。在用手指点了一个City以后CityListController就会隐藏起来。用户可以在选择了城市以后返回继续操作。

但是,选择了城市以后主界面显示的还是原来的城市,并没有更换。那时因为,我们并没有添加相关的代码。上面添加的只是让界面可以在segue的引导之下跳转,但是没有更换城市后重新请求天气预报数据。下面就完成这一部分功能。

UIViewController之间传递数据,我们这里是从CityListController传递选择好的城市给MainViewController。方法有很多,比如,以后你会经常用到的Notification的方法。用户选择一个城市之后发出一个Notification,在MainviewController中捕获这个Notification并做相应的处理。看似很简单把。不过我们这里不用这个方法。用Notification的方法会给代码的维护造成一定的困扰。哪里发送,哪里接受都是分开写的,不容易维护代码。我们这里要将的时用代理的方式传递数据。这个中方法在自定义控件,和ViewController之间都会经常用到。具体到我们的天气APP这里,我们需要从CityListController传递数据给MainViewController,那么就在CityListController文件中定义一个接口(protocol)。苹果一般的命名规则是你的Controller的名字后面加Delegate。这样非常好辨认哪里是定义Delegate的,哪里是用到这个Delegate的。

  1. @objc protocol CityListViewControllerDelegate{
  2. func cityDidSelected(cityKey: String)
  3. }

这个@objc不是一定要加的,一般是和其他的Objective-C代码共用的时候加。在CityListViewController中添加一个叫做delegate的属性。这个属性会在CityListViewController的UITableView里的某个Cell选中时被执行。

  1. @IBOutlet weak var delegate: CityListViewControllerDelegate?

这里用weak时因为,我们不希望这个代理强引用(strong)其他的Controller。这样会造成循环引用,使得连个Controller的引用计数不减少,从而无法在不用的时候从内存清除。在选中的时候执行

  1. func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  2. println("did select row \(indexPath.row)")
  3.  
  4. // set current selected index of city list
  5. self.selectedIndex = indexPath.row
  6. if self.selectedIndex != nil && self.delegate != nil {
  7. var cityKey = self.cityList.keys.array[self.selectedIndex!]
  8. self.delegate?.cityDidSelected(cityKey)
  9. }
  10. tableView.deselectRowAtIndexPath(indexPath, animated: true)
  11. }

在CityListViewController中执行代理的方法的话,就需要在MainViewController中实现这个protocol。

  1. class MainViewController: UIViewController, CLLocationManagerDelegate, CityListViewControllerDelegate{
  1. //MARK: city changed delegate
  2. func cityDidSelected(cityKey: String){
  3. println("selected city \(cityKey)")
  4.  
  5. }
    }

上面的代码是一个大概是实现。具体的代码可以在示例工程中查看。

运行APP,并选择一个其他的城市的时候,这段代码就会执行。在Console中会出现选择的城市的英文名称。现在我们需要来真的了。查看之前的代码,有一个方法func updateWeatherInfo(latitude: CLLocationDegrees, longitude: CLLocationDegrees)会在获取用户的地理位置后请求服务器获得天气预报。我们现在是需要根据城市的名称获取天气预报了。那么,我们就来Over Load方法updateWeatherInfoOver Load就是定义一个和某个别的方法同名但是参数不同的方法。

  1. func updateWeatherInfo(cityName: String)

以后的事情就是在前文中关于HTTP请求的url字符串问题了。这里不多做叙述。

还有一个功能没有完成。你很快会想到:刷新(refresh)。用户在选择了其他的城市的时候,需要很快回到用户点击刷新时所在位置的天气预报。点击Refresh的时候获取用户的最新地理位置数据,并请求天气预报数据。这个功能已经实现。在用户进入主界面的时候就会自动获取用户的位置,并请求天气预报。刷新功能只需要在在refreshAction方法中重新获取用户的地理位置就可以,请求天气预报会在获得用户位置后自动执行。这里需要提到的一点是,当成功获取了天气预报之后,就应该停止获取用户地理位置。因为这样会给用户省电!省电也是用户体验的一部分。作为一个开发者,如果你的APP太多费电,而且还不是一个很好玩的游戏的话实在是说不过去的。

本系列文章的代码在这里

延伸阅读:AFNetworking是cocoaPods加载进来的。了解更多cocoaPods,请看这里

本文所使用的代码的原始版本是来自Github的这里。我们现在的代码已经比原作者的丰富的多了。不过还是要感谢原作者。

用Swift实现一款天气预报APP(三)的更多相关文章

  1. 用Swift实现一款天气预报APP(二)

    这个系列的目录: 用Swift实现一款天气预报APP(一) 用Swift实现一款天气预报APP(二) 用Swift实现一款天气预报APP(三) 上篇中主要讲了界面的一些内容,这篇主要讨论网络请求,获得 ...

  2. 用Swift实现一款天气预报APP(一)

    这个系列的目录: 用Swift实现一款天气预报APP(一) 用Swift实现一款天气预报APP(二) 用Swift实现一款天气预报APP(三) Swift作为现在苹果极力推广的语言,发展的非常快.这个 ...

  3. 个人开发者做一款Android App需要知道的事情

    个人开发者做一款Android App需要知道的事情 在大学时, 自己是学计算机专业的,而且还和老师一起做过一年半的项目. 有时候是不是有这样的想法,做一个自己的网站.但一直未付诸行动.2012年时, ...

  4. 分享一下一款直播App开发的过程

    听说有人声称开发一款直播App不仅耗时还非常昂贵,今天跟大家说道一下,开发一款直播App到底分几步走? 第一步:分解直播App的功能,我们以X客为例 视频直播功能,这是一款直播App最主要的功能,要能 ...

  5. 毕业设计--天气预报App

    9月中旬,开始动手做我的毕业设计了,之前一直在纠结做啥,后来想想,既然是做毕业设计,那就大胆地做点自己没接触过的东西吧.然后网上查找资料得知做天气预报需要用到开放的API,而且要用那种现在还在维护的, ...

  6. android入门学习-天气预报app(一)

    引言 学习<android第一行代码>根据书本开发的天气预报app,主要用于熟练操作android开发(android studio3.0平台). 今天主要分享一下从服务器上获取天气信息, ...

  7. 一款天气app的温度曲线图的实现

    原文:一款天气app的温度曲线图的实现 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/tyhzsd/article/details/50544639 ...

  8. 基于Android开发的天气预报app(源码下载)

    原文:基于Android开发的天气预报app(源码下载) 基于AndroidStudio环境开发的天气app -系统总体介绍:本天气app使用AndroidStudio这个IDE工具在Windows1 ...

  9. 天气预报APP(1)

    一个天气预报APP至少应该具备以下功能: *可以罗列出全国所有的省.市.县: *可以查看全国任意城市的天气信息: *可以自由的切换城市,去查看其他城市的天气: *提供手动更新以及后台自动更新天气的功能 ...

随机推荐

  1. 如何查看MySql的BLOB内容

    一款Mysql的工具: SQLyog. 强项在于可以把blob的内容直接显示出来. 我觉得其实做产品能够活挺厉害,因为你做的东西确实为客户提供价值:在云云产品之中,能够让客户发现你并使用,购买你的产品 ...

  2. 基于Tomcat 的WEB Project存在的安全漏洞总结

    1 检查工具:Acunetix Web Vulnerability Scanner V9破解版 2 检查漏洞说明结果显示: 2.1 HTML Form Without CSRF Protection ...

  3. jeecg中的树形控件demo

    1.comboTree控件 1.页面方法: <t:comboTree url="jeecgFormDemoController.do?getComboTreeData" va ...

  4. 慕课网 -- 性能优化之PHP优化总结笔记

    视频链接,感兴趣的可以去看看,对我来说耳目一新. http://www.imooc.com/learn/205 什么情况下遇到PHP性能问题 1 :PHP语法使用不恰当 2 :使用了PHP语言他不擅长 ...

  5. jmeter录制https请求时,浏览器每一个请求都 跳 不安全访问页面的解决方法

    1.关闭所有浏览器 2,使用终端 输入 : /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ignore-certif ...

  6. Java 字符串 String

    什么是Java中的字符串 在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对 ...

  7. PHP扩展类ZipArchive实现压缩解压Zip文件和文件打包下载 && Linux下的ZipArchive配置开启压缩 &&搞个鸡巴毛,写少了个‘/’号,浪费了一天

    PHP ZipArchive 是PHP自带的扩展类,可以轻松实现ZIP文件的压缩和解压,使用前首先要确保PHP ZIP 扩展已经开启,具体开启方法就不说了,不同的平台开启PHP扩增的方法网上都有,如有 ...

  8. phpcms模块开发中的小问题及解决方法

    1.模块菜单中文名出错 在编写安装模块时候可能需要更改extention.inc.php中定义中文名称,由于反复安装或者通过phpcms的扩展->菜单管理 修改菜单名会导致中文名失败.解决办法很 ...

  9. JSP Servlet之间交换数据

    摘自:<轻量级Java EE企业应用实战>第三版 对于每次客户端请求而言,web服务器大致需要完成以下步骤: 1.启动单独线程 2.使用I/O流读取用户的请求参数 3.从请求数据中解析参数 ...

  10. How to Integrate JCaptcha in Spring Security

    The repository for JCaptcha is this one: <repository> <id>sourceforge-releases</id> ...