[iOS 开发] 图文快照分享的实现

前言:『图文快照』是指将一组图片和文字,进行排版组合,最终生成一张长图片。长图片(长微博)最早是在微博中出现的,后来在其他一些应用中(简书、小红书、百度地图)也看到了分享长图片的功能,另外还有些专门制作长图片的工具,比如锤子便签、Zine。

目录

0.背景介绍
1. 需求分析
2. 实现思路
3. 实现过程
4. 单元测试
5. 几个问题
6. 参考资料

0.背景介绍

任何有实际意义的创新都不是凭空想象出来的(这不废话么~)。长图片在微博中出现的起因是当时微博中文字长度不得超过140字,所以当文字比较多时就可以转为图片来发布。 微博作为碎片化时代的代表,本身带有“快餐化”、“碎片化”的标签,长图片的出现,在一定程度上与短微博形成了互补,现在我们经常可以看到一条这样的微博:一段短文描述 + 链接 + 一张内容详尽的长图片。而且,以图片代替文字在某种程度上可以躲过“河蟹”。

微博长图.png

所以,长图片给我们带来的好处显而易见——快好省,可以让用户用最少的代价,更快地,看到更多的内容。

1. 需求分析

需求:点击分享菜单中的“朋友圈快照”按钮,将要分享的社区图文帖转成长图片(图片中的内容从上到下,依次为品牌 logo、用户信息、文字、图片、点赞、二维码信息),然后分享到朋友圈。
以前分享到朋友圈的是这条帖子的链接,用户看到的只是一个标题和 icon,需要点击跳转再加载网页才能看到内容,而现在采用长图的方式,可以让用户更直观、更快地看到分享的内容,更利于传播。

技术分析:这个需求的核心是图片,所以我们在生成图片时需要重点考虑的有两点:

  • 尽量缩短用户在分享长图片时的等待时长
  • 尽量保证图片质量和体积大小的平衡,也就是既要好看又不能太大
生成并分享图文快照的过程

2. 实现思路

首先我们要考虑的是,如何把数据变成图片?
我们平时在开发过程中都是通过创建 view 来实现视觉效果的,所以我们可以从 UIView 入手——UIView->UIImage。我们可以通过 UILayer-renderInContext: 方法将 UIView 在 context 上进行图像内容渲染,然后再通过 UIGraphicsGetImageFromCurrentImageContext() 获取图片。

讨论:当然你也许会说,为什么不用 drawViewHierarchyInRect:afterScreenUpdates: 方法来生成图片,它比 -renderInContext: 方法效率要高啊,实际上drawViewHierarchyInRect:afterScreenUpdates: 不支持不可见的 view,也就是说,对于没有显示在屏幕上的 view,调用这个方法不起作用。

核心部分弄清楚之后,其他的就简单了,大概梳理一下,生成图文快照的主要流程如下图所示:

实现流程

3. 实现过程

具体的代码见 ShannonChenCHN/SCSnapshotManager,这里我们只讨论实现过程。

3.1 需要考虑的问题
  • 功能拆分
    • 二维码生成
    • 图片下载
    • UIView -> UIImage
  • 解耦:要根据不同数据生成不同样式的快照,怎么实现?
3.2 一图胜千言
SCSnapshotManager 的实现原理

结合上面的示例图,一张快照的生成需要经历下面几个步骤:

  • SCSnapshotManager 拿到传进来的 model
  • SCSnapshotManager 根据 model 的类型创建 model 对应的 provider 对象
  • provider 对象会通过 SCSnapshotModelTransformer 来转换生成快照的 content(其实也是一个 model,只不过是为了统一)
  • SCSnapshotManager 从 provider 对象那里获取到要分享的 URL,并调用 SCQRCodeGenerator 生成二维码图片,拿到二维码图片后,再回传给 content
  • provider 对象把下载的图片的 URL 整理好,交给SCSnapshotManagerSCSnapshotManager再通过 SCSnapshotImageDownloader 进行下载,下载完成后再将图片回传给 provider 对象
  • provider 对象拿到下载好的图片后,创建相应的 view
  • SCSnapshotManager 把 provider 对象提供的 view 交给 SCSnapshotGenerator 转成一个压缩过的 UIImage 对象
  • SCSnapshotManager 拿到最终的 UIImage 对象,就可以干自己想干的事了——分享或者保存到相册
3.3 Architecture
类名 功能
SCSnapshotManager 负责主线逻辑,串联各子逻辑(从生成二维码到分享快照),这里面的逻辑一般不怎么变动的
SCSnapshotProviderProtocol 所有 Provider 类需要遵循的协议,不论是要生成什么样的快照,主流程是不会有什么变化的,变化的是 Provider,不同的需求(数据、逻辑、展示)对应不同的 Provider,所以在 Example 中,商户快照对应有一个 SCSnapshotMerchantProvider,图文帖快照对应也有一个 SCSnapshotPostProvider,每个具体的 provider 只需要遵守 <SCSnapshotProvider> 协议,并实现其中的方法即可:① 转换 Model,② 拼装图片下载 URL,以提供给 SCSnapshotImageDownloader 下载,③ 拿下载好的图片来创建 view,④ 其他强业务逻辑,比如埋点
SCQRCodeGenerator 二维码生成器,根据一个字符串生成一张二维码图片
SCSnapshotImageDownloader 图片下载器,因为要下载的是多张图片,而且还要考虑到分组展示的情况,所以这里我们将要下载的图片 URLs 包装成二维数组传入,下载完成后,得到一个装有图片的二维数组
SCSnapshotGenerator 将 view 转成 image

4. 单元测试

4.1 单元测试的目的
  • 模拟大多数的真实情况,更方便地测试各种边界条件和异常情况
  • 保证在加入新功能或修改旧功能时,不影响原有功能
  • 检验程序的设计合理性,是否遵循功能单一原则,是否低耦合
4.2 怎么测
  • Mock 数据,测试基本流程
  • Mock 数据,测试单个模块的功能
  • Mock 数据,测试各种极端情况,比如 image 为空

具体测试内容见 ExampleProject

5. 几个问题

  • 图片压缩策略:是分别把各单张图片压缩好,还是最后对整张长图进行压缩呢?

从评测结果中的内存峰值和压缩时间来看,显然后者更好。

  • 图片压缩方式
    • 采用 JPG 呢,还是 PNG?如果是 JPG 的话,压缩比为多少最合适?是不是想压到多大就能压到多大?压了之后清晰度怎么保障?

    采用的是 JPG 的格式,因为 JPG 的格式可以设置压缩比。综合压缩后的体积和展示效果来看,压缩到压缩比为 0.8 就差不多了。值得注意的是,并不是压缩比越小,压缩后的图片体积就越小,实际上,当压缩到一定程度后,就会到一个极限,不能再小了。

  • 微信说限制为 10M 是指什么格式下呢?

从微信中导出的图片格式来看,应该是 JPG 的。

  • 如何保证所有图片都能压缩到10M 以下?
    从图片尺寸大小入手,限制每单张图片的宽高比(我们这里取1/2),太长的图片就裁剪掉一部分。

  • 长图最大宽度和分辨率:为什么是 640px,而不是750px ?这里还用考虑 @2x 和 @3x 的问题吗?

综合考虑图片展示效果和图片体积大小,640px 的宽度已经足够了。因为不论是iPhone 5 还是 iPhone 6Plus 生成的图片,都是640 px 宽度的,而且不需要在屏幕上展示,所以用于生成快照的 view 的图片素材不管是 @2x,还是 @3x,都是一样的大小。

  • 单张图片的宽高比限制:宽高比小于 1/2 就只显示中间部分,有两种方案可选:① 每张图片下载完成后,如果太高就裁剪中间的部分;② 在 view 中渲染时,只“显示”中间的 1/2 部分。应该选哪一种呢?

从效率和性能上看,显然选第二种更好,因为绘制是比较耗性能的。 评测结果证明也是如此。

6.参考资料

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

推荐阅读更多精彩内容

  • 1 图片处理 1.1 编辑图片的几个方法 第一种 先用UIImage对象加载一张图片 然后转化成CGImageRe...
    Kevin_Junbaozi阅读 1,436评论 0 7
  • http://idevtoy.cngump.com/?page_id=550 由红甘果科技内部整理 原文见:htt...
    创造世界阅读 1,439评论 0 5
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 120,585评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 5,980评论 0 4