iOS实现自定义绘图的几种方式(Swift版)
本文首发在我的个人博客ghui.me 欢迎指教
在iOS中实现自定义绘图,需要获得一个graphics context
对象,它就是你画图的地方(可以理解为一张画板)。总的来说你有两种方法来获得它:
1.如何得到一个画板(Context)
a.自己创建
通过UIGraphicsBeginImageContextWithOptions
方法就可以获得一个图形上下文,然后你就可以在其上进行绘图操作,当绘图操作完成以后,可以通过UIGraphicsGetImageFromCurrentImageContext
得到一个代表绘制内容的UIImage对象,最后不要忘了调用UIGraphicsEndImageContext
关闭此图形上下文。
步骤大概这样:
- 通过 UIGraphicsBeginImageContextWithOptions 创建context
- 执行绘制操作(UIBezierPath/Core Graphics)
- 通过 UIGraphicsGetImageFromCurrentImageContext 得到UIImage对象
- 通过 UIGraphicsEndImageContext 关闭context
示例代码:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0)
//do some draw ...
let image = UIGraphicsGetImageFromCurrentImageContext()
//to do something with the image object
UIGraphicsEndImageContext()
b. Cocoa提供
- 自定义UIView,重写 drawRect: 方法,此方法中已由Cocoa设置好了Context
- 自定义UIView,重写 drawLayer:inContext: 第二个参数就是一个Context
(以上两种方式任选一种)
示例代码:
class CustomView: UIView {
override func drawRect(rect: CGRect) {
//just draw ...
}
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
//draw something whit ctx ...
}
}
两种方式的对比
- 通过 UIGraphicsBeginImageContextWithOptions 创建的context会被自动设置为当前环境的context,所以这种方式下执行绘图时可以直接使用UIKit的绘图方法,不需要进行额外的操作(UIKit只能基于当前Context绘制)
- drawRect 方法中已自动设置好了context可以直接进行UIKit方式的绘图
- drawLayer 方法中的context,并没有被自动设置为当前环境的context,所以不能直接进行UIKit方式的绘制
2. 两种绘图框架
iOS支持两套绘图API: Core Graphics/QuartZ 2D
及OpenGL ES
. 前者又可以被细分为两种: UIKit方式
及Core Graphics方式
,其实所谓UIKit方式
也是基于Core Graphics
的,它只是对Core Graphics
的一个面向对象的封装(Core Graphics是一套基于C的面向过程的绘图API).
而OpenGL ES
是一套跨平台的图形API接口,它只是定义了一套API接口,实现由各个厂商自己做,做iOS的应用开发一般使用不到这种方式,所以这种方式的绘图不在今天的讨论范围.下面详细讨论一下UIKit
及Core Graphics
方式.
a. UIKit
这种方式就是对Core Graphics
方式的一种简化封装,你可以用面向对象的方式很方便的做各种绘图操作,主要是通过UIBezierPath
这个类来实现的, UIBezierPath
可以创建基于矢量的路径,例如各种直线,曲线,圆等等
使用这种方式绘图你的代码看上去,大概是这样:
let p = UIBezierPath(ovalInRect: CGRectMake(0,0, 100, 100))
UIColor.blueColor().setFill()
p.fill()
// ...
b. Core Graphics
这种方式使用起来要比UIKit
方式复杂一些,它是面向过程的,它的每一个绘图函数都需要传入一个context对象,如果你当前位于UIGraphicsBeginImageContextWithOptions函数或drawRect:方法中,并没有引用一个上下文。为了使用Core Graphics
,你可以调用UIGraphicsGetCurrentContext函数获得当前的图形上下文。
使用这种方式绘图你的代码看上去,大概是这样:
let ctx = UIGraphicsGetCurrentContext()!
CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100))
CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor)
CGContextFillPath(ctx)
// ...
两种方式的对比
-
UIKit
也是基于Core Graphics
的,是对Core Graphics
的一种封装,使用起来更简便,Core Graphics
的功能更强大但也更复杂. -
UIKit
只能基于当前Context绘制,可以通过UIGraphicsGetCurrentContext
函数获得当前的图形上下文。
3. 六种绘图形式
至此,我们有了两大绘图框架的支持以及三种获得图形上下文的方法(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)
。那么我们就有6种绘图的形式:
1 . 在UIView的子类方法drawRect:中绘制一个蓝色圆, 使用 UIKit
方式:
override func drawRect(rect: CGRect) {
let p = UIBezierPath(ovalInRect: CGRectMake(0, 0, 100, 100))
UIColor.blueColor().setFill()
p.fill()
}
2 . 在UIView的子类方法drawRect:中绘制一个蓝色圆, 使用 Core Graphics
方式:
override func drawRect(rect: CGRect) {
let con = UIGraphicsGetCurrentContext()!
CGContextAddEllipseInRect(con, CGRectMake(0, 0, 100, 100))
CGContextSetFillColorWithColor(con, UIColor.blueColor().CGColor)
CGContextFillPath(con)
}
3 . 在UIView子类的drawLayer:inContext:方法中,使用UIKit
方式:
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
UIGraphicsPushContext(ctx) //将ctx设置为当前context
let p = UIBezierPath(ovalInRect: CGRectMake(0, 0, 100, 100))
UIColor.blueColor().setFill()
p.fill()
UIGraphicsPushContext(ctx)
}
4 . 在UIView子类的drawLayer:inContext:方法中,使用Core Graphics
方式:
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100))
CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor)
CGContextFillPath(ctx)
}
5 . 使用UIGraphicsBeginImageContextWithOptions
创建画板&用UIKit
方式绘制:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0)
let p = UIBezierPath(ovalInRect: CGRectMake(0, 0, 100, 100))
UIColor.blueColor().setFill()
p.fill()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//to do something with the image
6 . 使用UIGraphicsBeginImageContextWithOptions
创建画板&用Core Graphics
方式绘制:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0)
let con = UIGraphicsGetCurrentContext()
CGContextAddEllipseInRect(con, CGRectMake(0, 0, 100, 100))
CGContextSetFillColorWithColor(con, UIColor.redColor().CGColor)
CGContextFillPath(con)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
参考
http://www.cocoachina.com/industry/20140115/7703.html