【iOS】仿知乎日報,RxSwift-Part2-詳情頁的搭建
阿新 • • 發佈:2019-01-09
前言
在上一篇,我們搭建了首頁。而這篇,我們將開始搭建話題詳情頁。
分析
還是先來看下演示gif
{
"body": "<div class=\"main-wrap content-wrap\">\n<div class=\"headline\">\n\n<div class=\"img-place-holder\"></div>\n\n\n\n</div>\n\n<div class=\"content-inner\">\n\n\n\n\n<div class=\"question\">\n <h2 class=\"question-title\">機會成本是否有「時效性」?</h2>\n\n<div class=\"answer\">\n\n<div class=\"meta\">\n<img class=\"avatar\" src=\"http://pic4.zhimg.com/b1ccdc223_is.jpg\">\n<span class=\"author\">Kallas,</span><span class=\"bio\">Penn State Econ Ph.D. Student</span>\n </div>\n\n<div class=\"content\">\n<p>是的,機會成本是一個非常簡化的概念,題主敏銳的發現了這個問題。機會成本特別適合<strong>靜態、有限選擇、風險因素不重要</strong>時候的分析,但是當存在風險、選擇無限、動態問題的時候,機會成本這一概念就顯得過於簡單了。</p>\r\n<p>機會成本遺漏了<strong>風險結構</strong>,兩塊錢可以買一瓶水,也可以買彩票;可以買獎金 500 萬但是中獎率千萬分之一的大彩票,也可以買獎金 10 塊但是中間率高很多的小彩票。買大彩票還是小彩票不光取決於機會成本(以期望收益計算),也取決於個人的風險偏好。技術性地講,機會成本特別適用一階隨機佔優時候的比較,但是當風險是主要因素的時候就不太適用。</p>\r \n<p>而且兩塊錢買一瓶水 vs 兩塊錢買張彩票,和 200 塊錢買 100 瓶水 vs 100 張彩票又不一樣。我可以花其中的 180 塊錢去買水,剩下的錢買彩票,這樣的選擇有非常多種。這樣的選擇有非常多。我們當然依然可以列出所有的選項,然後從中挑選一個最偏好的方案。但是更方便的辦法可能是用<strong>邊際效用</strong>來描述這個新的選擇問題。</p>\r\n<p>題主所說的時效性,我舉另一個例子。比如題主在考前糾結是看電影還是複習。看電影要花 30 塊錢買票,還要搭上兩小時的時間,這時候的機會成本就是 30 塊錢 + 兩小時的複習量(同時也可以思考複習的機會成本是啥)。但是如果看了一半發現電影很無聊,考慮要不要回去複習,那麼這時候的機會成本就是一小時的複習量。而回去複習的機會成本就是剩下一小時的愉悅 + 可能的彩蛋。(看,又有“可能性”的問題)。可以看到機會成本是隨著時間不斷變化的。如果題主在看電影的每時每刻都在做這樣的比較,那麼用機會成本來刻畫選擇就會變得非常複雜,一個更好的選擇是做成動態規劃問題。</p>\r\n<p>曼昆一開始就介紹機會成本的概念是因為它非常簡單、符合直覺,並且生活中非常多的問題確實也是可以用機會成本的概念思考的。我上面說的有些名詞不理解並無所謂,後來慢慢都會知道的。題主剛接觸經濟學就能有這樣反思概念的意識非常好,經濟學就是這樣不斷在概念和反思概念中發展起來的。</p>\n</div>\n</div>\n\n\n<div class=\"view-more\"><a href=\"http://www.zhihu.com/question/66457929\">檢視知乎討論<span class=\"js-question-holder\"></span></a></div>\n\n</div>\n\n\n</div>\n</div>",
"image_source": "Public Domain",
"title": "考前糾結是看電影還是複習?這你可牽扯到經濟學問題了",
"image": "https://pic2.zhimg.com/v2-003879862c9104f540b05001938983fd.jpg",
"share_url": "http://daily.zhihu.com/story/9649565",
"js": [],
"ga_prefix": "101309",
"images": [
"https://pic3.zhimg.com/v2-158fb865f361b059aedfcc65e25bd06a.jpg"
],
"type": 0,
"id": 9649565,
"css": [
"http://news-at.zhihu.com/css/news_qa.auto.css?v=4b3e3"
]
}
不難發現,返回的資料是返回HTML的Body內容,而CSS樣式則讀取css欄位。那麼主題內容需要我們“拼出”一個HTML格式的字串,然後用webView進行載入。而頭部的圖片(image),文字(title),圖片來源(image_source)需要我們自己佈局及載入。
要點解析
1、自定義WKWebView
按以上的分析,我們需要自定義一個WKWebView,頭部需要插入圖片,標題Label等元素,還要在該webView的頭部和底部新增上下載入的提示語。由於我們在WKWebView的底部新增提示語“載入下一篇”,所以我們需要獲得該webview的contentSize。
由於WKWebView不能通過scrollView.contentSize直接獲取內容告訴,所以在webView載入完畢時,呼叫了js語句,獲取其內容高度:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.body.scrollHeight") { (result, error) in
if let height = result as? CGFloat {
self.nextLabel.frame.origin.y = height + 50
}
}
}
2、拼接HTML
上面也說了,介面返回的只有HTML的Body內容,以及CSS連線,所以我們需要額外新增等元素,使之合乎規範。
具體拼接方式如下:
/// 載入HTML網頁
fileprivate func loadHTML(model: MPStoryDetailModel) {
guard let css = model.css, let body = model.body else {
return
}
var html = "<html>"
html += "<head>"
css.forEach { html += "<link rel=\"stylesheet\" href=\($0)>" }
html += "<style>img{max-width:320px !important;}</style>"
html += "<body>"
html += body
html += "</body>"
html += "</head>"
html += "</html>"
self.loadHTMLString(html, baseURL: nil)
}
3、內容自適應
WKWebView的內容自適應比UIWebView稍微麻煩一點,我是在WKWebView建立時,設定了js語句
init() {
// 設定內容自適應
let js = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
let wkUserScript = WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let config = WKWebViewConfiguration()
let wkUControl = WKUserContentController()
wkUControl.addUserScript(wkUserScript)
config.userContentController = wkUControl
super.init(frame: CGRect.zero, configuration: config)
}
4、上下載入文章
原理:載入上一篇或下一篇文章只需要監聽scrollView的滾動,判斷載入上一篇還是下一篇,那麼,我們就要在拖拽結束的時候進行監聽。而動畫效果,需要兩個輔助的動畫View實現,一個是在頂部的TopAnimatedView,一個是在底部的BottomAnimatedView。佈局如下圖:
拿載入上一篇的效果進行說明,其動畫效果是,topAnimatedView向下移動,動畫結束後還原,再重新載入webView即可。
因此,轉化為對應的程式碼就是
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView.contentOffset.y <= -75 && index != 0{
webView.startLoading()
UIView.animate(withDuration: 0.3, animations: {
self.topAnimatedView.transform = CGAffineTransform.init(translationX: 0, y: (screenH + 20))
}, completion: { (state) in
if state {
self.topAnimatedView.transform = CGAffineTransform.identity
// 載入上一篇文章
self.didSetIndex(self.index - 1)
self.loadData()
}
})
}
}