本文内容来自Intermediate Core Graphics
Intermediate Core Graphics(Swift)-Transforms、Shadows
Transforms
所有的view
和context
都有一个current transformation matrix
,或者叫做CTM。matrix
表示的当前的position
、scale
和rotation
。
UIView
有一个CGAffineTransform
类型的transform
属性来表示matrix
context通过CGContextGetCTM
来获取当前状态的CTM
例如,需要绘制如下的饼状图:
首先绘制红色的扇形图:
绘制绿色的扇形,把context
旋转红色的扇形大小的角度,由于context
的默认旋转点是在左上角,所以此时绿色的扇形是绘制在屏幕之外的地方
所以,正确的做法是,在旋转context之前,平移context,这样context的左上角就处于旋转的正确的位置:
然后再旋转
再把context平移到原来的位置
这时就可以绘制绿色的扇形
所以,整个的绘制的过程是:Translate->Rotate->Translate Back->Draw
本例的绘制chart过程如下:
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let totalSpent = categories.reduce(0, combine: {$0 + $1.spent})
guard totalSpent > 0 else { return }
let diameter = min(bounds.width, bounds.height)
let margin: CGFloat = 20
//绘制Circle,平移到中心点
let circle = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: diameter, height: diameter).insetBy(dx: margin, dy: margin))
let transform = CGAffineTransformMakeTranslation(bounds.width/2-diameter/2, 0)
circle.applyTransform(transform)
let workingCategories = categories.filter({$0.spent > 0})
for(index, _) in workingCategories.enumerate() {
//扇形的百分比和角度
let percent = CGFloat(workingCategories[index].spent / totalSpent)
let angle = percent * 2 * π;
print(workingCategories[index].name, workingCategories[index].spent, round(percent * 100))
//半径和中心点
let radius = diameter / 2 - margin
let centerPoint = center
//扇形路径
let slice = UIBezierPath()
slice.moveToPoint(centerPoint);
slice.addLineToPoint(CGPoint(x: centerPoint.x+radius, y: centerPoint.y))
slice.addArcWithCenter(centerPoint, radius: radius, startAngle: 0, endAngle: angle, clockwise: true)
slice.closePath()
//填充
chartColors[index].setFill()
slice.fill()
//CTM变换
CGContextTranslateCTM(context, centerPoint.x, centerPoint.y)
CGContextRotateCTM(context, angle)
CGContextTranslateCTM(context, -centerPoint.x, -centerPoint.y)
}
circle.stroke()
}
结果如下:
不光Core Graphic可以应用Transform,view和layer也可以应用Transform
Shadows
设置阴影的非UIKit
的方法:
CGContextSetShadowWithColor(_:_:_:_:)
指定color,shadow的偏移量和blur radius
如下所示的例子:
Shadow with Stroke
下图是上图的circle添加了stroke之后的阴影效果,会发现其有两个阴影
- fill的阴影
- stroke的阴影
如果要实现如下的只有一个阴影该怎么做呢?
答案就是要使用transparency layer
在stroke和fill之前,开始了一个transparency layer
。它所做的是,创建了一个单独的drawing buffer
,所有的shape
都被绘制到这个buffer
中,在这个
transparency layer
结束后,然后buffer
作为一个整体被绘制到context
中,所以就只有一个阴影
本例的内容是为上一节的chart,创建一个阴影
如果单独添加如下代码:
//阴影
let shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.4)
CGContextSetShadowWithColor(context, CGSize(width: 10,height: 10), 1, shadowColor.CGColor)
此时的效果如下,会发现每个扇形区域都一个阴影:
当添加CGContextBeginTransparencyLayer(context, nil)
后
还要注意的是当改变当前CTM,最好使用CGContextSaveGState
来保存状态
完整代码如下:
CGContextSaveGState(context)
//阴影
let shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.4)
CGContextSetShadowWithColor(context, CGSize(width: 10,height: 10), 1, shadowColor.CGColor)
CGContextBeginTransparencyLayer(context, nil)
let workingCategories = categories.filter({ $0.spent > 0 })
for (index, _) in workingCategories.enumerate() {
......
}
circle.stroke()
//结束TransparencyLayer
CGContextEndTransparencyLayer(context)
CGContextRestoreGState(context)
可以对context进行scale和translate,使chart看起来有3d的效果
CGContextScaleCTM(context, 1, 0.6)
CGContextTranslateCTM(context, 0, 100)
效果如下: