#14 — 串接 Dcard API,模仿Dcard App

陳秉軒
10 min readMay 2, 2022

--

呈現畫面:

上述畫面分為兩個部分,第一頁單純單純利用Table View Controller設計畫面,再用Dcard提供的最新分類文章的 JSON 資料的網址去解析,最後再利用UIRefreshControl()去更新顯示最新資料。

  • 不分類最新文章的 JSON 資料
https://dcard.tw/_api/posts

自定義的資料型別

將解析JSON的Decoder寫在自定義的Function裡

var posts = [Post]()@objc func fetchApi(){
let urlStr = "https://dcard.tw/_api/posts"
if let url = URL(string: urlStr){
URLSession.shared.dataTask(with: url) { data, response, error in
let decoder = JSONDecoder()
if let data = data {
do{
let posts = try decoder.decode([Post].self, from: data)
self.posts = posts
DispatchQueue.main.async {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}catch{
print(error)
}
}
}.resume()
}
}

將設計好的畫面元件拉IBOutlet至自定義的TableViewCell中

實作UITableViewDataSource

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DcardTableViewCell", for: indexPath) as? DcardTableViewCell else{return DcardTableViewCell()}
let index = posts[indexPath.row]

if index.gender == "F"{
cell.genderImage.image = UIImage(named: "woman")
}else{
cell.genderImage.image = UIImage(named: "man")
}

if index.school == nil{
cell.schoolLabel.text = "匿名"
}else{
cell.schoolLabel.text = index.school
}

if index.likeCount != 0{
cell.likeCountImage.tintColor = UIColor.systemRed
}else{
cell.likeCountImage.tintColor = UIColor.systemGray
}

if index.mediaMeta.count != 0{
let urlStr = index.mediaMeta[0].url
//利用套件抓圖
cell.postImage.kf.setImage(with: urlStr)

cell.postImage.isHidden = false
}else{
cell.postImage.isHidden = true
}
cell.forumNameLabel.text = index.forumName
cell.titleLabel.text = index.title
cell.excerptLabel.text = index.excerpt
cell.likeCountLabel.text = "\(index.likeCount)"
cell.commentCountLabel.text = "\(index.commentCount)"

return cell
}

最後一樣將UIRefreshControl()更新顯示資料寫在Function裡

//上拉更新tableView
func refreshView(){
refreshControl = UIRefreshControl()
let attributes = [NSAttributedString.Key.foregroundColor:UIColor.white] //文字顏色
refreshControl?.attributedTitle = NSAttributedString(string: "更新中", attributes: attributes) //顯示文字
refreshControl?.tintColor = .white //元件顏色
refreshControl?.backgroundColor = .gray //下拉背景顏色
refreshControl?.addTarget(self, action: #selector(fetchApi), for: UIControl.Event.valueChanged) //下拉後執行的動作
tableView.refreshControl = refreshControl
//下面這種方式加入也可以
//tableView.addSubview(refreshControl!)
}

(要記得加上refreshControl?.endRefreshing() 才會停止轉動)

由於第二頁顯示的資料是第一頁選擇的cell,所以這裡我利用func prepare搭配tableView.indexPathForSelectedRow?.row將資料傳到第二頁

第二頁的做法如同第一頁,差別在於詳細內文、時間需要另外解析以及額外增加了文章的留言如下:

  • 文章詳細內容
https://dcard.tw/_api/posts/文章id
  • 文章的留言
http://dcard.tw/_api/posts/文章id/comments

自定義的資料型別

比較不一樣的是文章的詳細內容是以 table view 的 section header 呈現

再利用viewForHeaderInSection將設計的View加到Table View中

再來就是最為麻煩的,製作包含圖片的文章內容,這裡我是參考其他學長姐的做法,利用func split將文章內容以”\n”換行就將字串拆開,再利用func forEach找到包含https帶有網址的圖片,搭配NSMutableAttributedString()就可以製作出來。

let contentSplit = self.postDetail?.content.split(separator: "\n").map(String.init)
let mutableAttributedString = NSMutableAttributedString()
contentSplit?.forEach({ row in
if row.contains("https") {
mutableAttributedString.append(imageSource: row, labelText: self.detailView.contentLabel)
} else {
mutableAttributedString.append(string: row)
}
})

由於圖片大小不一定,需自行用程式產生且定義大小

(如果拆開的字串有包含https就會利用其字串網址產生自定義比例圖片並加入NSMutableAttributedString()中)

而解析時間的部分可以直接參考下方連結,我就不贅述了。

參考資料:

GitHub網址:

--

--