概述
摘要:用JSON和工具欄做一個分析白宮請愿書的app
概念:JSON,NSData致盟,UITabBarController
1.設(shè)置
2.創(chuàng)建基本UI:UITabBarController
3.JSON解析:NSData 和SwiftyJSON
4.演繹一個請求:loadHTMLString
5.結(jié)束接觸:didFinishLaunching
6.總結(jié)
設(shè)置
這個項目會從網(wǎng)站獲取數(shù)據(jù)并為用戶整理有用的消息碎税。按照以往的管理,這就是教你一些新的iOS開發(fā)技術(shù)的方法勾邦,但讓我們面對現(xiàn)實——你已經(jīng)完成了2個app和2個游戲了蚣录,所以你已經(jīng)開始建立起一個很好的工作庫!
這次眷篇,你會學習UITabBarController萎河,NSData等內(nèi)容。你也會使用一種叫JSON的數(shù)據(jù)格式蕉饼,它是一種很流行的線上收發(fā)數(shù)據(jù)的方式虐杯。要發(fā)現(xiàn)一些有趣的JSON免費訂閱服務(wù)不太容易,但但我們要獲取的是“We the people”美國白宮的請愿書,在這里美國人可以提交要求某些行動的發(fā)生玛荞,而其他人可以對它進行投票创葡。
有些是完全不重要的,但它也有好的达舒,干凈的JSON可以讓每個人閱讀,這讓它成為我們的訂閱源變得十分合適叹侄。很多要學巩搏,很多要做,所以趾代,讓我們開始吧:在Xcode中創(chuàng)建一個新項目贯底,選擇Master-Detail Application 模板,命名為project7并保存撒强。
原文鏈接:
https://www.hackingwithswift.com/read/7/1/setting-up
創(chuàng)建基本UI:UITabBarController
目標給了我們很多不需要的東西禽捆,但這次我們不是要刪除而是調(diào)整笙什。在用戶界面上我們只需要做一點調(diào)整,所以現(xiàn)在在IB中打開Main.storyboard胚想。
在故事板中琐凭,你會看到有兩個導(dǎo)航控制器:上下各一。選中上面的那個浊服,然后選擇菜單Editor>Embed In>Tab Bar Controller淘正。跟UINavigationController一樣,UITabBarController也是個iOS用戶界面設(shè)計中常用的元素臼闻。標簽欄是底部的一條圖標帶鸿吆,它可以顯示很多屏幕的內(nèi)容,在App Store述呐,音樂惩淳,電話等app中都可以看到。
在屏幕背后乓搬,UITabBarController管理著可供用戶選擇的一組視圖控制器思犁。你確實可以經(jīng)常在IB中完成很多工作,但不是在這個項目中进肯。我們要用一個標簽來顯示近期請愿激蹲,另一個顯示熱門請愿,他倆其實是一樣的——只是數(shù)據(jù)來源不同而已江掩。
在故事板中完成全部任務(wù)意味著要復(fù)制我們的視圖控制器学辱,這是個糟糕的主意,所以我們要做的正相反环形,我們要設(shè)計一個標簽的內(nèi)容策泣,然后用代碼復(fù)制一份。
現(xiàn)在我們的導(dǎo)航控制器已經(jīng)在一個標簽欄控制器里了抬吟,在IB中它的底部帶有一條灰色帶子萨咕。如果你現(xiàn)在點擊它,它會選中一個叫UITabBarItem的新類型的對象火本,也就是早標簽欄中顯示視圖控制器的圖標和文字危队。在屬性觀察器中把System Item從“Custom”改成“Most Recent”。
關(guān)于UITabBarItem有一件很重要的事情是當你設(shè)置好它的System Item钙畔,它會同時給標簽的標題以圖標和標題茫陆。如果你嘗試把文本改成你自己的內(nèi)容,圖標就會被移除而你也需要提供自己的刃鳄。這是因為Apple已經(jīng)讓用戶形成“某些圖標就是代表某些信息”的習慣盅弛,而他們不想你用一種不正確的方式來使用這些圖標钱骂!
選中導(dǎo)航控制器本身(直接點擊Navigation Controller的文字)叔锐,然后按下Alt+Cmd+3來打開身份觀察器挪鹏。我們沒進過這里,因為不是那么常用愉烙。但這里讨盒,我要你做的是在Storyboard ID這一項右邊的空格中輸入NavController。我們很快就會用到了步责!
另外一個改動是在表視圖控制器中返顺。點擊Title(就在Prototype Cells下面),然后在屬性觀察器中你會看到一些關(guān)于cell的選項蔓肯。在屬性觀察器的頂部顯示“Table View Cell”表示你選對了對象遂鹊。把style從“Basic”改成“Subtitle”,它會在標題下面添加一個副標題蔗包。
IB部分搞定了秉扑,現(xiàn)在打開MasterViewController.swift文件來做一些基礎(chǔ)性改動。首先调限,刪除整個insertNewObject()方法舟陆,刪除viewDidLoad()中除了super.viewDidLoad()之外的全部內(nèi)容,刪除表視圖的commitEditingStyle 和 canEditRowAtIndexPath方法耻矮,最后刪除prepareForSegue()和cellForRowAtIndexPath方法中的as! NSDate——不是一整行秦躯,而只是as! NSDate。
第一步完成了:我們有了一個基本的用戶界面裆装,而且我們已經(jīng)清理了模板中的不想要的煩人東西□獬校現(xiàn)在開始碼代碼……
原文鏈接:
https://www.hackingwithswift.com/read/7/2/creating-the-basic-ui-uitabbarcontroller
JSON解析:NSData 和SwiftyJSON
JSON——JavaScript Object Notation的縮寫——是一種描述數(shù)據(jù)的方式。它不是讀取自己的最簡單方式哨免,但它簡單緊湊勾扭,電腦很容易分析,所以它在帶寬還很窄的時候它就在網(wǎng)上很流行了铁瞒。
項目6中你已經(jīng)學過在Auto Layout中使用字典妙色,在這個項目中我們會在更大范圍內(nèi)使用字典。甚至慧耍,我們還會把字典放進數(shù)組身辨,這樣就能保證數(shù)據(jù)按順序排列。
你用方括號定義一個字典芍碧,然后輸入它的key類型煌珊,一個冒號,再是它的value類型泌豆。比如一個key為字符串定庵,UILabel為值的字典的定義應(yīng)該如下所示:
var labels = [String: UILabel]()
定義數(shù)組時,我們用的是:
var strings = [String]()
所以把這倆放到一起就是我們想要的字典數(shù)組,每個字典都有一個字符串關(guān)鍵字和一個字符串值蔬浙。所以猪落,它看起來是介樣:
var objects = [[String: String]]()
把它放在MasterViewController.swift的頂部的objects定義的位置上,現(xiàn)在的類型為AnyObjects畴博,這完全沒用笨忌。
是時候分析一些JSON了,這意味著要處理它并檢查它的內(nèi)容俱病。在Swift中并不容易官疲,所以出現(xiàn)了一系列的輔助庫,用它們就可以幫你完成很多的重任亮隙。我們將要用到其中的一部分:從GitHub中下載項目文件然后找到一個叫SwiftyJSON.swift的文件途凫。把它添加進你的項目,確币缥牵“Copy items if needed”已經(jīng)勾選颖榜。
SwiftyJSON讓我們可以用一種非常接近直覺的方式讀取JSON:你可以把所有的東西都當成字典來對待,所以如果你知道有個值叫“information”煤裙,它包含另外一個值“name”掩完,而它又包含另外一個值“firstName”,你可以使用json["information"]["name"]["firstName"]來獲得數(shù)據(jù)硼砰,然后使用string屬性來向Swifty請求獲取它且蓬。
在我們分析之前,這里是你接收到的實際JSON中極小的一部分:
{
"metadata":{
"responseInfo":{
"status":200,
"developerMessage":"OK",
}
},
"results":[
{
"title":"Legal immigrants should get freedom before undocumented immigrants – moral, just and fair",
"body":"I am petitioning President Obama's Administration to take a humane view of the plight of legal immigrants. Specifically, legal immigrants in Employment Based (EB) category. I believe, such immigrants were short changed in the recently announced reforms via Executive Action (EA), which was otherwise long due and a welcome announcement.",
"issues":[
{
"id":"28",
"name":"Human Rights"
},
{
"id":"29",
"name":"Immigration"
}
],
"signatureThreshold":100000,
"signatureCount":267,
"signaturesNeeded":99733,
},
{
"title":"National database for police shootings.",
"body":"There is no reliable national data on how many people are shot by police officers each year. In signing this petition, I am urging the President to bring an end to this absence of visibility by creating a federally controlled, publicly accessible database of officer-involved shootings.",
"issues":[
{
"id":"28",
"name":"Human Rights"
}
],
"signatureThreshold":100000,
"signatureCount":17453,
"signaturesNeeded":82547,
}
]
}
實際上你會得到大約2000-3000行這樣的東西题翰,全都是來自美國公民恶阴,關(guān)于政治分類下的請愿。請愿的內(nèi)容跟我們沒啥關(guān)系豹障,我們關(guān)心的是它的數(shù)據(jù)結(jié)構(gòu)冯事。特別是:
1. 這是個元數(shù)據(jù)值,它包含了一個responseInfo值血公,responseInfo再包含了一個狀態(tài)值昵仅。狀態(tài)200是網(wǎng)絡(luò)開發(fā)者用來表示“一切OK”的意思。
2.這里有個結(jié)果值累魔,它包含了一系列的請愿摔笤。
3.每個請愿包含一個標題,一個正文垦写,一些相關(guān)事件吕世,還有一些特征信息。
4.JSON也有字符串和整型梯投。注意字符串都是在引號中被使用命辖,而整型不需要况毅。
現(xiàn)在你對JSON的工作方式有了基本的了解,是時候該寫點代碼了尔艇。我們將要升級viewDidLoad()方法這樣它就能從WhiteHouse請愿系統(tǒng)中下載數(shù)據(jù)尔许,然后把它轉(zhuǎn)化成SwiftyJSON對象,同時確認狀態(tài)值等于200漓帚。
我們將用NSURL和一個新的NS類NSData來完成這件事。NSData類是被設(shè)計用來存放任何格式的數(shù)據(jù)午磁,可能是字符串尝抖,可能是圖像,又可能是其他的什么東西迅皇。你已經(jīng)見過用contentsOfFile從硬盤載入數(shù)據(jù)可以創(chuàng)建NSString昧辽。contentsOfURL可以從URL(一定要用NSURL)下載數(shù)據(jù),然后創(chuàng)建NSData(還有NSString)登颓。
下面是新的viewDidLoad()方法:
override func viewDidLoad() {
? ? ? ?super.viewDidLoad()
? ? ? ?let urlString = "https://api.whitehouse.gov/v1/petitions.json?limit=100"
? ? ? ?if let url = NSURL(string: urlString) {
? ? ? ? ? ? if let data = try? NSData(contentsOfURL: url, options: []) {
? ? ? ? ? ? ? ? ? let json = JSON(data: data)
? ? ? ? ? ? ? ? ? if json["metadata"]["responseInfo"]["status"].intValue == 200 {
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ?}
}
讓我們關(guān)注下新內(nèi)容:
URLString指向Whitehouse.gov服務(wù)器搅荞,連接請愿系統(tǒng)。
我們用if/let來確保NSURL是可用的框咙,而不是強制解包咕痛。晚點你可以回過來添加更多的鏈接,所以這樣做更安全喇嘱。
我們用contentsOfURL方法創(chuàng)建了一個新的NSData對象茉贡。它從NSURL返回了內(nèi)容,也就是我們所需的——這就是我們使用[]作為選項的原因者铜。它可能會拋出一個錯誤腔丧,所以我們要用try?.
如果NSData對象被成功創(chuàng)建,我們就從它創(chuàng)建一個新JSON對象作烟。這就是個SwiftyJSON結(jié)構(gòu)體愉粤。
最后,我們有了JSON解析的第一位:如果存在“metadata”值拿撩,它包含的“responseInfo”值也包含的“status”值衣厘,將其作為整型返回,然后跟200進行比較压恒。
“we're OK to parse头滔!”以“//”開頭,表示Swift中的評論涎显。評論通常會被編譯器忽視坤检;我們寫它是給自己看的。
SwiftyJSON擅長JSON解析的理由是它的核心內(nèi)建了可選性期吓。如果任何“metadata”早歇、“responseInfo”倾芝、“status”不存在,它就會返回0給status——我們不需要每個都單獨檢查箭跳。如果你在讀取一個字符串的值晨另,SwiftyJSON會返回它找到的字符串,或者一個空字符串谱姓,如果不存在的話借尿。
代碼并不完美,還遠著呢屉来。實際上路翻,viewDidLoad()從互聯(lián)網(wǎng)上下載數(shù)據(jù)時,我們的app會在數(shù)據(jù)轉(zhuǎn)移完全之前都會被鎖定茄靠。有解決辦法茂契,但為了避免過于復(fù)雜它們會在項目9中出現(xiàn)。
現(xiàn)在慨绳,讓我們集中關(guān)注JSON解析掉冶。我們已經(jīng)為數(shù)據(jù)字典準備了一個objects數(shù)組。我們想要把解析過的JSON存入字典中脐雪,每個字典都有三個值:請愿的標題厌小,正文,支持票數(shù)战秋。一旦存儲完成召锈,我們需要告訴表視圖讓它重新載入自身。
準備好了获询?因為比起它要完成的工作量來說涨岁,代碼真的是出奇的簡單:
func parseJSON(json: JSON) {
? ? ? ? for result in json["results"].arrayValue {
? ? ? ? ? ? ? ? let title = result["title"].stringValue
? ? ? ? ? ? ? ? let body = result["body"].stringValue
? ? ? ? ? ? ? ? let sigs = result["signatureCount"].stringValue ? ??
? ? ? ? ? ? ? ? let obj = ["title": title, "body": body, "sigs": sigs]
? ? ? ? ? ? ? ? objects.append(obj)
? ? ? ? ? }
? ? ? ? ? tableView.reloadData()
}
把這個方法直接放到viewDidLoad()方法下面,然后把viewDidLoad()中的//we're OK to parse!用這個替代:
parseJSON(json)
方法parseJSON()從它得到的JSON對象讀取數(shù)組“result”吉嚣。如果你回顧一下我給你的JSON代碼片段梢薪,results數(shù)組包含所有的請愿等待讀取。當你通過SwiftyJSON使用arrayValue時尝哆,你會返回一個有對象的數(shù)組或者空數(shù)組秉撇,所以我們在循環(huán)中使用返回值。
對于結(jié)果數(shù)組中的每個結(jié)果秋泄,我們需要讀出三個值:標題琐馆,正文,還有支持簽名數(shù)恒序,所有都是以字符串的格式獲取瘦麸。簽名數(shù)從JSON中得到時實際是數(shù)字,但SwiftyJSON為我們把它轉(zhuǎn)換成字符串是因為我們的字典中key和value都是字符串歧胁。
每次我們用stringValue來訪問result中的一個元素滋饲,我們會得到它的值或者空字符串厉碟。無論如何,我們都會得到點什么屠缭,所以我們從所有三種值構(gòu)建一個新的字典箍鼓,然后用objects.append()來把新的字典加入到我們的數(shù)組中。
一旦所有的結(jié)構(gòu)都被解析完畢呵曹,我們會告訴表視圖重新載入款咖,這樣代碼就完成了。
好了奄喂,如果Whitehouse實際使用的是好的HTTPS铐殃,那么我們的app就已經(jīng)完成了。雖然我們要獲取的URL的開頭是https://api.whitehouse.gov砍聊,在寫的時候HTTPS形式非常弱所以iOS9并不信任它背稼。所以贰军,如果你嘗試運行代碼你會得到一個錯誤提示:“NSURLSession/NSURLConnection HTTP load failed(kCFStreamErrorDomainSSL, -9802)”玻蝌。(NSURLSession/NSURLConnection HTTP載入失敗)
解決辦法是給Whitehouse的連接安全度升級。我們可以請求iOS允許這個網(wǎng)站主機作為例外處理词疼,辦法是修改它的App Transport Security Settings(應(yīng)用程序傳輸安全設(shè)置)俯树。這是個煩人的東西,我本來不想給你看贰盗,除非是萬不得已许饿。我恐怕現(xiàn)在就是。
所以舵盈,在項目導(dǎo)航中找到一個名為Info.plist的文件陋率。右擊它然后選擇Open As>Source Code。它的結(jié)尾是介樣的:
</dict>
</plist>
在此之前秽晚,我想讓你加入下面的代碼:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>whitehouse.gov</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads<key>
<true/>
</dict>
</dict>
</dict>
以上代碼增加了一個App Transport Security例外意思是iOS不會因為WhiteHouse的弱證書而拒絕處理它的數(shù)據(jù)瓦糟。
現(xiàn)在你可以運行程序,雖然它目前還不是最理想的:你會看到一行行奇怪的文字格式赴蝇。這是因為Xcode項目在表視圖的cellForRowAtIndexPath方法中內(nèi)建了如下代碼:
cell.textLabel!.text = object.description
單元格的文本標簽期待的是一個字符串菩浙,而不是字典,但Xcode的默認模板代碼用object.description要讓object以字符串的形式被描述句伶。換做是字典劲蜻,打印出來的就是一組漂亮的key/value布局,里面有字典的所有內(nèi)容考余。
我們想把它調(diào)整成輸出字典的title值先嬉,而且我們還要用到被從基礎(chǔ)改成副標題的單元格中的副標題文本標簽。把現(xiàn)在的代碼(上面的一行)改成:
cell.textLabel!.text = object["title"]
cell.detailTextLabel!.text = object["body"]
我們已經(jīng)給字典里的title楚堤,body和sigs鍵都賦了值坝初,現(xiàn)在我們可以讀取它們來配置單元格了浸剩。
如果現(xiàn)在運行,你會看到該有的都有了——每個單元格現(xiàn)在顯示了請愿標題還有下面的部分正文內(nèi)容鳄袍【钜空間不足時,副標題最后默認顯示“...”拗小,是時候該給用戶看點現(xiàn)在的熱門了重罪。
原文鏈接:
https://www.hackingwithswift.com/read/7/3/parsing-json-nsdata-and-swiftyjson
演繹一個請求:loadHTMLString
JSON解析完成之后就簡單了:我們要升級DetailViewController類這樣提取請愿內(nèi)容時會更加漂亮。最簡單的從網(wǎng)站演繹復(fù)雜內(nèi)容的方法差不多總是要用到WKWebView哀九,所以我們也會在這里用到跟項目4一樣的技術(shù)來調(diào)整DetailViewController剿配,來讓它擁有一個網(wǎng)頁視圖。
把DetailViewController的所有內(nèi)容用下面的替代:
import UIKit
import WebKit
class DetailViewController: UIViewController {
? ? ? ?var webView: WKWebView!
? ? ? ?var detailItem: [String: String]!
? ? ? ?override func loadView() {
? ? ? ? ? ? ? webView= WKWebView()
? ? ? ? ? ? ? view = webView
? ? ? ?}
? ? ? ?override func viewDidLoad() {
? ? ? ? ? ? ? super.viewDidLoad()
? ? ? ?}
}
這跟項目4的代碼幾乎一樣阅束,但你應(yīng)該已經(jīng)注意到我增加了一個detailItem屬性來存放我們的字典數(shù)據(jù)呼胚。
這部分比較簡單,難的是我們不能直接把請愿文本內(nèi)容直接放進網(wǎng)絡(luò)視圖息裸,因為這樣會讓顯示變得細長蝇更。相反,我們得用HTML來解析代碼呼盆。HTML是另一門全新的語言有它自己的規(guī)則和復(fù)雜性年扩。但這個系列不叫“Hacking with HTML”,所以我不打算講很多HTML的細節(jié)問題访圃。但我會說我們要用的HTML會告訴iOS讓頁面適應(yīng)設(shè)備厨幻,而且我們希望字體大小為標準的150%。所有這些HTML會跟我們字典的body值連在一起送入網(wǎng)絡(luò)視圖腿时。
把下面的代碼放入viewDidLoad()中super.viewDidLoad()下面:
guard detailItem != nil else { return }
if let body = detailItem["body"] {
var html = "<html>"
html += "<head>"
html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
html += "<style> body { font-size: 150%; } </style>"
html += "</head>"
html += "<body>"
html += body
html += "</body>"
html += "</html>"
webView.loadHTMLString(html, baseURL: nil)
}
這里有個重要的Swift新語句:guard况脆。它的目的是創(chuàng)建“提早返回”,就是說在你碼完代碼以后批糟,如果它發(fā)現(xiàn)重要數(shù)據(jù)丟失就會直接跳出不再繼續(xù)執(zhí)行代碼格了。這里我們希望在detailItem被設(shè)置之前程序不會運行,此時guard就會執(zhí)行return跃赚。
我已經(jīng)試著把HTML盡可能的講清楚笆搓,但如果你覺得無所謂也不要緊。重要的是Swift有個叫html的字符串用來存放顯示頁面所需的全部代碼纬傲,通過方法loadHTMLString()來完成满败。這跟之前載入HTML的方法不同,因為我們不需要載入整個網(wǎng)絡(luò)叹括,而只是一部分算墨。
詳情視圖控制器部分就到這里了,真的挺簡單汁雷。好了净嘀,試試新完成的程序吧报咳。
原文鏈接:
https://www.hackingwithswift.com/read/7/4/rendering-a-petition-loadhtmlstring
結(jié)束接觸:didFinishLaunching
在結(jié)束之前,我們還要做兩個小改動挖藏。第一暑刃,我們要給UITabBarController增加一個顯示熱門請愿的標簽,其次膜眠,我們要通過增加錯誤提示來讓NSData載入過程更加人性化岩臣。
我之前就說過,我們不能直接把第二個標簽放進故事板因為兩個標簽都持有一個MasterViewController宵膨,而這樣的話就要求我們要在故事板里面復(fù)制一個視圖控制器架谎。你可以這樣做,但請不要這樣——它簡直就是個夢靨辟躏!
相反谷扣,我們要做的是不改動故事板而是用代碼來創(chuàng)建第二個視圖控制器。這是之前沒做過的事捎琐,但它并不困難会涎,而且我們已經(jīng)踏出第一步了。
打開AppDelegate.swift野哭。它一直在我們的項目中在塔,但是我們從未使用過幻件。在文件的頂部找到didFinishLaunching拨黔。當app準備運行時它會被調(diào)用,而我們準備通過改動它來達到在標簽欄插入第二個MasterViewController的目的绰沥。
方法中已經(jīng)有些代碼在了篱蝇,但我們還要在return true之前加入更多:
let tabbarController = splitViewController.viewControllers[0] as! UITabBarController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIndentifier("NavController") as! UINavigationController
vc.tabBarItem = UITabBarItem(tabBarSystemItem: .TopRated, tag: 1)
tabBarController.viewControllers?.append(vc)
幾乎每行都是新的,所以讓我們深挖下:
我們的故事板在我們的視圖控制器顯示的地方自動創(chuàng)建了一個新窗口徽曲。這個窗口需要知道它的初始視圖控制器是哪個零截,然后把它賦值給rootViewController屬性。這些全都是由故事板完成的秃臣。
在Master-Detail Application 模板中涧衙,根視圖控制器是UISplitViewController,它有一個屬性叫viewControllers奥此,用于儲存兩個項目:第一個是左邊的視圖控制器(即表視圖)弧哎,第二個是右邊的(詳情視圖)。
我們需要手動創(chuàng)建一個新的MasterViewController稚虎,第一種方法是引用Main.storyboard文件撤嫩。可以用類UIStoryboard完成蠢终。你不需要提供目錄序攘,因為nil表示使用默認目錄茴她。
我們用名字超長的方法instantiateViewControllerWithIdentifer()創(chuàng)建視圖控制器,以我們想要的視圖控制器的故事板ID作為參數(shù)傳入程奠。早些時候我們給導(dǎo)航控制器設(shè)置ID為“NavController”丈牢,所以我們使用它作為參數(shù),并將結(jié)果的類型轉(zhuǎn)換為UINavigationController瞄沙。
我們?yōu)樾碌囊晥D控制器創(chuàng)建了一個新的UITabBarItem對象赡麦,給它“Top Rated”圖標和tag1。標簽很重要帕识,但不是現(xiàn)在泛粹。
我們把心的視圖控制器加入到我們的標簽欄控制器數(shù)組viewController中,它會在標簽欄中顯示新的標簽肮疗。