19.0 概述
Apple 提供了下列framework来处理图像和动画:
- UIKit:
- Quartz 2D: iOS主要的的图形engine,UIKit也是用Quartz
- Core Graphics:支持graphics context
- Core Animation: animation接口
iOS的图形单位是Point,而不是pixel,当app里面提供长和宽的时候,衡量的单位都是point,point和pixel的转换时用content scale factor,比如,iPhone 5是320 point宽,568 point长,但是如果是换算为pixel是640*1136, content scale factor是2。
iOS图形系统的原点在左上角。
19.1 画出文字
调用NSString的drawAtPoint函数,在drawRect里面调用。
drawRect: 当cocoa touch要画一个view的时候,这个函数会被cocoa touch自动调用
override func drawRect(rect: CGRect) {
let fontName = "HelveticaNeue-Bold"
let HelveticaBold = UIFont(name: fontName, size: 40)
let string = "Some String" as NSString
string.drawAtPoint(CGPoint(x: 40, y: 180), withAttributes: [NSFontAttributeName: HelveticaBold!])
}
19.2 画一个图像
可以调用UIKit的下列函数:
- imageNamed:
- imageWithData: 传入一个NSData对象,里面存放一个图片
- initWithContentsOfFile: 传入一个图片文件的全路径
- initWithData: 用一个NSData对象初始化图片
- drawAtPoint: 以图片的原始大小在给的一点画出来
- drawInRect:把图片花在给定的方形里面
drawAtPoint:
let image = UIImage(named: "Safari")
image?.drawAtPoint(CGPointMake(0, 20))
drawInRect:
image?.drawInRect(CGRect(x: 50, y: 10, width: 40, height: 35))
Aspect ratio: 宽高比
19.3 resizable image
一个resizable image包含2个部分:
- 一个不能被拉伸的区域
- 一个可以被拉伸去附和任何大小的区域
下面是一个resizable image:
中间3个竖框中的图像都是一致的,意味着button可以随意的变长,或者缩短。
所以最后切出的图片,中间部分只有一个pixel
当要拉伸上面的切图的时候,iOS是将上面的图分为9个部分,也就是所谓的nine-part images:
top edge/bottom edge: 高不可以resize
right/Left edge:宽不可以resize
Center:都可以
然后用UIEdgeInsets来定义的:
结构中的top/left/bottom/right的值表示你不想拉伸的区域。比如,定义left是10,11是top, 14是right,5是left,那么拉伸,不可拉伸如下图:
拉伸最关键的函数是:
resizableImageWithCapInsets
let button = UIButton.buttonWithType(.Custom) as! UIButton
button.frame = CGRect(x: 0, y: 0, width: 200, height: 44)
button.setTitle("Stretched Image on Button", forState: UIControlState.Normal)
button.titleLabel?.font = UIFont.systemFontOfSize(15)
let image = UIImage(named: "Button")?.resizableImageWithCapInsets(UIEdgeInsets(top: 0, left: 14, bottom: 0, right: 14))
button.setBackgroundImage(image, forState: UIControlState.Normal)
button.center = view.center
view.addSubview(button)
19.4 画线
步骤:
- 取得graphic context;
- 用CGContextMoveToPoint和CGContextAddLineToPoint,画线;
当我们谈到在iOS画一些图形的时候,其实谈的是paths,一条path由屏幕上的一系列点组成,一条path可能包含许多line。画一条line需要用到path,定义开始和结束点,然后让core graphics填充path。
UIColor.brownColor().set()
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 5)
CGContextMoveToPoint(context, 50, 10)
CGContextAddLineToPoint(context, 100, 200)
//当画线到100,200这个点的时候,画笔已经移动到了这个点。想要在100,200这个点继续画线的时候,不需要再调用MoveToPoint
CGContextAddLineToPoint(context, 300, 100)
CGContextStrokePath(context)
在两条线的交叉/连接点,叫join,你可以定义连接的方式,用CGContextSetLineJoin函数,可以有几种方式,定义在CGLineJoin类型里面:
- kCGLineJoinMiter
- kCGLineJoinBevel
- kCGLineJoinRound
代码:
CGContextSetLineJoin(context, kCGLineJoinBevel)
19.5 创建paths
相关函数:
- CGPathCreateMutable: 新建一个mutable path
- CGPathMoveToPoint:移动当前的画笔到point
- CGPathAddLineToPoint:画一条线,从当前画笔的位置到参数指向的点
- CGContextAddPath: 添加一条path到graphic context
- CGContextDrawPath: 把path画出来,有3个重要的画的方法:
- kCGPathStroke: 画一条线,用当前颜色
- kCGPathFill: 用当前颜色,填充封闭的区域,如果没有封闭区域,就不填充
- kCGPathFillStroke:画线而且填充,用当前颜色
- CGPathAddRect: 添加一个方形到path上
附录:让status bar隐藏的方法2,修改plist文件
let path = CGPathCreateMutable()
let screenBounds = UIScreen.mainScreen().bounds
//画第一条线
CGPathMoveToPoint(path, nil, screenBounds.origin.x, screenBounds.origin.y)
CGPathAddLineToPoint(path, nil, screenBounds.size.width, screenBounds.size.height)
//画第二条线
CGPathMoveToPoint(path, nil, screenBounds.size.width, screenBounds.origin.y)
CGPathAddLineToPoint(path, nil, screenBounds.origin.x, screenBounds.size.height)
let context = UIGraphicsGetCurrentContext()
CGContextAddPath(context, path)
UIColor.blueColor().setStroke()
CGContextDrawPath(context, kCGPathStroke)
19.6 画一个方形
用CGPathAddRect
let path = CGPathCreateMutable()
let rectangle = CGRect(x: 10, y: 30, width: 200, height: 300)
CGPathAddRect(path, nil, rectangle)
let currentContext = UIGraphicsGetCurrentContext()
CGContextAddPath(currentContext, path)
UIColor(red: 0.20, green: 0.60, blue: 0.80, alpha: 1.0).setFill()
UIColor.brownColor().setStroke()
CGContextSetLineWidth(currentContext, 5)
CGContextDrawPath(currentContext, kCGPathFillStroke)
画多个方形,用CGPathAddRects
let path = CGPathCreateMutable()
let rectangle1 = CGRect(x: 10, y: 30, width: 200, height: 300)
let rectangle2 = CGRect(x: 40, y: 100, width: 90, height: 300)
let rectangles = [rectangle1, rectangle2]
CGPathAddRects(path, nil, rectangles, 2)
let currentContext = UIGraphicsGetCurrentContext()
CGContextAddPath(currentContext, path)
UIColor(red: 0.20, green: 0.60, blue: 0.80, alpha: 1.0).setFill()
UIColor.brownColor().setStroke()
CGContextSetLineWidth(currentContext, 5)
CGContextDrawPath(currentContext, kCGPathFillStroke)
19.7 添加阴影
有2种方法可以做:
- CGContextSetShadow
- 取得graphics context
- 定义offset,是一个CGSize值,阴影从右方和底部长出,x越大,右方阴影越长,y越大,底部阴影越长
- 定义blur值,0.0f表示实心,值越高,越模糊
- CGContextSetShadowWithColor: 是上一个方法的扩展,加多了一个color值,表示阴影的颜色
如果没有明确表示去除,graphics context是自带阴影属性
let currentContext = UIGraphicsGetCurrentContext()
let offset = CGSizeMake(10, 10)
CGContextSetShadowWithColor(currentContext, offset, 20, UIColor.grayColor().CGColor)
let path = CGPathCreateMutable()
let firstRect = CGRect(x: 55, y: 60, width: 150, height: 150)
CGPathAddRect(path, nil, firstRect)
CGContextAddPath(currentContext, path)
UIColor(red: 0.20, green: 0.60, blue: 0.80, alpha: 1.0).setFill()
CGContextDrawPath(currentContext, kCGPathFill)
如果想graphic context回复到设置之前的值,需要在设置之前调用CGContextSaveGState,在完成之后调用CGContextRestoreGState
CGContextSaveGState(currentContext)
CGContextRestoreGState(currentContext)
19.8 画渐进色图形
用CGGradientCreateWithColor,Core Graphics可以创建2类渐进色: axial和radial。
axial是一个颜色从一个点到另外一个点,另外一个颜色,axial意味着axis(轴),2点形成了一条线,也就是轴。
CGGradientCreateWithColorComponents有4个参数:
- 一个color space:包含了一个范围的颜色,类型是CGColorSpaceRef,
- 一个RGBA值的数组: 如果是2个点,需要包含2组RGBA值
- 一组地址
- 地址和颜色的个数
代码如下,艰涩的代码,很有graphic API的特色
let currentContext = UIGraphicsGetCurrentContext()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let startColor = UIColor.blueColor()
let startColorComponents = CGColorGetComponents(startColor.CGColor)
let endColor = UIColor.greenColor()
let endColorComponents = CGColorGetComponents(endColor.CGColor)
let colorComponents = [
startColorComponents[0],
startColorComponents[1],
startColorComponents[2],
startColorComponents[3],
endColorComponents[0],
endColorComponents[1],
endColorComponents[2],
endColorComponents[3],
]
let colorIndices = [
0.0,
1.0,
] as [CGFloat]
let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents, colorIndices, 2)
let screenBounds = UIScreen.mainScreen().bounds
let startPoint = CGPoint(x: 0, y: screenBounds.size.height / 2)
let endPoint = CGPoint(x: screenBounds.size.width, y: startPoint.y)
CGContextDrawLinearGradient(currentContext, gradient, startPoint, endPoint, 0)
19.9 transform
用CGAffineTransformMake来做仿射变换。可以做3个效果,移动,旋转,缩放。
- 移动用CGAffineTransformMakeTranslation:
x轴移动100个点
var transform = CGAffineTransformMakeTranslation(100, 0)
CGPathAddRects(path, &transform, rectangles, 2)
2。也可以直接对画布进行偏移,用CGContextTranslateCTM
let currentContext = UIGraphicsGetCurrentContext()
CGContextSaveGState(currentContext);
CGContextTranslateCTM(currentContext, 100, 0)
3。缩放用CGAffineTransformMakeScale,如果想对画布进行缩放,用CGContextScaleCTM
缩放成一半的大小
let transform = CGAffineTransformMakeScale(0.5, 0.5)
CGPathAddRect(path, &transform, rectangle);
4。 如果想旋转,用CGAffineTransformMakeRotation,如果想对画布进行旋转,用CGContextRotateCTM
//旋转45度
var transform = CGAffineTransformMakeRotation(CGFloat((45.0 * M_PI) / 180.0))
19.10 动画 (UIKit)
用animateWithDuration:animations:completion: 函数,第一个是animation持续的时间,单位是s,第二个参数是要执行的animation,最后一个参数是完成后要执行的。
let endRect = CGRect(x: 0, y: 30, width: 100, height: 100)
UIView.animateWithDuration(5.0, animations: { () -> Void in
self.imageView.frame = endRect
}) { (Bool) -> Void in
println("Finished")
}
//顺时针转90度
self!.imageView1.transform = CGAffineTransformMakeRotation(CGFloat((90 * M_PI)/180.0))
//逆时针返回初始值
self?.imageView1.transform = CGAffineTransformIdentity