新浦京81707con > 注册购买 > 我只是想截个屏,我只是想要截个屏

原标题:我只是想截个屏,我只是想要截个屏

浏览次数:78 时间:2020-04-05

只怕使用红米的客户, 大家都知道依照Home键 电源键就足以截屏了。 截屏对于成品经营、技术员、设计员都很首要。那么在iOS中用代码截屏也是再常用然则的效应了~ 那么在iOS研究开发中, 怎么着才干一蹴而就的截屏呢? 小编在此周用了2天时间去写了一个斯威夫特版本的截图开源库 - SwViewCapture。

上两日写了一篇《自身只是想要截个屏》的博文, 来说述了在挥洒SwViewCapture中相见的有的不利和解决方案。在《小编只是想要截个屏》中并不曾找到针对WKWebView的全内容截图的相对圆满的缓和方案, 只是用一种滚动的武力的章程去截图然后组装不常消弭。

胚胎我有三个细微的主见, 怎样去截取整个网页照旧整个滚动视图的从头到尾的经过吧? 造四个帮助该意义的开源库会不会受应接啊? 基于CocoaChina App的享用思路以至作者自个儿的一点小主见, 我决定写三个有利Swift开采者使用的截屏库, 补助截取页面载体全体内容的库。

正文首要在上篇小说中做一些简便的增加补充, 来陈说SwViewCapture中是怎么更好的消除WKWebView的截屏难题, 还会有怎么找到这种取巧的缓解方案的~

  • 该主张的来源于来自于@子循的贰个开源App - CocoaChina , 在该App中, 顾客能够享受客商正在浏览的页面内容, 也等于WebView的内容。

PS: 即使大家想平素看贯彻原理, 请跳过一次失利尝试章节~

大家兴许好奇, 就那样叁个截屏, 须要写2天么? 一从头小编的主见相当粗略: 无非就写二个截屏库。作者真正实际写起来的时候, 才发掘原本光光一个截屏也会有如此多的坎等着本身去踩。小编代码截屏中遇见的困难在这里间梳理了一晃, 幸免我们也再度采坑。

五遍失利尝试

阅读过《自身只是想要截个屏》的童鞋们可能都知道, 对于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

最鸡血的来了, 跟踪了多少个时辰, 作者废弃了... 对的, 作者直到最终都并未有从源码中找到解决方案~ =。=

在刚最早写SwViewCapture的时候, 作者想达成的简便点, 先完成幼功的截屏成效, 能够将轻巧的View直接转化成一张UIImageView。

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. 读取画布中的图像并回到

大约思路如图:

图片 1

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>

本条效能测度大多数iOS研究开发者都关涉到过, 在iOS7早先就一向用CoreGraphic的秘籍去Draw出相应的图。关键代码如下:

总结

因为WKWebView只可以渲染显示器范围大小左右的视图范围, 因而小编就应用这几个点, 不断的去改换WKWebView的frame去截图, 然后组装成为一张内容截图。通过这种办法得以奇妙的避让过因为滚动视图爆发的片段页面成分重复的难题。

实在WKWebView现在在iOS开荒应用中并从未UIWebView遍布, 做截图相关成效的开辟者也大概会预先使用UIWebView最为搭载容器, 不过多多少少本篇随笔理所应当还是会赞助到有的用到WKWebView的前驱的~

另: 本文提供的消除方案或许只是过多减轻方案的此中一种, 並且极度的耗费时间也消耗内部存款和储蓄器, 希望我们能够同步研究能还是不能够有更优的缓慢解决方案~ 希望多多交换~ 如果有越来越好的方案, 跪求Pull Request抑或提交issue到SwViewCapture。

PS: 鉴于个人水平有限, 有错误之处, 请我们即刻提议~ 谢谢~

UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreenself.layer.renderInContextlet capturedImage = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()

参谋文献

  1. Apple Open Source - WebKit2
  2. iOS Developer library - WKWebView
  3. 自己只是想要截个屏

上海体育场合代码的根本代码renderInContextCALayer的方法, CALayer是CoreGraphic底层的图层, 组成UIView。UIGraphic等连锁操作Context是Quartz 2D框架中的API, 而Quartz 2D是CoreGraphic的内部七个结缘。

无非4行代码基本已经能够满足一大半的急需了~ 大多数是因为小编在脚下除了在WKWebView上此截图方法截图失利, 目前还应该有在其它的View上截图退步, 有待继续检查。那么WKWebView又有哪些难点啊?

我在写SwViewCapture的时候, 尝试去截取WKWebView的图。截图的结果回到给笔者的就仅仅只是一张背景图, 鲜明截图失败。通过搜寻StackOverflow和谷歌(Google卡塔尔(قطر‎, 笔者开掘WKWebView并无法大约的接受layer.renderInContext的主意去绘制图形。

假设直接调用layer.renderInContext急需取得相应的Context, 不过在WKWebView中举办UIGraphicsGetCurrentContext()的回到结果是nil (具体的准则目前还不明, 待我领悟之后会补充卡塔尔(قطر‎

StackOverflow提供了一种减轻思路是应用UIViewdrawViewHierarchyInRect办法去截取显示器视图。

因而一直调用WKWebView的drawViewHierarchyInRect方法(afterScreenUpdates参数必得为true卡塔尔, 能够成功的截取WKWebView的显示屏内容。

在搜寻资料设法消除WKWebView截屏难点的时候, 无意中搜寻到Chrome开源项目chromium的截屏源码SnapshotManager。我在翻阅源码的时候开采自身漏思考了一个大场景:

  • 底蕴的UIView包罗WKWebView场景下的截屏

参照他事他说加以考察SnapshotManager中的设计方案, 定义一个递归函数去判定是或不是带有了WKWebView:

public func swContainsWKWebView() -> Bool { if self.isKindOfClass(WKWebView) { return true } for subView in self.subviews { if (subView.swContainsWKWebView { return true } } return false}

末段普通截屏的方案为:

  • view中自由叁个子View包罗WKWebView, 则接纳drawViewHierarchyInRect的章程去截取视图
  • view中任性贰个子View都不含有WKWebView, 则选拔renderInContext的方法去截图

世家或者好奇为何不全部使用drawViewHierarchyInRect的秘诀好了, 还画蛇添足来个剖断, 引用chromium源码SnapshotManager中的注释来讲明为什么

-drawViewHierarchyInRect:afterScreenUpdates:YES is buggy as of iOS 8.3.

Using it afterScreenUpdates:YES creates unexpected GPU glitches, screen

redraws during animations, broken pinch to dismiss on tablet, etc. For now

only using this with WKWebView, which depends on -drawViewHierarchyInRect.

TODO(justincohen): Remove this (and always use drawViewHierarchyInRect)

once the iOS 8 bugs have been fixed.

PS: 写iOS的近几来来, 多多少少已经遇到不菲的iOS系统原生BUG, 也是醉了

终结到作者写本篇博客的时候, chromium项目master上仍然还留存该段注释。

作者将上述根底截屏效率封装了一晃, 在SwViewCapture库中, 仅仅必要一行代码就可以兑现截图功效:

view.swCapture { (capturedImage) -> Void in // Do something with capturedImage}

平常说来截屏完成了, 那么就从头想怎么去贯彻全内容的截屏。开采八个复杂的功效, 第一步就是先把成效轻便化完结, 那么小编一从头就拿UIWebView作为实验对象去完成内容的截取功效。那么难点来了, 怎么落到实处呢?

经过打字与印刷UIWebView内部的UIScrollView的尺码, 可以起头询问到UIWebView的内容本质上其实是承袭在中间的UIScrollView中的。那么二个简洁明了的耗内部存款和储蓄器的兑现思路就冒出来了:

将UIWebView的长宽改革为UIScrollView的内容尺寸大小, 然后将UIWebView用日常截图的办法截收取来。

借助上述那些轻巧的主张, 作者立马想到是还是不是足以直白对UIWebView内部的UIScrollView举行长度宽度校正操作并截屏, 假使可行的话, 则能够一直引申使用在UITableView甚至底工的UIScrollView上了。

焦点实今世码如下:

UIGraphicsBeginImageContext(scrollView.contentSize)let savedContentOffset = scrollView.contentOffsetlet savedFrame = scrollView.framescrollView.contentOffset = CGPointZeroscrollView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height)scrollView.layer.renderInContext(UIGraphicsGetCurrentContextlet image = UIGraphicsGetImageFromCurrentImageContext();scrollView.contentOffset = savedContentOffset;scrollView.frame = savedFrame;UIGraphicsEndImageContext()

本条情形下, UIScrollView能够被经常的截图, 那么引申改善应用在UIWebView上相应不是什么样难点吧?

PS: UIWebView有个get方法能够获取相应的UIScrollView

在主导的场所下, 该办法都足以寻常的截取UIScrollView, 不过在iOS8条件下会现出尾巴部分可视区域为铁青的极其BUG。(那些BUG大概和UIScrollView在被UINavigationController托管下的VC下发出的State of Qatar

通过一阵招来Stackoverflow和CocoaChina 的源码提示, 有贰个比较适度的化解措施:

add scrollview to another temp view and render it.

把UIScrollView单独拎出来, 放在任何有时的UIView里单独渲染。通过该措施果然能够将iOS8的渲染难题给屏蔽掉。

图片 2改善方案合成示意

将在此之前所陈述的截图应用到骨子里情形中, 作者发掘成些网页的因素是异步加载的, 即独有页面滚动到对应的有个别, 才会奉行渲染加载(小编的博客首页核心正是这种境况卡塔尔国。其余, UIScrollView和UITableView中也不缺少这种情景。

对此这种异步的诀要, 未有一种完美的缓慢解决方案, 小编只好祛除一种暴力的章程解决一部分案例:

  • 截屏前滚动ScrollView至底部, 再滚动回首部

因而Stackovetflow - Getting a Screenshot of a UIScrollView including offscreen parts中的方式修复了iOS8的截图多个黑色区域的诡异BUG后, 在其实截图中发觉了其余一个题目:

  • 将显示屏中正在显示的View拎出来放置在其他View中渲染, 渲染完结后再恢复, 恐怕会产出一闪而过的事态。

上述现象发生的开始和结果大家估量都精通: 因为View离开当前视图的时候, 触发了分界面渲染, 展现分界面中的视图已经不在突显分界面中, 自然就改成了背景象。

既是要做三个截屏库, 那么那些难题也是亟需减轻的, 总不能让人家调用截屏API的时候闪一下吗?

本文由新浦京81707con发布于注册购买,转载请注明出处:我只是想截个屏,我只是想要截个屏

关键词: 新浦京81707con 我只是 截个屏 iOS 优秀实践 首页投

上一篇:pod install vs. pod update

下一篇:没有了