[IOS]IOS绘画概念

源文档地址iOS Drawing Concepts

仅仅翻译了Introduction 并且部分由于个人能力不足 所以就简略了


高质量的图像质量对于你App的UI(用户界面)至关重要。高质量的图像不仅使得你的App看起来so cooool,并且是的你的App看起来就像是系统原生的扩展。IOS提供两种主要途径来创建高质量图像:OpenGL  或者是使用原生渲染方式 比如Quartz,CoreAnimation以及UIKit。本篇文档主要讲解的是如何使用原声的渲染方式(如果想要了解OpenGL绘画方式,详见OpenGL ES Programming Guide for iOS。)


Quartz 是主要的绘画接口,提供了基于路径绘画,抗锯齿渲染,渐变填充图案,图像,颜色,空间坐标变化,以及PDF文档的创建,显示和解析支持。UIKit则提供了封装好的艺术线条,Quartz 的图像处理和颜色处理。Core Animation提供了UIView中的动画支持,并且可以来自定义动画效果。


Important:并不是所有的UIKit都线程安全的. 所以在使用某些类的绘制方法查阅文档以确保并不是在主线程中绘制的。


UIKit的图像系统
在IOS中无论是使用OpenGL还是Quartz或者UIKit又或者是CoreAnimation,所有屏幕上所绘图都市UIView的实例类或者是其子类。View限定了绘图屏幕上的绘图范围。如果你使用系统所提供的views,那么该视图将代为处理。如果你自定义视图,那么你就必须提供绘图代码。如果你使用Quartz,CoreAnimation和UIKit来绘制,那么你肯定会用到以下章节的各种绘图概念。
除了直接在屏幕内绘图,UIKit也允许你在屏幕外绘制位图和PDF的图形上下文。当你在屏幕外绘图时,你并不是绘制在View内,即View的绘画周期将不再适用。(除非你获得再获得图片并且重新绘制)




视图的绘图周期
UIView和其子类的基本绘图模式涉及到在需要时更新内容。UIView类使得更新过程变得轻松,高效。通过收集你的更新内容请求,UIView会在最合适的时机更新。
触发更新视图的有以下几种:
1.移动或者删除一部分视图遮住了你的视图
2.使之前不可见的视图变的可见
3.滚动屏幕,使得视图不可见,然后又将视图回滚到屏幕
显式的调用setNeedsDisplay 或者setNeedsDisplayInRect方法。对于自定义的视图(view)必须重写drawRect方法,并在该方法内完成绘制内容。即在该方法哪,使用原生的技术来绘制图片,文本,图形,渐变效果,或者其他任何你想要的视觉效果。当你的视图第一次变得可见的时候,IOS将一块方形的区域传递给视图的drawRect方法。在随后的调用过程时,必须更新视图中需要被重绘制的地方。为了优化性能,你应该只重绘受到影响的部分。
在调用完drawRect方法后,视图将会标记为已更新,然后等待新的请求到达,并且触发重新绘制周期。如果你的视图显示静态的内容,那么你所需要做的只是在触发页面滚动或者其他视图呈现的时候重绘。
如果你想要改变视图的内容,你必须告诉你的视图重新绘制。为了达到这点 显式的调用setNeedsDisplay或者setNeedsDisplayInRect方法吧。比如你要一秒内更新你的视图多次,那么你可能需要设置一个定时器来更新视图。当然你也可以更具用户和视图的交互或者你视图又添加了新的内容来更新内容。
important: 千万不要调用你视图的drawRect方法。该方法需要让系统来调用。




IOS中的坐标系统以及绘图
当一个应用程序在IOS绘图时,它必须有一个坐标系来绘制并定位内容位置。这个概念乍看起来可能很简单,但是并非如此,因为IOS内存在多种不同的坐标系
在IOS中,所有的的绘制都发生在图像上下文(graphics context)。从概念上来讲,上下文简单的说就是一个描述你在where and how 绘制的对象,包括比如颜色,裁减区域,线宽,样式,字体,等等基本属性。


此外,图1.1每个上下文都有一个坐标系,准确的说每个上下文都有三个坐标系

1.绘图坐标系(user)。该坐标系是发出绘图命令时候使用的。

2.视图自身坐标系(base)。该坐标系对于视图来说是固定的。

3.物理坐标系。该坐标系代表了屏幕像素点。

Figure 1-1  The relationship between drawing coordinates, view coordinates, and hardware coordinates











IOS绘图框架创建上下文于特定的目标—屏幕,位图,PDF内容等等,并且这些上下文为这些目标创建了初始的绘图坐标系。初始坐标系呗称为默认坐标系,1:1映射到视图的坐标系统
每个视图也有当前变换矩阵(一个数学矩阵,将当前绘图坐标系的点映射到视图坐标系),app可以修改这个矩阵来改变某些绘图操作的行为。
每个绘图框架都有自己的基于当前上下文的默认坐标系,在IOS中主要有两种坐标系:
左上点为原点的坐标系(ULO),UIKit和CoreAnimation就是基于该坐标系。
另外一种则是坐下点为原点的坐标系(LLO),Core Graphics框架就是基于该坐标系的。
坐标系如1.2所示
Figure 1-2  Default coordinate systems in iOS



在调用你的视图的drawRect方法时,UIKit构建好默认的坐标系来为上下为提供绘图空间。在drawRect中,app能够设置图像状状态参数(比如填充颜色),并且预先提供好上下文,无需显式的获取。非显示的上下文是基于ULO默认坐标系的。


点VS像素
在IOS设备中,坐标系中的点 和 设备里的像素 是有所区别的。
当使用原生的技术比如Quartz,UIKit,CoreAnimation的时候,绘图坐标空间和视图的坐标空间都是逻辑坐标空间。使用逻辑坐标空间的目的是解耦坐标系空间,并让系统框架来管理屏幕上的像素。
系统自动的映射坐标系上的点到设备上的像素坐标系,但是这种映射并不是一对一的关系。这种行为必须牢记在心:一个点并不是对应一个像素。使用点(与逻辑坐标系统)的目的是为了提供输一个一致的输出大小而与又设备无关。
以下就不翻译了 毕竟我们一般还是用点的


获得上下文
大多数情况下,上下文都会被配置好给你。每一个视图对象都会自动创建上下文,以使得你的代码可以立即绘制,只要drawRect方法被调用。作为配置的一部分,底层的UIView会创建上下文给当前绘图环境。
如果你想要在其他地方绘制而不是在你的视图中的,或者如果你想要调用Core Graphics函数需要一个上下文对象的话,你就必须采用额外的步骤来获得上下文对象。下面的章节将会讲解如何做。
更多信息关于上下文,修改图像状态信息,使用上下文自定义内容,见 Quartz 2D Programming Guide.




在屏幕上绘制
如果你使用Core Graphics函数来绘制视图,而不是drawRect方法,你将需要一个上下文(大多数这样的函数的第一个参数也必须是CGContextRef对象)。你应该调用函数UIGraphicsGetCurrentContext来显式的得到上下文,该上下文同样也是ULO默认坐标系的。
如果你想要使用CoreGraphics函数在UIKit的视图上绘画,那么你就应该使用ULO坐标系,来进行绘画操作。或者你可以应用一次翻转变换(U->L)然后就可以直接使用CoreGraphics的原生坐标系。翻转默认坐标系论述其中变化细节。
UIGraphicsGetCurrentContext函数总是返回当前有效的上下文。比如,如果你创建一个PDF上下文,然后调用UIGraphicsGetCurrentContext,你将得PDF上下文。你必须使用UIGraphicsGetCurrentContext来的大上下文如果你想用CoreGraphics在视图中绘制。





颜色和颜色空间
IOS在Quartz中支持全范围的色彩空间,然而大多素app仅仅只需要RGB的色彩空间。因为IOS被设计在特定的硬件运行和显示图像,所以RGB就是最合适的。
你也可以 使用Core Graphics中的 CGContextSetRGBStrokeColor 和 CGContextSetRGBFillColor 函数来创建设置颜色。尽管Core Graphics 框架支持其他色彩空间的来创建颜色,但是这么做并不被支持。所以还是总是用RGB的颜色吧




使用Quartz和UIKit来绘制
Quartz是IOS平台上的原生绘图技术的通用名。Core Graphics 框架就是Quartz的核心,同时也是你用来绘制内容的主要接口。该框架提供数据类型和函数来操作如下属性:
Graphics contexts(上下文)
Paths(路径)
Image and bitmaps(图像和位图)
Transparency layer(涂层透明)
Colors,pattern colors, and color spance(颜色)
Gradients and shadings  (渐变阴影)
Fonts (字体)
PDF content (PDF 内容)
UIKit则是构建于Quarts基础特性之上专注于图像相关的操作库。UIKit图像类并不打算做为一套完整的绘图工具——因为CoreGraophics已经提供了该功能。所以它只提供了为其他的UIKit类绘图支持。以下为UIKit所支持的类:
UIImage
UIFont
UIColor
UIScreen
UIBrzierPath
更多信息了解UIKit Framework Reference


配置上下文
在进行调用drawRect方法前,视图对象先创建上下文并且将该上下文设置为当前上下文,该上下文存在于drawRect调用周期。在drawRect方法中,调用UIGraphicsGetCurrentContext 函数得到  CGContextRef 类型的上下问,这样你就可以传进CoreGraphics函数来修改当前图像状态
表1-1列出了主要函数可以用来改变图像状态的。更新信息了,了解 CGContext Reference。
Table 1-1  Core graphics functions for modifying graphics state
修改某些属性 两个框架对比

Graphics state Core Graphics 函数 UIKit 替代方法

当前上下文转换矩阵 CGContextRotateCTM

CGContextScaleCTM None

CGContextTranslateCTM

CGContextConcatCTM

裁减区域 CGContextClipToRect UIRectClip function

线条的各种属性 CGContextSetLineWidth

CGContextSetLineJoin None

CGContextSetLineCap
CGContextSetLineDash
CGContextSetMiterLimit

曲线估计精度 CGContextSetFlatness None
抗拒锯齿设置 CGContextSetAllowsAntialiasing None

颜色 CGContextSetRGBFillColor UIColor class

CGContextSetRGBStrokeColor

全局透明度 CGContextSetAlpha None
渲染 CGContextSetRenderingIntent None

色彩空间 CGContextSetFillColorSpace UIColor class

CGContextSetStrokeColorSpace

文本:包括字体,大小,宽度等 CGContextSetFont UIFont class

CGContextSetFontSize

CGContextSetCharacterSpacing


混合模式 CGContextSetBlendMode 很多



上下文用栈了已经存储的图像状态。当Quartz创建上下文时,栈是空的。当调用CGContextSaveGState函数时,向栈内压入当前上图像状态。此后,您对图形状态的修改影响到后续绘制操作,但不影响存储在堆栈上的副本。当你完成修改工作后,你可以通过 CGContextRestoreGState函数出栈先前存储在栈上的上下文状态来进行回复。压栈和出栈时快速的方式回复之前图像状态的方式并且无需单独恢复每个状态的变化。同时在某下状态的恢复上面,如剪切路径,这也是恢复到他们原来设置的唯一方法。更多信息关于上下文,见 Graphics Contexts in Quartz 2D Programming Guide.




创建和绘制路径
路径是由一系列的点和贝塞尔曲线组成基于矢量的图形。UIKit包含了 UIRectFrame和 UIRectFill 来绘制简单的路径比如视图中的矩形。Core Graphics也包含了便利的函数来创建类似于矩形,椭圆的路径。
对于更复杂的路径,你必须使用UIKit的UIBezierPath 类自己创建路径,或者使用Core Graphics框架的函数来操纵 CGPathRef。尽管你不用上下文使用上述两种方法就可以构筑一条路径,但是路径上的点仍然需要当前坐标系,所以你仍然需要上下文来画出这些路径。(路径只是单纯的指路径,如果把路径上的点实际画出来 是看不到路径的。)
如果要在IOS上创建路径,推荐使用UIBezierPath 代替 CGPath 函数,出位你确实需要某些特性只有Core Graphics所能提供的,比如添加椭圆路径。更多创建路径信息见Drawing Shapes Using Bézier Paths。更多信息关于使用UIBezierPath 绘制路径见 Drawing Shapes Using Bézier Paths




创建图案,渐变和阴影图
Core Graphics框架包括了额外的函数来创图案,渐变,和阴影。你可以使用这些类型来创建颜色,并用它们来填补你创建的路径。图案是由重复的图像或内容创建。梯度和阴影提供了不同的方式来创建平滑的过渡从颜色到颜色间。
详细创建和使用模式,渐变和阴影见 Quartz 2D Programming Guide.


自定义坐标空间
默认情况下,UIKit创建点映射成像素的变换矩阵。大多数时候你并不需要去修改这些矩阵,然是有时这样做确实很方便。
当你的视图的drawRect方法第一次被调用。CTM(current Transformation Matrix)当前变换矩阵就被设置好了,这个时候你的坐标系 右X增,下Y增。但是你可以改变该矩阵来达到缩放,旋转,平移等转换。这样你也就相应的改变了你的视图的坐标系。




使用坐标转换提高绘图性能
修改视图的CTM绘制内容是一种标准技术来达到重用路径,而省去了大量的计算。比如你想画一个方形在点(20,20),你需要在(20,20)处开始创建一条路径,然后闭合曲线完成撞见,但是之后你想移动方形到(10,10)改怎么办呢?你可能重新创建路径。因为创建路径是相当昂贵(昂贵意思系统开销非常大)的操作,所以最好起始将方形的起始点创建在(0,0),然后通过修改CTM矩阵来移动到想要的左边去。(这就是平移变换,或者可以理解为view的transform属性)
在Core Graphics框架中,有两种方式可以直改CTM。第一种,直接通过定义在CGContext Reference中函数来修改CTM.第二种,你可以创建CGAffineTransform结构体,然后直接施加转换于CTM。


翻转当前的默认坐标系
翻转UIKit中修改了其之下的CALayer来达到LLO效果。如果你只使用UIKit中的方法来绘制,那么你就不应该翻转CTM。但是如果你混合使用 Core Graphics 或者Image I/O函数 和UIKit一起,那么翻转坐标系就是必要的。
特别的,如果你使用Core Graphics里的函数直接绘制图片或者PDF文档,那么就会出现视图内容上下颠倒情况。所以必须正确的在绘图时,翻转坐标系。
为了使得Core Graphics绘制的内容正确的显示在UIKit视图中就需要翻转视图。修改CTM主要可以分为两步,你将当前坐标系的原点转换为左上点,同时将y坐标的增量变成减量,即Y=-1*y。可以参考如下代码

CGContextSaveGState(graphicsContext);


CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);


CGContextScaleCTM(graphicsContext, 1.0, -1.0);


CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));


CGContextRestoreGState(graphicsContext);


如果你使用Core Graphics创建UIImage对象,UIKit会为你进行翻转。每个UIImage对象都有CGImageRef类型支持。你可以访问Core Graphics的CGImage属性来对图像进行一些修改(因为UIKit没有Core Graphics所拥有的那些便利方法)。当你完成创建,重新从修改过的CGImageRef属性创建UIImage对象。


应用Core Animation效果
Core Animation 是一种Objective C框架,提供了快速简单的创建动态,实时动画效果。Core Animation本身并不是绘制技术,从某种意义上来它并不提供基本的途径来创建图形,图像,等等方法,但是它却能为你所创建的内容提供如何显示的技术。
大多数app能够通过使用Core Animation在某些方面获益。动画效果为用户提供了反馈,告诉用户发什么了些什么变化。比如用户使用Settings app(苹果的设置),滑进和滑出某些视图的时候,这告诉了用户你当前正在进行更深入的设置或者是返回上级菜单。这是一项很重要的反馈。同时这也加强了app的视觉效果。
大多数情况下,你可以用很少的经历获得Core Animation所带来的好处。例如设置UIView的几个属性(frame,center,color,opacity等等),会触发动画效果。你得自己做些工作来让UIKit知道你想要这些动画效果,然后这些动画效果就会自动的出现。更深入的了解内置动画效果触发,见Animating Views in UIView Class Reference。

如果你要的动画效果已经超过了内置的基本动画效果所能满足的情况下,你必须直接使用Core Animation了。接下来的章节将会是关于Core Animation,了解更多内置动画效果,见Core Animation Programming Guide.




关于图层
Core Animation的关键技术就是图层。图层是轻量级的对象类似于视图的特性,但是实际上封装了你所想要呈现内容的几何,时间,和视觉属性。该内容可以由以下三种方式中任意一种提供:
你可以设置一个CGImageRef到图层对象的内容属性
你可以给图层设置delegate,然后让delegate帮你完成绘制
你可以继承CALayer然后重写其中的展现方法
当你直接操纵一个图层对象的属性的时候,实际上你也就在操作图层内的内容该如何呈现。你的代码不需要负责该图层的内容的渲染,你所需要做的仅仅只是配置动画效果,然后接下来的就让Core Animation来做吧。更多信息见Core Animation Programming Guide.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值