我只是想截个屏(续)

上两天写了一篇《我只是想要截个屏》的博文, 来描述了在书写SwViewCapture中遇到的一些坎坷和解决方案。在《我只是想要截个屏》中并没有找到针对WKWebView的全内容截图的相对完美的解决方案, 只是用一种滚动的暴力的方式去截图然后组装临时解决。

本文主要在上篇文章中做一些粗略的补充, 来描述SwViewCapture中是怎么更好的解决WKWebView的截屏问题, 还有怎么找到这种取巧的解决方案的~

PS: 如果大家想直接看实现原理, 请跳过几次失败尝试章节~

几次失败尝试

阅读过《我只是想要截个屏》的童鞋们可能都知道, 对于WKWebView的截图, 只能使用View的drawViewHierarchyInRect:afterScreenUpdates:方法去获取截图。在此前我曾尝试用如下几种方案去截图, 均以失败收尾。

  1. 将WKWebView的frame拉长和ContentSize的高度保持一致, 然后截图
  2. 将WKWebView的frame拉长和ContentSize的高度一致, 然后通过WKWebView的snapshotViewAfterScreenUpdates获取的view进行截图
  3. 对WKWebView内部的WKContentView直接截图
  4. 将WKScrollView对应的Screen进行拉伸, 然后对WKWebView进行等价拉伸, 再截图
  5. 使用私有API_snapshotRect:intoImageOfWidth:completionHandler

上述第一、二、三种方法是笔者自己脑洞尝试, 可是截图要么完全是空白, 要么就只能显示屏幕区域的图。

第四种和第五种是对WKWebView源码不了解的窥看后, 进行一种投机取巧尝试。

既然已经实在找不到解决方案了, 笔者就去官网下载源码, 希望能够找到突破口。WKWebView是开源的, 其源码放置在苹果官方开源网站http://opensource.apple.com中, 项目名字为WebKit2。

笔者以为下载到源码了, 至少能够找到一个突破口, 在打开工程项目后, 笔者就发现自己错了, 这个工程太庞大了。。。

WKWebView的组成笔者尚不熟悉, iOS的WKWebView底层更多的是WebKit的底层实现, 如果彻底从理解去阅读代码, 估计半个月甚至大半年都不一定读的完~ 有这个心思去阅读代码, 还不如先去阅读《WebKit技术内幕》这本书~

笔者自知不可能从阅读理解源码进行着手, 那就只能直奔要点: 关键字跟踪!笔者一开从WKWebView.mm文件进行突破, 去寻找遮盖关键字unobscured, 从这个关键字中发现遮盖区域和scrollView的window相关, 因此尝试第四种方法, 修改window的大小~ 失败的结果唯一能够告诉笔者的就是: 没有找到遮盖视图不渲染的根源!

笔者在第一次寻找关键字失败后尝试从snapshot这个关键字去突破, 结果发现了私有API_snapshotRect:intoImageOfWidth:completionHandler。这也是第五种方法的尝试来源。通过snapshot关键字其实还发现了隐藏在WKWebView.mm底下的_takeViewSnapshot方法, 可是该方法返回的对象是C++对象, 笔者就没有从Object-C层级对方法进行调用尝试。

结合snapshotunobscured两个关键字的搜索, 笔者在底层一串跟踪, 发现了WebPage、DrawingArea等一系列概念, 笔者偶然间在WebPage的初始化方法中发现有个WebPageCreationParameters参数作为构造WebPage的初始参数, 其中包含了如下几个参数

#if PLATFORM(IOS)
    WebCore::FloatSize screenSize;
    WebCore::FloatSize availableScreenSize;
    float textAutosizingWidth;
#endif

通过全局搜索availableScreenSize, 在WebPageProxyIOS.mm源码中发现, WebPage的屏幕尺寸是根据WKGetAvailableScreenSize()WKGetScreenSize()获取的, 核心代码如下:

FloatSize WebPageProxy::screenSize()
{
    return FloatSize(WKGetScreenSize());
}

FloatSize WebPageProxy::availableScreenSize()
{
    return FloatSize(WKGetAvailableScreenSize());
}

终于有些眉目了, 一全局搜索WKGetAvailableScreenSize崩溃了~ 在WebKit2开源中并没有这个方法的定义, 并且无法通过GoogleApple Developer搜索到相关信息... T_T

最鸡血的来了, 跟踪了几个小时, 笔者放弃了... 没错, 笔者直到最后都没有从源码中找到解决方案~ =。=

WKWebView截图方案

虽然没有通过源码找到解决方案, 但是通过改变Window的尝试让我的脑洞打开, 想到了另外一种和滚动截图很相似的暴力的解决方式。

PS: 滚动截图是笔者在我只想要截个屏中所描述的暴力解决截图的方式。实现方式就是滚动一页截取一页, 最后组装成一张长图。

笔者想: 既然WKWebView的渲染区域是屏幕范围固定的, 那我不滚动视图, 不断的往上推视图呢?

不断往上推视图的意思就是改变View的origin的y轴, 每截取一张图片后去上移View的高度(高度等价于该WKWebView在界面中的显示范围)和拉长WKWebView的总高度, 直到截取到了最后一张图并组装。

这个思路有个小小的问题, 就是笔者曾经尝试通过放大WKWebView本身去截图, 但是却截出一片空白的情况。透过这个问题可以假设, 我不断上移y轴并放大高度的最后一张情况和上述有问题的情况完全一致, 可以猜测这个方法是无法正确的截取WKWebView的图的。

笔者用了一种很巧妙的方法去躲避了这个问题, 就是去截取WKWebView的父视图, 因为无论WKWebView怎么改变, 通过WKWebView父视图截图是可以正确获取对应的界面的(笔者实验的)。

通过优化后大致的流程如下:

  1. 基于WKWebView的尺寸伪造一个UIView, 并拉长至ContentSize高度
  2. 将伪造的UIView作为WKWebView的父视图
  3. 放置一张大画布长度和WKWebView的ContentSize高度一致
  4. 对父视图进行普通截图并放置在大画布中
  5. 将WKWebView的高度上移一个父视图的高度
  6. 循环执行步骤3和步骤4直到总高度和WKWebView的ContentSize高度一致
  7. 读取画布中的图像并返回

大致思路如图:

WKWebView合成示意

思路核心代码如下:

let containerView  = UIView(frame: self.bounds)

self.removeFromSuperview()
containerView.addSubview(self)


let totalSize = self.scrollView.contentSize
let page      = floorf(Float( totalSize.height / containerView.bounds.height))

UIGraphicsBeginImageContextWithOptions(totalSize, false, UIScreen.mainScreen().scale)

for index in 0...Int(page) {
    // async for, action need package a method

    var splitFrame = CGRectMake(0, CGFloat(index) * containerView.frame.size.height, containerView.bounds.size.width, containerView.frame.size.height) 
    var myFrame = self.frame
        myFrame.origin.y = -(CGFloat(index) * containerView.frame.size.height)
        self.frame = myFrame 
    containerView.drawViewHierarchyInRect(splitFrame, afterScreenUpdates: true)
}

let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() 

通过这种方式果然可以截取完整的WKWebView, 并且不存在position: fixed;的标签重复的问题。

<font color='orange'>上述代码起示意作用, 实际循环部分需要等待延迟, 因为需要等待WKWebView在改变frame之后准备完毕执行下一次循环。</font>

总结

因为WKWebView只能渲染屏幕范围大小左右的视图范围, 因此笔者就利用这个点, 不断的去改变WKWebView的frame去截图, 然后组装成为一张内容截图。通过这种方式可以巧妙的躲避过因为滚动视图产生的部分页面元素重复的问题。

其实WKWebView现在在iOS开发应用中并没有UIWebView广泛, 做截图相关功能的开发者也可能会优先采用UIWebView最为搭载容器, 但是多多少少本篇文章应该还是会帮助到一些使用WKWebView的先驱者的~

另: 本文提供的解决方案可能只是众多解决方案的其中一种, 并且相当的耗时也消耗内存, 希望大家可以一起想想能否有更优的解决方案~ 希望多多交流~ 如果有更好的方案, 跪求Pull Request或者提交issueSwViewCapture

PS: 鉴于个人水平有限, 有错误之处, 请大家及时指出~ 谢谢~

参考文献

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

推荐阅读更多精彩内容

  • 想必使用iPhone的用户, 大家都知道按照Home键+电源键就可以截屏了。 截屏对于产品经理、工程师、设计师都比...
    Startry阅读 7,974评论 21 43
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,596评论 4 59
  • 转载:http://www.jianshu.com/p/aa64fd3dd621 想必使用iPhone的用户, 大...
    晓飞90阅读 1,224评论 0 1
  • 每天都有不顺心的事情。每天都不开心,是,家里总来关系好的大姨,这我知道好,也平时照应照应我妈。我高兴。但是能不能有...
    鸡米兮阅读 96评论 0 0
  • 制作一份分的清单,可以是出行前的 开车前的 度假前的。 工作前的,工作中的,完成检查的。 沟通前的,关键对话时的,...
    后知后觉的持续努力阅读 255评论 0 0