#24 製作臺灣頭條新聞 App(串接 News API)
Delegate data transfer / Remote JSON parsing / Simple image caching / segue or performSegue(withIdentifier:sender:) + prepare(for:sender:) data transfer / WKNavigationDelegate / UIRefreshControl
delegate 傳資料
概念同 #22 製作臺灣迷因測驗 App,要透過 protocol 和 delegate 實現「通知 main thread 資料已送達並回傳」的功能。
步驟
・發明 ArticleModelProtocol 協定和其方法 func articlesRetrieved(_ articles:[Article])
。
・讓 ViewController 遵從 ArticleModelProtocol 協定。
・在 ArticleModel 內宣告 delegate
,delegate 會遵從協定。
・在 ViewController 生 ArticleModel 物件 model
。ViewDidLoad 內寫 model.delegate = self
。
・定義該協定的方法。
解說
・ViewDidLoad 內,model 呼叫 getArticles 方法。
・getArticles 方法內,讓 delegate 執行協定的 articlesRetrieved 方法,以欲傳資料為參數。
・即可透過 articlesRetrieved 方法把資料送給 ViewController 的 articles 屬性。
補完 getArticles 函式,從 API 抓資料
View
加入 tableView / ArticleCell,DetailViewController / webView
・Cell 的 Identifier 設為 “ArticleCell”。…cellForRowAt… 方法內會用到。
・ArticleCell 和 Table View 的 Row Height 都設為 100。
・ArticleCell 的 Selection 選 None,取消點選反白效果。
Cell 的 UI 小技巧:
・label 右邊不設 constraint,把 label 寬度設為 ArticleCell 寬度的 0.7 倍再減(label 左右欲留寬度)* 2。
・image view 左邊不設 constraint,把 image view 寬度設為 ArticleCell 寬度的 0.3 倍。
Controller
建立 tableView 架構
拉 tableView IBOutlet,設定 tableView 的 delegate 和 datasource 是 ViewController
ViewController 遵從 UITableViewDataSource, UITableViewDelegate
return articles.count
讓 tableView 知道文章總數。
articlesRetrieved 協定方法最後加入
tableView.reloadData()
,如此一來,獲得文章陣列後就會更新 tableView。
目前的畫面 demo,可以看到 cell 筆數(articles.count)是正確的。
客製 Cell 內容
這次的 cell 要抓圖,我們讓 cell 呼叫自己的 method,把 article 傳給 cell 的 property。
新建 ArticleCell 類別(繼承 UITableViewCell)
拉 label, image view 的 IBOutlet 到 ArticleCell
1. 用 as! 強制轉型成 ArticleCell
2. cell 呼叫 displayArticle 方法,把位於 indexPath.row 位置的 article 傳給 ArticleCell 的 property
目前畫面 Demo:
在 displayArticle 內開頭先補上 Reset cell 的動作
既然顯示完文字了,緊接著下載圖片、顯示圖片
注意:顯示圖片前,要確認 self.articleToDisplay!.urlToImage 與 urlString 仍相同(可能因 cell recycle 而不同)
用字典實現簡單 Cache 功能:不用每次都重新下載圖片
import Foundationclass CacheManager {
static var imageDictionary = [String : Data]()
static func saveData(_ url: String, _ imageData: Data) {
// 儲存圖片資料和其 urlString
imageDictionary[url] = imageData
}
static func retrievedData(_ url: String) -> Data? {
// return 圖片資料或 nil
return imageDictionary[url]
}
}
透過 let urlString = articleToDisplay.urlToImage!
得到圖片 urlString 後,以該 urlString 從 CacheManager 抓取非 nil 圖片,顯示並 return:
確定下載到新的圖片資料後,把 urlString 和 data 放進 CacheManager 存起來:
點選 Cell 顯示網頁:Segue / performSegue 兩種轉場,搭配 prepare(for:sender:) 傳資料
轉場方法一:
從 “cell” 拉 Segue 到 DetailViewController。
轉場方法二:
1. 從 “ViewController” 拉 Segue 到 DetailViewController。
2. 給 Segue 一個 “goToDetail” 的 Identifier。
3. 加入 performSegue(withIdentifier:sender:)。(於 didSelectRowAt 方法中)
用 prepare function 傳資料
動畫
文章標籤、圖片淡入動畫
func displayArticle(_ article: Article)
中。
・Reset cell 後,設定透明度為 0:
・顯示標籤文字後,透明度漸變成 1:
・從 Cache 載入圖片後,透明度漸變成 1:
・載入新圖片後,透明度漸變成 1:
載入時的 Spinner 旋轉動畫
1. Spinner 放在 Detail ViewController 中間,拉 IBOutlet
2. WebKit 的 WKNavigationDelegate 協定、…didFinish…方法
功用是讓 Web View 通知其 navigationDelegate(這邊是 DetailViewController)已經結束 loading 了。
加入///註解部分,實現載入時的 spinner 旋轉動畫。
重新抓資料的 spinner 動畫
設定 tableView 的 property 為 UIRefreshControl 之物件
拖曳觸發函式
文章陣列屬性設定之後,結束 refreshing
參考資料
作業來源
存參
裡面提到:
WKNavigationDelegate ✓(這篇使用的)
UITextViewDelegate
AVSpeechSynthesizerDelegate
URLSessionDataDelegate
MKMapViewDelegate
改用第三方套件 SDWebImage 下載圖片
主要先 import SDWebImage, imageView 再呼叫 sd_setImage 相關方法。