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

前言

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

分析

还是先来看下演示gif

详情页.gif

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

{
  "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></HTML>等元素,使之合乎规范。

具体拼接方式如下:

/// 加载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()
                }
            })
        }
}

总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,980评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,081评论 4 62
  • 俗语有云:一命二运三风水,四积阴德五读书。 运气在人生成功的因素中位列第二,重要性不言而喻。 不可否认,运气确实是...
    吴廿九阅读 418评论 0 0
  • 玩笑限于即兴 留下误会就成了谎言。 尽可能预想所有残酷的可能性 因为现实永远让你无法预警 而且又吝于给人慈悲。 真...
    情绪忄患者阅读 266评论 0 3