【iOS】仿知乎日报,RxSwift-Part2-详情页的搭建

前言

在上一篇,我们搭建了首页。而这篇,我们将开始搭建话题详情页。

分析

还是先来看下演示gif

【iOS】仿知乎日报,RxSwift-Part2-详情页的搭建_第1张图片
详情页.gif

再结合话题详情的接口分析 http://news-at.zhihu.com/api/4/news/9649565。具体的json格式如下:

{
  "body": "
\n
\n\n
\n\n\n\n
\n\n
\n\n\n\n\n
\n

机会成本是否有「时效性」?

\n\n
\n\n
\n\nKallas,Penn State Econ Ph.D. Student\n
\n\n
\n

是的,机会成本是一个非常简化的概念,题主敏锐的发现了这个问题。机会成本特别适合静态、有限选择、风险因素不重要时候的分析,但是当存在风险、选择无限、动态问题的时候,机会成本这一概念就显得过于简单了。

\r\n

机会成本遗漏了风险结构,两块钱可以买一瓶水,也可以买彩票;可以买奖金 500 万但是中奖率千万分之一的大彩票,也可以买奖金 10 块但是中间率高很多的小彩票。买大彩票还是小彩票不光取决于机会成本(以期望收益计算),也取决于个人的风险偏好。技术性地讲,机会成本特别适用一阶随机占优时候的比较,但是当风险是主要因素的时候就不太适用。

\r\n

而且两块钱买一瓶水 vs 两块钱买张彩票,和 200 块钱买 100 瓶水 vs 100 张彩票又不一样。我可以花其中的 180 块钱去买水,剩下的钱买彩票,这样的选择有非常多种。这样的选择有非常多。我们当然依然可以列出所有的选项,然后从中挑选一个最偏好的方案。但是更方便的办法可能是用边际效用来描述这个新的选择问题。

\r\n

题主所说的时效性,我举另一个例子。比如题主在考前纠结是看电影还是复习。看电影要花 30 块钱买票,还要搭上两小时的时间,这时候的机会成本就是 30 块钱 + 两小时的复习量(同时也可以思考复习的机会成本是啥)。但是如果看了一半发现电影很无聊,考虑要不要回去复习,那么这时候的机会成本就是一小时的复习量。而回去复习的机会成本就是剩下一小时的愉悦 + 可能的彩蛋。(看,又有“可能性”的问题)。可以看到机会成本是随着时间不断变化的。如果题主在看电影的每时每刻都在做这样的比较,那么用机会成本来刻画选择就会变得非常复杂,一个更好的选择是做成动态规划问题。

\r\n

曼昆一开始就介绍机会成本的概念是因为它非常简单、符合直觉,并且生活中非常多的问题确实也是可以用机会成本的概念思考的。我上面说的有些名词不理解并无所谓,后来慢慢都会知道的。题主刚接触经济学就能有这样反思概念的意识非常好,经济学就是这样不断在概念和反思概念中发展起来的。

\n
\n
\n\n\n\n\n
\n\n\n
\n
", "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 += ""
        css.forEach { html += "" }
        html += ""
        html += ""
        html += body
        html += ""
        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。布局如下图:

【iOS】仿知乎日报,RxSwift-Part2-详情页的搭建_第2张图片
上下加载文章结构分析@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()
                }
            })
        }
}

总结

以上就是整个话题详情的要点了,有不明白的可以留言~
源码地址:https://github.com/maple1994/RxZhiHuDaily

你可能感兴趣的:(【iOS】仿知乎日报,RxSwift-Part2-详情页的搭建)