用Swift实现一款天气预报APP(一)
这个系列的目录:
Swift作为现在苹果极力推广的语言,发展的非常快。这个语言就和她的名字一样,比OC减少了很多的文件和代码量。头文件,bye bye啦,再不用查个代码上下的头文件源文件切换了。而且语言本身也增加了很多的安全性的考虑,比如类的初始化个阶段的检查等。不按照规定的写就不能编译通过!本文假定你有一定的编程基础,和一定的Swift基础。如果木有的话,请看这里迅速补起。
本文就用Swif写一个APP,让各位一起来体会一下Swift到底是好在了哪里。为了简单,教程会使用Storyboard,而不是手动的添加Controller以及Controller的View的各种视图。使用Storyboard可以减少很多的代码量。这个会在之后的教程中体现出来。任何的视图跳转都是按下Ctrl之后的连线,都是Segue。这些标准的跳转都不用写一行代码。
现在的APP,没有哪个是孤立的存在在用户的手机里的。除了,额,类似于2048这样的APP没有明显的连接后台的服务器,但是,你的位置等信息都传到了后台的服务器。所以,我们的教程还要涉及到网络连接。先用SDK内置的,然后再讲解现在十分流行的AFNetworking。
这个天气预报的APP看起来会是这样的:
现在进入正题,创建我们的项目:
选择一个Single View的模版。然后:
之后填写项目名称,Swift Weather以及其他的如,组织名称等。之后一直下一步,确定就创建了项目。
这个时候你会看到除了Storyboard居然会有一个xib文件。这个xib文件不是用来在APP中结合使用ViewController的。而是专门用来适配不同个屏幕分辨率的。也就是有了这个叫做LaunchScreen.xib的文件,就不用在每一个分辨率下都做一张LaunchImage的图片了。
这里有一点需要注意。这个LaunchScreen的nib文件是不能像之前我们常用到的xib文件一样设置File Owner之后绑定IBOutlet和IBAction的。因为这个nib文件在加载的时候APP还没有加载完成,所以即使绑定了File Owner的ViewControler也是用不了的。
下面我们开始在Storyboard中穿件不同的Scene(对应到一个UIViewController)并把他们连接起来。在这之前需要明白我们的这个APP会如何工作呢?这里,我们的比较简单,不要想成新浪天气、墨迹天气。因为,这个是课后作业。看完教程以后由你自己去实现一个类似的活着更好的天气预报的客户端。我们的Demo天气预报APP相对简单。用户打开APP后直接进入APP天气的主界面查看天气。之后用户可以点击导航栏左侧的“City”按钮更换城市活着点击右侧的“Refresh”按钮来实时的刷新天气数据。
主界面的头看起来会是这个样子的:
好的,回到Storyboard上来。首先从右侧动Utilities边栏中拖动一个UINavigationController:
到Storyboard上来,并设定位Initial Controller, 如图:
拖动一个UINavigationController会自带一个RootViewController上来。选中这个Controller删了。我们要用自己的Controller作为RootViewController。删了之后从右侧到菜单中选择一个ViewController,并把鼠标放到UINavigationController上,然后按下Ctrol键,用鼠标缓缓的拖一条蓝蓝的细线到你新家上来的ViewController上。放开Ctrl。在弹出的菜单里选择最下面的relationship->rootViewController。连接完成之后就会产生一个segue。
这个APP已经可以运行起来了,按下Command+R,模拟器中就会出现这个APP。虽然现在的界面还是很简单的。
基本的结构已经有了,下面添加主界面元素。仔细观察界面,可以发现一个规律:
大体的结构是在最上面显示地点,下面的大太阳是当前的天气状况,再下面是当前的温度。在主界面下面的一排小图是分时段的天气和温度。显示的顺序都是时间,当时天气的图片和当时的温度。小的结构都是上面文字,中间图片,下面又是文字。最大的不同在于主界面的大图部分上面显示的是地名,而下面分时间显示天气部分,显示的是时间。所以,正题的结构都差不过,从上到下的顺序显示了文字图片然后文字。先记住这个,在后面会用到这个规律。现在先按照这样的规律把界面上需要的元素拖动到ViewController的View上。使用快捷键Option+Command+L,之后输入label。你会在右侧的边栏里出现UILabel这个控件,拖动到View上。其他的依次都拖动到主界面上。然后在Storyboard中绑定Controller文件。主界面对应的代码文件是MainViewController,并在代码中加入各个控件对应的IBOutlet。就像这样:
@IBOutlet weak var icon : UIImageView! @IBOutlet weak var temperature : UILabel! @IBOutlet weak var loading : UILabel! @IBOutlet weak var location : UILabel! @IBOutlet weak var time1: UILabel! @IBOutlet weak var time2: UILabel! @IBOutlet weak var time3: UILabel! @IBOutlet weak var time4: UILabel! @IBOutlet weak var image1: UIImageView! @IBOutlet weak var image2: UIImageView! @IBOutlet weak var image3: UIImageView! @IBOutlet weak var image4: UIImageView! @IBOutlet weak var temp1: UILabel! @IBOutlet weak var temp2: UILabel! @IBOutlet weak var temp3: UILabel! @IBOutlet weak var temp4: UILabel!
现在就可以绑定Storyboard的控件和MainViewController中的IBoutlet了。绑定的顺序:先、在Storyboard中选中主界面的Scene,二、点一下圈住的地方,这个时候在有边栏里就会发现有IBOutlet的一个section里面就都是我们刚刚在代码中定义的属性。如左图,有icon这个属性就是在上面的代码中有提到的定义的属性。
绑定的时候把鼠标放到比如icon的后面的小圈圈上面,然后按住Ctrl键,拖动鼠标到你要绑定的控件上,放开Ctrl键绑定就完成了。就像左图中的textLabel一样的效果。点那个小叉叉就可以断开控件和代码中的对应属性的绑定。
绑定好之后就可以在代码中修改这些控件的值等属性了。绑定好的:
这里假定你已经把界面的元素都按照主界面样子摆好了,设定好,并且已经绑定。如果有任何问题,请参考源代码。
这里必须提一件事。之所以很多公司的开发都不使用nib文件和现在的storyboard。就是因为,大家也看到了,使用nib或者storyboard文件会有很多的名称,稍微的修改就可能忘记了什么地方没有重新绑定。没有绑定控件的,运行起来的时候可能就会出现崩溃的情况。因为,根据key-value的方式存取控件的时候,没有绑定的找不到。其他还有很多需要设定名称的地方还包括IBAction还有Segue等。虽然,Storyboard有这个不好的地方。不过对于个人开发者是切实的减少了代码量。也大大的提高了开发速度!所以Storyboard的优点还是很明显的。
当全部的控件都摆好之后看起来就是这个样子的了。
以上的内动几乎都在说界面的事情了。你会看到还有两个地方没有提到。一个是City,一个是Refresh。这两个按钮式用来更换城市和刷新数据的。点了City之后弹出界面,用户可以选择一个另外的城市,查看该地的天气信息。点击Refresh之后可以重新获取当地的天气数据,并刷新主界面的数据。在City按钮的界面元素中会涉及到一个“unwind segue”的知识点。比较简单。放在下面的教程中继续讲解。
咱们的天气都是根据用户所在的位置来获取和显示的。至少在用户第一次进入APP的时候,APP自动获取用户的地理位置信息,并根据这个位置信息访问服务器信息获取天气数据。所以,首要的问题就是后去用户的地理位置,也就是经纬度。
详细的关于在iOS8下获取地理位置的方法可以看这篇。这里简单的说一下。获取位置信息都是用到CoreLocation框架。在Xcode6中无需再专门的做引入框架的操作。只要在代码中用import 看框架名称就可以。
import UIKit import CoreLocation //获取地理位置
要获取用户的地理位置是需要用户的同意的,所以首先需要在代码中请求用户的同意(在iOS8中)。请求用户的同意时的文字则一定要在项目的plist文件中配置。这个plist中的配置是必须的!否则,APP运行起来也获取不了用户的地理位置。
获取地理位置的方法和iOS的其他版本都是一样的,设定代理,在代理中获取地理位置,或者处理获取信息中产生的错误。
class ViewController: UIViewController, CLLocationManagerDelegate
上面是实现LocationManagerDelegate,下面是指定代理,设置精度和请求用户同意等。
self.locationManager.delegate = self; self.locationManager.desiredAccuracy = kCLLocationAccuracyBest self.locationManager.distanceFilter = kCLLocationAccuracyKilometer if self.locationManager.respondsToSelector("requestAlwaysAuthorization") { println("requestAlwaysAuthorization") self.locationManager.requestAlwaysAuthorization() }
这一句if self.locationManager.respondsToSelector("requestAlwaysAuthorization")是为了和iOS8之前的系统兼容。
最后看看LocationManager的代理如何获取地理位置和如何处理错误。
//MARK: CoreLocationManagerDelegate func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!){ println("get location") var location:CLLocation = locations[locations.count-1] as CLLocation if (location.horizontalAccuracy > 0) { self.locationManager.stopUpdatingLocation() println(location.coordinate) self.textLabel.text = "latitude \(location.coordinate.latitude) longitude \(location.coordinate.longitude)" } } func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) { println(error) self.textLabel.text = "get location error" }
这里的错误处理,只是在显示具体的经纬度的Label中显示了出错的文字。为了更好的用户体验,错误的提示可以采用更友好的方法。这里只简单说到这里。
这个APP的第一部分就先介绍到这里。更多内容会在后面的教程中继续解说。