1. 程式人生 > >【iOS】仿知乎日報,RxSwift-Part2-詳情頁的搭建

【iOS】仿知乎日報,RxSwift-Part2-詳情頁的搭建

前言

在上一篇,我們搭建了首頁。而這篇,我們將開始搭建話題詳情頁。

分析

還是先來看下演示gif

詳情頁.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。佈局如下圖:

上下載入文章結構分析@2x.png

拿載入上一篇的效果進行說明,其動畫效果是,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()
                }
            })
        }
}

總結