0%

这篇教程是系列教程的第一篇,主要是面向于没有怎么接触过iOS平台上图像编解码的人的,不会涉及到多媒体处理中的数字信号处理、图像编码的深入知识。这是系列最简单的一篇,之后会有关于第三方编解码,以及vImage的另两篇教程。

Image/IO

Image/IO是Apple提供的一套用于图片编码解码的系统库,对外是一层非常直观易用的C的接口。上层的UIKit,Core Image,还有Core Graphics中的CGImage处理,都是依赖Image/IO库的。因此,掌握Image/IO的基本编解码操作,对一些图像相关的数据处理是非常必要的。这篇教程就主要从简单的用法,说明Image/IO的用法,完整的文档,可以参考Apple Image/IO

解码

解码,指的是讲已经编码过的图像封装格式的数据,转换为可以进行渲染的图像数据。具体来说,iOS平台上就指的是将一个输入的二进制Data,转换为上层UI组件渲染所用的UIImage对象。

Image/IO的解码,支持了常见的图像格式,包括PNG(包括APNG)、JPEG、GIF、BMP、TIFF(具体的,可以通过CGImageSourceCopyTypeIdentifiers来打印出来,不同平台不完全一致)。在iOS 11之后另外支持了HEIC(即使用了HEVC编码的HEIF格式)。

阅读全文 »

这篇文章讲的是有关近期自己参与的几个开源项目的经历以及感受,不过巧合的是内容都和APNG和WebP这两种图像格式相关,阅读前建议先简单略读一下之前写的一篇文章:客户端上动态图格式对比和解决方案

SDWebImage

SDWebImage是iOS平台上非常著名的图片下载、缓存库,而今年发布的SDWebImage 4.0在架构、接口变动并带来性能优化的同时,还支持了Animated WebP,因此我就高兴地去实验了一下,本想着可以替代之前使用的YYImage。但是一测试就发现渲染不正常,追回去看源码,发现SDWebImage的实现可以说是Too naive,压根没有按照WebP规范实现,大部分Animated WebP动图渲染都挂了,完全不可用(连测试都过不了,更别说生产环境了)。演示Demo在此:AnimatedWebPDemo

总结出来的具体问题有以下几个:

  1. SD绘制每帧的canvas大小不正确,在代码中,直接取得当前帧frame的大小,而非整个canvas的大小。这就导致最后生成的所有帧图片的数组中,每帧的图像大小不一致。这样渲染就会出现Bug(把所有帧拉伸到最大的那个图像大小上)。
  2. SD的实现没有考虑过WebP Disposal Method,这个在很多动图中都会用到,因为能够重复利用前一帧的画布,来大幅减少最后生成动图的体积。常见的动图格式如GIF、APNG生成工具一般都采用这种Disposal,不然最终文件体积较大(但Google提供的WebP工具暂时没有自带这种优化的方式,一般使用第三方工具处理)。
  3. UIKit自带的UIImage.animatedImages是非常弱的,SD并没有提供额外的抽象,而是直接用的这个接口。这带来的最大的问题,是UIImage需要提供一个图片数组和总时长,但是会对数组中每个图片平均分配时长。这与Animated WebP的规范就是不同的,后者允许对每帧设置一个不同的持续时长。
  4. UIImageView直接设置image属性,是不支持设置循环次数的,会默认无限循环播放。而有些Animated WebP图片需要有循环次数。

既然知道这么多坑,想着SD毕竟是主流框架,就赶紧提了Issue,但是过了一周多,SD社区依然没有任何回应。于是尝试自己一个个解决。最后的成果也比较好,上述4个问题都得到了解决。

阅读全文 »

虽然Swift现在是开发iOS推荐入手的最佳语言,但是对于代码库而言,最大的一个问题是Swift ABI仍然没有定下(今年发布的的Swift 4.0,依然放弃ABI稳定性,而注重于Swift源代码3.x->4.0的兼容性)。所以这就意味着Swift 3.x编译的二进制库,在Swift 4.0将无法链接,只能重新代码编译。看来这又将是Objective-C这门古老的语法,能够作为一些framework首选开发语言的一年。

对于一个代码库来说,有时候我们为了隐藏一些实现的细节,或者内部处理流程,需要编译到二进制进行分发,并提供Public Header来供其他开发者调用。

因此,开发代码库的时候,需要明确哪些API是对外公开的,可以由其他开发者调用。那些是库内部之间互相调用的,不应该由外部使用者调用。而Objective-C不像C++提供了private关键字来限制直接访问成员变量和成员方法。因此,就需要尽量避免私有属性和私有方法的定义出现在头文件中。只要不引入私有的头文件,那就无法直接访问这些属性和方法。

隐藏内部属性

私有属性,可以分成两种,一种是希望放到类内部而纯粹不想暴露给任何人的,可以叫做内部属性。一种是希望暴露到Private Header中,只限于引入该头文件的地方进行访问。

内部属性的声明非常简单,我们可以直接使用类扩展声明属性,而编译器会自动生成getter和setter,不需要任何额外工作。

阅读全文 »

对各种客户端来说,无论是Web还是移动端,图片占据的容量和传输资源一定是非常大的。对于静态图,我们常见的PNG和JPEG格式在压缩率和画质无损上都存在着不尽如人意的地方,而动图格式的GIF更是存在着很多问题,比如因此,在很多情况下,我们需要迁移到新的图片格式。

GIF

为什么我们不用GIF呢,GIF由于时代限制,存在的天生的问题。GIF的规范最新版本是在1989年制定的,一个24位色都没有普及的时代,因此,GIF规范只支持256色索引颜色,并且只能通过抖动、差值等方式模拟较多丰富的颜色。更为悲剧的是,它的alpha通道只有1bit,换言之,一个像素要么完全透明,要么完全不透明,而不像现在PNG的RGBA的8bit alpha通道,alpha值也可以和RGB一样都有255个透明值。这导致了所有GIF的图片带上透明度以后,边缘会出现明显的锯齿。所以如果你的客户端需要展示带透明度的动图,GIF基本上可以不考虑

实际的在线Demo,建议用Safari或者Chrome+插打开:http://apng.onevcat.com/demo

APNG

阅读全文 »

年后的最后一天,也是该总结一下这一年的成长了。

从校园到实习

软件专业的大三学年,如果你不考研留学的话,说起来都是会走实习加校招的流程。自己这年印象最深的,也就是整个下半年的实习,面试,以及现在的实习了。

大三上的实习当时自己错过了机会,只进入了一个普通的互联网公司,但是也学到了更多校园中无法见到的东西(比如多移动App多Project管理,MapKit,React Native),开阔了一点眼界。真正的下半年好几个月都在忙着校招面试相关的东西,从中也总结了一些经验吧(虽然不知道对社招是否有帮助)。然而到现在,从南京来到了北京,提前过来头条这边实习。

实习开始都是兴奋和好奇的,因为可以看到公司的各种设施,福利,还有认识各式各样的人。来这边也是专门拉了两个同公司的应届生一起来,也挺愉快的。公司离得近,组里的人也都比较厉害(至少都是我请教别人教育的情形),虽然上班累了些,但是感觉自己成长也是非常的大,很难想象假如不来实习,全靠自己摸索,明年直接毕业过来上班自己是什么样的水平。

近期工作感想

阅读全文 »

ReactiveCocoa和RxSwift

iOS的开发上,Objective-C可以说既是一个巨大的成功,也是一个巨大的限制。Cocoa Touch提供的原生API本身就是目标当年的事件驱动和消息派发的GUI编程模型,并且专门为Objective-C这门类smalltalk的消息式OO语言设计的,更为尴尬的是iOS上没有OS X上自带的Data Binding。种种原因,导致Target-Acion,KVO,Notification,Apple式MVC架构才会一直成为iOS开发的主流。然而,做过开发的都知道,这套架构在大型App,尤其复杂是网络请求和人机交互特别多的情况下,非常容易让整个App架构变得难以维护。

Apple式的MVC,又称为Massive View Controller,会让你整个业务代码和UI绑定代码充斥同一个文件中,并且导致很多人经常会在View中,直接#include一个Modeld的头文件,然后起一个configureInfo: 的方法,直接在里面把Model的数据拿来绑定到View的属性上。不信?试试搜一遍你所有的View,把Model的头文件删掉,看看能否编译通过。

1
2
var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(user)

MVP架构或许是你的救星,不过实际上,MVP只是一个工程化的解决问题,把Massive View Controller变成Massive View Presenter,带来相对明确的架构分层的副作用就是近乎两倍的代码量。而在这种情况下,MVVM的架构就是一个非常大的突破,和MVP一样把View/ViewController扔到一起,但是引入单独的ViewModel,通过View到ViewModel的单向绑定,ViewModel对Model的订阅,既避免了MVC造成的代码混乱,又减少了MVP的造成的重复代码。而实践上,提到MVVM,就得 提到ReactiveCocoa或者RxSwift,这两者都是FRP的GUI框架实现。

ReactiveCocoa

阅读全文 »

熟悉做端GUI程序(客户端,Web前端)的同学一定会知道,做UI最大的问题就是模型和视图对象的绑定,视图对象的状态管理,以及事件消息的处理。

背景

传统的GUI编程的一大核心,就是使用了事件驱动编程模型。UI对象的布局、状态等,通过外部的消息事件(点击,触摸,网络请求响应等等)来触发。这是由于GUI程序的人机交互的天生性质决定的(当然,这里的GUI不包括游戏,游戏一般采用立即的帧驱动而非事件)。对于GUI编程的架构方面,从发现到现在,不外乎这几种:

传统的事件监听,消息派发机制:

这是最常见,也是最贴近GUI程序底层实现的模型。一般来说,GUI程序的框架入口就是一个大的while(true)循环,通过在循环内不断向窗口管理器请求消息(比如点击事件等用户输入),通过把底层的消息回调函数回调或者IPC机制,封装成一个个对开发者友好的事件对象来派发出来。

阅读全文 »

Core Animation 3D 仿射变换知识

之前写的Core Graphics是2D平面上的坐标变换,而iOS开发中,为了实现复杂的动画效果,视图切换效果,会用到很多3D变换,这就是Core Animation提供的CATransform3D,其中大部分API都和2D情况类似,但这里需要详细解释一下透视投影这个概念,和m34这个值的真实来源,一些博客抄来抄去却没有点到点子上,让人看不下去……

变换矩阵

1
2
3
4
5
6
typedef struct CATransform3D {
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
} CATransform3D;

这个结构体对应的是这样一个4x4的变换矩阵:

$ \begin{bmatrix} m11 & m12 & m13 & m14 \\ m21 & m22 & m23 & m24 \\ m31 & m32 & m33 & m34 \\ m41 & m42 & m43 & m44 \end{bmatrix} $

阅读全文 »