本課主要開發(fā)一個天氣App,運(yùn)用58和60課的知識點(diǎn):從網(wǎng)上下載數(shù)據(jù)和處理字符串。
課程筆記文集地址:Udemy課程:The Complete iOS 9 Developer Course - Build 18 Apps
1. 找到數(shù)據(jù)來源,也就是選好網(wǎng)址
網(wǎng)址為:http://www.weather-forecast.com/locations/Paris/forecasts/latest
所以需要去.plist文件里輸入:
NSAppTransportSecurity Dictionary (1 item) 這一個item就是-> NSAllowsArbitraryLoads Boolean YES
2.布局storyboard
放置控件,設(shè)置好各種Auto Layout的約束,最終效果見下圖:
建立 Outlet 和 Action 連接:
@IBOutlet var cityTextField: UITextField!
@IBOutlet var resultLabel: UILabel!
@IBOutlet var findWeather(sender: AnyObject) {
//這是Button的Action連接
}
3.獲取網(wǎng)絡(luò)數(shù)據(jù)
首先是Lecture 58中學(xué)到的代碼,幾乎沒有什么太大的變化,如下:
let url = NSURL(string:"http://www.weather-forecast.com/locations/Paris/forecasts/latest")
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
//這里的代碼都是task完成之后再執(zhí)行,如果沒有完成,則不執(zhí)行,如果無法執(zhí)行,也不執(zhí)行之后的代碼
if let urlContent = data {
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
print (webContent)
} else {
// 這里可以寫出錯的提示神馬的
}
}
task?.resume()
從打印出來代碼可以看出來,我們需要的數(shù)據(jù)只是其中一行,那么接下來需要做的事情就是把這一行從數(shù)據(jù)中提取出來。鼠標(biāo)右鍵查看源碼(谷歌瀏覽器),比起print(webContent)
更容易閱讀。
下圖是我們需要的信息:
查看源碼之后找到對應(yīng)的部分:
所有的源碼實(shí)際上就是webContent
,現(xiàn)在我們只需要這一小部分,那么怎么截取出來呢?
首先用上一節(jié)課學(xué)到的知識把webContent
分成字符串類型的數(shù)組,這樣就方便取出需要的字段了。
newTypeString.componentsSeparatedByString("某某字符串")
那我們用哪個字符串來分割webContent
呢?
就是:
3 Day Weather Forecast Summary:</b><span class="read-more-small"><span class="read-more-content"> <span class="phrase">
這段放到代碼中時要注意雙引號的問題,在Swift中,兩個雙引號之間表示是一個字符串,所以直接復(fù)制到代碼中后,還需要對代碼進(jìn)行小小的修改,在雙引號前面加上 \ 表示這是一個標(biāo)點(diǎn)符號不是表示字符串:
let websiteArray = webContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
這里最好確保websiteArray
這個數(shù)組里真正有內(nèi)容,如果網(wǎng)頁的布局變了,或者網(wǎng)站停止服務(wù)了,掛掉了之類的,數(shù)組里就不會有值,這樣空值出現(xiàn),會造成應(yīng)用崩潰,用if確保有值之后再去繼續(xù)往下走:
if websiteArray!.count > 1 {
print(websiteArray![1])
}
然后繼續(xù)websiteArray[1]
中的內(nèi)容,最后得到我們想要的信息:
if websiteArray!.count > 1 {
let weatherArray = websiteArray![1].componentsSeparatedByString("</span")
}
還是老樣子,要確保 weatherArray
這個數(shù)組里真的有內(nèi)容:
if weatherArray!.count > 1 {
let weatherSummary = weatherArray[0]
將weatherSummary賦值給對應(yīng)的Label控件
}
將weatherSummary賦值給對應(yīng)的Label控件,這個步驟可以用多線程的方法放到主線程來做:
if weatherArray!.count > 1 {
let weatherSummary = weatherArray[0]
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.resultLabel.text = weatherSummary
})
}
運(yùn)行之后發(fā)現(xiàn),weatherSummary
里的攝氏度沒有顯示出來,所以我們用上一節(jié)的知識替換出正確的符號:
let weatherSummary = weatherArray[0].stringByReplacingOccurrencesOfString("°", withString:"???")
攝氏度的快捷鍵:Option+Alt+Z
4.更換城市
我們現(xiàn)在使用的是巴黎的天氣情況,如果是其他城市的情況怎么辦呢?
根據(jù)網(wǎng)站上的信息,我們發(fā)現(xiàn),只需要替換網(wǎng)址中的Paris即可。
let url = NSURL(string:"http://www.weather-forecast.com/locations/" +cityTextField.text! +"/forecasts/latest")!
不過輸入帶有空格的城市如 San Francisco,會造成程序崩潰,解決方法:
let url = NSURL(string:"http://www.weather-forecast.com/locations/" + cityTextField.stringByReplacingOccurrencesOfString(" ", withString:"??-") + "/forecasts/latest")!
輸入亂七八糟的字符也會造成崩潰,解決方法:
let attemptedUrl = NSURL(string:"http://www.weather-forecast.com/locations/" + cityTextField.stringByReplacingOccurrencesOfString(" ", withString:"??-") + "/forecasts/latest")!
if let url = attemptedUrl {
//這里繼續(xù)往下的所有操作
}
5.錯誤提示
如果用戶輸入了錯誤的信息,需要提示一下,我們沒有獲取到城市信息,不然用戶看到程序界面沒有變化,以為死掉了呢。
聲明一個 wasSuccessful 的布爾值,當(dāng)為false時,更新Label的內(nèi)容。
6.完整代碼
到這里,代碼就已經(jīng)全部寫完了。
全部代碼如下:
import UIKit
class ViewController: UIViewController {
@IBOutlet var cityTextField: UITextField!
@IBOutlet var resultLabel: UILabel!
@IBAction func findWeather(sender: AnyObject) {
var wasSuccessful = false
let attemptedUrl = NSURL(string: "http://www.weather-forecast.com/locations/" + cityTextField.text!.stringByReplacingOccurrencesOfString(" ", withString: "-") + "/forecasts/latest")
if let url = attemptedUrl {
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
let websiteArray = webContent!.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
if websiteArray.count > 1 {
let weatherArray = websiteArray[1].componentsSeparatedByString("</span>")
if weatherArray.count > 1 {
wasSuccessful = true
let weatherSummary = weatherArray[0].stringByReplacingOccurrencesOfString("°", withString: "o")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.resultLabel.text = weatherSummary
})
}
}
}
if wasSuccessful == false {
self.resultLabel.text = "Couldn't find the weather for that city - please try again."
}
}
task.resume()
} else {
self.resultLabel.text = "Couldn't find the weather for that city - please try again."
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}