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

这个系列的目录:

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

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

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

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

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对象的属性:

@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。

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已经创建了,只重新赋值而不再创建!参见下面的代码:

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier") as? UITableViewCell
        if cell == nil {
            cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CellIdentifier")
        }

        cell?.textLabel.text = self.cityList.values.array[indexPath.row]

        return cell!
    }

首先按照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的属性:

@IBOutlet weak var cityNameLabel: UILabel!

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

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

看到有什么不同的么。是的,这里不用再从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的。

@objc protocol CityListViewControllerDelegate{
    func cityDidSelected(cityKey: String)
}

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

@IBOutlet weak var delegate: CityListViewControllerDelegate?

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

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        println("did select row \(indexPath.row)")

        // set current selected index of city list
        self.selectedIndex = indexPath.row
        if self.selectedIndex != nil && self.delegate != nil {
            var cityKey = self.cityList.keys.array[self.selectedIndex!]
            self.delegate?.cityDidSelected(cityKey)
        }
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }

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

class MainViewController: UIViewController, CLLocationManagerDelegate, CityListViewControllerDelegate{
    //MARK: city changed delegate
    func cityDidSelected(cityKey: String){
        println("selected city \(cityKey)")

    }}

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

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

func updateWeatherInfo(cityName: String)

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

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

本系列文章的代码在这里

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

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

时间: 2024-10-15 14:57:30

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

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

Swift作为现在苹果极力推广的语言,发展的非常快.这个语言就和她的名字一样,比OC减少了很多的文件和代码量.头文件,bye bye啦,再不用查个代码上下的头文件源文件切换了.而且语言本身也增加了很多的安全性的考虑,比如类的初始化个阶段的检查等.不按照规定的写就不能编译通过!本文假定你有一定的编程基础,和一定的Swift基础.如果木有的话,请看这里迅速补起. 本文就用Swif写一个APP,让各位一起来体会一下Swift到底是好在了哪里.为了简单,教程会使用Storyboard,而不是手动的添加C

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

上篇中主要讲了界面的一些内容,这篇主要讨论网络请求,获得天气的数据.具体的说是HTTP请求天气站点的API,得到返回的JSON数据.解析这些数据,并更新到界面内容中. 让用户知道当前的和之后几个小时的天气状况. 发起HTTP请求主要用到的是SDK的NSURLSession这个类,使用这个类对象可以创建请求任务并在这个任务中处理请求之后由服务器返回的JSON数据.在NSURLSession之前主要用到的是NSURLConnection.这两个类比较类似.只是在NSURLSession中增加了后台执

随便山寨个1305款假App?背后或许有个你不知道的大蓝海

恶意.山寨APP的存在,其实提供了一个应用审核之外的"杀毒"市场.这个结果呢,会很像当年流氓软件横行下,安全软件出现和最终变成流量入口的重现. 文/张书乐 刊载于<法人>杂志2017年4月刊 在315国际消费者权益日前夕,知名上网工具WI-FI万能钥匙对外宣布,截至2017年3月,WI-FI万能钥匙联合各大应用市场及手机厂商,在各个渠道共筛查出1387款次"WI-FI万能钥匙"的山寨应用,经过努力,已暂时将1305款次山寨应用下架. 然而,一款热门产品有

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

个人开发者做一款Android App需要知道的事情 在大学时, 自己是学计算机专业的,而且还和老师一起做过一年半的项目. 有时候是不是有这样的想法,做一个自己的网站.但一直未付诸行动.2012年时, 终于付诸行动了,花了三个月,现学现卖, 熬夜通宵用PHP做了一个小网站,但后续就再没有坚持下去. 直到从电信行业转行互联网行业后,做一款属于自己的应用的的想法越来越迫切,于是今年开始便投入到Android App开发的阵营中来.今年断断续续做了4款Android App应用, 一款公司应用,三款自

Android简易版天气预报app的实现(改进版)

最近总是有人来和我说我以前写的一个小app无法正常获取数据~Android简易版天气预报app 今天就又运行了下来查找问题,发现或许是接口有限制吧,不能在多台手机使用同个apikey 然后,发现了我写的代码实在乱七八糟,界面也实在不好看,就又重写了一遍,小小地修改了一遍,开发环境改为了Android Studio 最终效果图如下 工程图如下 一.获取地区信息 做这么一个天气预报app,首先就要获取到国内地区列表 (在我的另一篇博客有介绍:向任意网址发起数据请求) 中国天气网开放有天气预报接口,访

3月第4周业务风控关注 |9款违规App曝光:涉及恶意扣费、隐私窃取、×××

易盾业务风控周报每周呈报值得关注的安全技术和事件,包括但不限于内容安全.移动安全.业务安全和网络安全,帮助企业提高警惕,规避这些似小实大.影响业务健康发展的安全风险.一.9款违规App曝光:涉及恶意扣费.隐私窃取.×××据网信广东消息,国家计算机病毒应急处理中心近期在净网行动中通过互联网监测发现,9款违法有害移动应用存在于移动应用发布平台中,其主要危害涉及恶意扣费.隐私窃取.×××三类.这些违法有害移动应用具体如下:1.<PhotoLoop>(版本20.4).<快对答案>(版本7.

我向大家推荐一款手机app——百度地图

对于广大路痴来说,经常找不到路是一种是十分痛苦的经历,但是在现在智能手机时代一款手机app横空出世--百度地图,成为广大路痴的一个福音 这是百度地图的初始界面,清晰简明,一目了然.点击嘴上方的对话框会弹出一下画面 点击嘴上方的对话框会弹出一下画面,使用者就可以直接输入自己想去的地方,我的位置可以是根据手机的位置自动定位的,当然也可以手动输入,同时也可以选择出行方式出租·驾车·步行等等十分的方便,在界面的下方会有经常使用的地址,只要点击一下就可以使用,用户体验性非常的好 确定了目的地和出行方式以后

大视电子推出世界首款HDMI版三屏宝

大视电子推出世界首款HDMI版三屏宝 目前上海大视电子科技有限公司(上海股权交易中心挂牌,企业名称:大视科技:股票代码:201276),目前推出了HDMI版三屏宝,型号为MV303-HDMI. 图一 MV303-HDMI三屏宝 大视电子MV303-HDMI三屏宝是世界首款支持HDMI 1.4 3Ghz输入的三屏宝支持4Kx2K输入,MV303-HDMI 支持水平1X2,1X3分割,同时也支持垂直2X1,3X1分割,支持常规三屏宝3072x768 60Hz,3840x800 60Hz,3840x1

推荐一款我个人认为非常好的一款手机APP

今天我要说的这款手机APP名字叫做支付宝,我相信绝大多数人都不会陌生,基本上每个人在用.支付宝是国内领先的第三方支付平台,致力于提供"简单.安全.快捷"的支付解决方案.主要提供支付.理财.网购担保.网络支付.转账.手机充值.水电缴费.等多个领域的.为零售百货.电影院线.超市.出租车.等多个行业提供了优质的服务. 首先我喜欢使用它的理由是他的功能非常的强大,健全.实用.由于它的功能十分的多这里就不一一列举了,我只是列举几个我自己个人经常用到的几个能. 首先打开APP进入首页,给人的第一感