第五章:Transforms
Affine Transforms
CGAffineTransform是二维的
Creating a CGAffineTransform
主要有三种变化方法
旋转:
CGAffineTransformMakeRotation(CGFloat angle)
缩放:
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
移动:
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
例子5.1 CGAffineTransformMakeRotation
源码在这里下载:http://www.informit.com/title/9780133440751
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
- self.layerView.layer.affineTransform = transform;
- }
-
- @end
修改 CGAffineTransformMakeScale
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CGAffineTransform transform = CGAffineTransformMakeScale(0.5, 0.5);
- self.layerView.layer.affineTransform = transform;
- }
修改 CGAffineTransformMakeTranslation
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CGAffineTransform transform = CGAffineTransformMakeTranslation(-50.0, 30.0);
- self.layerView.layer.affineTransform = transform;
- }
Combining Transforms
方法1:使用CGAffineTransformConcat
继续修改例子5.1
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CGAffineTransform transform1 = CGAffineTransformMakeRotation(M_PI_4);
- CGAffineTransform transform2 = CGAffineTransformMakeScale(0.5, 0.5);
- CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
- self.layerView.layer.affineTransform = transform;
- }
方法2:
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
和前面的CGAffineTransformMakeRotation函数相同,也可以混用
CGAffineTransform t可以使用CGAffineTransformIdentity函数初始化
例子5.2
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CGAffineTransform transform = CGAffineTransformIdentity;
-
-
- transform = CGAffineTransformScale(transform, 0.5, 0.5);
-
-
- transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
-
-
- transform = CGAffineTransformTranslate(transform, 200, 0);
-
-
- self.layerView.layer.affineTransform = transform;
- }
-
- @end
下面记几个特殊值
1. 首先要知道函数 CGAffineTransformIdentity 初始化的结果
2. 左右翻转
CGAffineTransformMake(-1,0,0,1,0,0);
3. 以右边为轴向右翻转
CGAffineTransformMake(-1,0,0,1,self.layerView.frame.size.width,0);
4. 上下翻转
CGAffineTransformMake(1,0,0, -1,0,0);
5. 以底边为轴向下翻转
CGAffineTransformMake(1,0,0, -1,0,self.layerView.frame.size.height);
6. 转180°
CGAffineTransformMake(-1,0,0, -1,0,0);
7. 例子5.3,向右斜拉
CGAffineTransformMake(1,0, -1,1,0,0);
代码:
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
- @implementation ViewController
-
- CGAffineTransform CGAffineTransformMakeShear(CGFloat x, CGFloat y)
- {
- CGAffineTransform transform = CGAffineTransformIdentity;
- transform.c = -x;
- transform.b = y;
- return transform;
- }
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- self.layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0);
- }
-
- @end
8. 例子5.3,向左斜拉
CGAffineTransformMake(1, 0,1, 1, 0, 0);
3D Transforms
类似CGAffineTransform,CATransform3D是三维的
CATransform3D又是一个结构。他有自己的一个公式,可以进行套用。
struct CATransform3D
{
CGFloat m11(x缩放), m12(y切变), m13(旋转), m14( );
CGFloat m21(x切变), m22(y缩放), m23( ), m24( );
CGFloat m31(旋转), m32( ), m33( ), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
CGFloat m41(x平移), m42(y平移), m43(z平移), m44( );
};
同样有三种变换方法
旋转:
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
首先要先清楚x,y,z是什么
{x, y, z}组成的向量就是旋转要使用的轴,angle是旋转角度
例:原图
向X轴旋转45度。 向Y轴旋转45度。 向Z轴旋转45度。
向 X轴,Y轴都旋转45度,就是沿着对角线旋转。
缩放:
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
sx:X轴缩放,代表一个缩放比例,一般都是0 ---1之间的数字。
sy:Y轴缩放。
sz:整体比例变换时,也就是m11(sx) == m22(sy)时,若m33(sz)>1,图形整体缩小,
若0 < m33(sz) < 1,图形整体放大,
若m33(sz) < 0,发生关于原点的对称等比变换。
当sx = 1,sy =1时。如图:
当sx = 0.5,sy =0.5时。如图:
变换:
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
t' = [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]
1 0 0 0
0 1 0 0
0 0 1 0
tx ty tz 1
竖起来看对应前面的数据结构就很明显了。
tx:X轴偏移位置,往下为正数。
ty:Y轴偏移位置,往右为正数。
tz:Z轴偏移位置,往外为正数。
可以通过直接修改数据结构,来设置变换效果
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
}
- CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- transform.m11 = 2;
或者修改键值
- [myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];
例子5.4
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- self.layerView.layer.transform = transform;
- }
-
- @end
修改例子5.4,修改自http://lepetit-prince.net/ios/?p=451
- #import "ViewController.h"
- #import <QuartzCore/QuartzCore.h>
-
- @interface ViewController ()
- {
- BOOL front;
- }
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
- front = YES;
-
- self.layerView.layer.contents = (__bridge id)([UIImage imageNamed:@"front.png"].CGImage);
- }
-
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- [UIView animateWithDuration:0.5 animations:^{
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 0.5, 0.0f, 1.0f, 0.0f);
- } completion:^(BOOL finished) {
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 1.5, 0.0f, 1.0f, 0.0f);
-
- self.layerView.layer.contents = front ? (__bridge id)([UIImage imageNamed:@"back.png"].CGImage) : (__bridge id)([UIImage imageNamed:@"front.png"].CGImage);
-
- [UIView animateWithDuration:0.5 animations:^{
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 2, 0.0f, 1.0f, 0.0f);
- } completion:^(BOOL finished) {
- front = !front;
- }];
- }];
- }
-
- @end
资源文件
front.png back.png
Perspective Projection
前面提到过m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义)
例子5.5
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *layerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D transform = CATransform3DIdentity;
-
-
- transform.m34 = - 1.0 / 500.0;
-
-
- transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
-
-
- self.layerView.layer.transform = transform;
- }
-
- @end
如果修改注释掉旋转,看看会有什么结果
例子是用的透视场景是±1.0/d,d镜头到景物的距离,取值500~1000效果最好,±代表方向
The Vanishing Point
当景物慢慢远离镜头时,随着越来越小最终聚集到一点就是Vanishing Point(灭点)
通常情况灭点是在视图的正中心,或者在包含所有景物范围的中心。
Core Animation把灭点定义在anchorPoint,所以在变换前需要确定anchorPoint,
尤其需要注意,3D变换时最好确保同一视图内的所有layey有相同的灭点
The sublayerTransform Property
如果你有多个View或Layer有相同的3D变换,就可以使用
sublayerTransform,
sublayerTransform也是CATransform3D,只有sublayers才会响应。
默认值是Identity Transform(CATransform3DIdentity)
例子5.6
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *containerView;
- @property (nonatomic, weak) IBOutlet UIView *layerView1;
- @property (nonatomic, weak) IBOutlet UIView *layerView2;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D perspective = CATransform3DIdentity;
- perspective.m34 = - 1.0 / 500.0;
- self.containerView.layer.sublayerTransform = perspective;
-
-
- CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- self.layerView1.layer.transform = transform1;
-
-
- CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
- self.layerView2.layer.transform = transform2;
- }
-
- @end
我们挪动一下xib里的图片位置:
再看结果
恢复xib文件,并修改代码
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
-
-
-
-
-
- CATransform3D transform1 = CATransform3DIdentity;
- transform1.m34 = - 1.0 / 500.0;
- transform1 = CATransform3DRotate(transform1, M_PI_4, 0, 1, 0);
- self.layerView1.layer.transform = transform1;
-
-
- CATransform3D transform2 = CATransform3DIdentity;
- transform2.m34 = - 1.0 / 500.0;
- transform2 = CATransform3DRotate(transform2, -M_PI_4, 0, 1, 0);
- self.layerView2.layer.transform = transform2;
- }
结果和最初一样,但再次修改xib文件,挪动图片看
结果,比较未改代码时的效果
大家发现设置
sublayerTransform的好处了吗
1. 可以一次设置所有subLayer的变换效果
2. Vanishing Point(灭点)被同时设置在container layer即父图层的中心,这就意味着无论你怎么
修改subLayer的position或frame,它们都会保持一个相同的灭点。
Backfaces
例子5.4,我们设置的是旋转M_PI_4(45°),改为M_PI(180°)
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
- self.layerView.layer.transform = transform;
- }
翻到了layer背面,显示的是原图像的镜像图。由此可见layer是双面的,并且两面都被描绘了。
因此我们会想到,为什么要浪费GPU去描绘我们看不见的部分呢。CALayer的另外一个属性
doubleSided可以解决这个问题。
在刚才修改过的例子5.4的代码中增加doubleSided设置
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
- self.layerView.layer.transform = transform;
- self.layerView.layer.doubleSided = NO;
- }
图像没有了
Layer Flattening
自己看例子吧,例子5.7和5.8
Solid Objects
类别 |
用途 |
CAEmitterLayer |
用于实现基于Core Animation粒子发射系统。发射器层对象控制粒子的生成和起源 |
CAGradientLayer |
用于绘制一个颜色渐变填充图层的形状(所有圆角矩形边界内的部分) |
CAEAGLLayer/CAOpenGLLayer |
用于设置需要使用OpenGL ES(iOS)或OpenGL(OS X)绘制的内容与内容储备。 |
CAReplicatorLayer |
当你想自动生成一个或多个子层的拷贝。复制器为你生成拷贝并使用你指定的属性值以修改复制品的外观和属性。 |
CAScrollLayer |
用于管理由多个子区域组成的大的可滚动区域 |
CAShaperLayer |
用于绘制三次贝塞尔曲线。CAShaperLayer对绘制基于路径的形状非常有帮助。因为CAShaperLayer总是生成一个最新的路径。而如果将路径画在图层储备中,一旦图层被缩放,形状就变形了。 |
CATextLayer |
用于渲染一个无格式或属性文本字符 |
CATransformLayer |
用于渲染一个真3D的图层层级。而不是由其他图层类实现的2D图层层级。 |
QCCompositionLayer |
用于渲染一个Quartz组件元素(仅在OS X中有效) |
CAShapeLayer
使用CGPath绘制矢量图,
UIBezierPath类可以创建基于矢量的路径,此类是Core Graphics框架关于path的一个封装。它可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状。
源码在这里下载:http://www.informit.com/title/9780133440751
例子6.1
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *containerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(175, 100)];
- [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(150, 125)];
- [path addLineToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(125, 225)];
- [path moveToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(175, 225)];
- [path moveToPoint:CGPointMake(100, 150)];
- [path addLineToPoint:CGPointMake(200, 150)];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor clearColor].CGColor;
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinRound;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
-
- @end
先说说CAShapeLayer的属性设置
1. 线颜色
- @property CGColorRef strokeColor
2. 填充色
- @property CGColorRef fillColor
3. 填充规则
- @property(copy) NSString *fillRule
修改例子6.1
默认值kCAFillRuleNonZero的情况
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(200, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(250, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:2*M_PI clockwise:YES];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor blueColor].CGColor;
- shapeLayer.fillRule = kCAFillRuleNonZero;
-
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
再修改
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(200, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(250, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:-2*M_PI clockwise:NO];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor blueColor].CGColor;
- shapeLayer.fillRule = kCAFillRuleNonZero;
-
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
kCAFillRuleEvenOdd的情况
修改代码
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(200, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(250, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:2*M_PI clockwise:YES];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor blueColor].CGColor;
-
- shapeLayer.fillRule = kCAFillRuleEvenOdd;
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
同样修改
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(200, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(250, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:-2*M_PI clockwise:NO];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor blueColor].CGColor;
-
- shapeLayer.fillRule = kCAFillRuleEvenOdd;
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
继续为了看清奇偶的效果,画3个同方向圆圈
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(200, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(250, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(300, 150)];
- [path addArcWithCenter:CGPointMake(150, 150) radius:150 startAngle:0 endAngle:2*M_PI clockwise:YES];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor blueColor].CGColor;
-
- shapeLayer.fillRule = kCAFillRuleEvenOdd;
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
以上我们应该清楚不同的规则了吧,挪用别人的描述
nonzero字面意思是“非零”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右穿过射线则计数加1,从右向左穿过射线则计数减1。得出计数结果后,如果结果是0,则认为点在图形外部,否则认为在内部。下图演示了nonzero规则:
evenodd字面意思是“奇偶”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部。下图演示了
evenodd 规则:
4. 线端点类型
- @property(copy) NSString *lineCap
5. 线连接类型
- @property(copy) NSString *lineJoin
6. 线宽
- @property CGFloat lineWidth
7. 线型模板
- @property(copy) NSArray *lineDashPattern
这是一个NSNumber的数组,索引从1开始记,奇数位数值表示实线长度,偶数位数值表示空白长度
8. 线型模板的起始位置
- @property CGFloat lineDashPhase
修改例子6.1,为了看得更清楚,把lineCap的设置注释,,自己看看不注释是什么结果
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(175, 100)];
- [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(150, 125)];
- [path addLineToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(125, 225)];
- [path moveToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(175, 225)];
- [path moveToPoint:CGPointMake(100, 150)];
- [path addLineToPoint:CGPointMake(200, 150)];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor clearColor].CGColor;
-
- shapeLayer.lineWidth = 5;
- shapeLayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:20], [NSNumber numberWithInt:10], [NSNumber numberWithInt:10], [NSNumber numberWithInt:2], nil nil];
-
- shapeLayer.lineJoin = kCALineJoinBevel;
-
-
-
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
再修改lineDashPhase值=15
9. 最大斜接长度。
- @property CGFloat miterLimit
斜接长度指的是在两条线交汇处内角和外角之间的距离。
只有lineJoin属性为kCALineJoinMiter时miterLimit才有效
边角的角度越小,斜接长度就会越大。
为了避免斜接长度过长,我们可以使用 miterLimit 属性。
如果斜接长度超过 miterLimit 的值,边角会以 lineJoin的 "bevel"即kCALineJoinBevel类型来显示
10. 部分绘线
- @property CGFloat strokeStart
- @property CGFloat strokeEnd
都是0.0~1.0的取值范围
具体看修改例子6.1
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- UIBezierPath *path = [[UIBezierPath alloc] init];
- [path moveToPoint:CGPointMake(175, 100)];
- [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
- [path moveToPoint:CGPointMake(150, 125)];
- [path addLineToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(125, 225)];
- [path moveToPoint:CGPointMake(150, 175)];
- [path addLineToPoint:CGPointMake(175, 225)];
- [path moveToPoint:CGPointMake(100, 150)];
- [path addLineToPoint:CGPointMake(200, 150)];
-
-
- CAShapeLayer *shapeLayer = [CAShapeLayer layer];
- shapeLayer.strokeColor = [UIColor redColor].CGColor;
- shapeLayer.fillColor = [UIColor clearColor].CGColor;
-
- shapeLayer.lineWidth = 5;
-
-
- shapeLayer.lineJoin = kCALineJoinBevel;
- shapeLayer.lineCap = kCALineCapRound;
- shapeLayer.strokeStart = 0.1;
- shapeLayer.strokeEnd = 0.6;
- shapeLayer.path = path.CGPath;
-
-
- [self.containerView.layer addSublayer:shapeLayer];
- }
UIBezierPath贝塞尔曲线的常用绘图方法
1. 矩形
- + (UIBezierPath *)bezierPathWithRect:(CGRect)rect
2. 矩形内切椭圆
- + (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
3. 圆角矩形
- + (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius
4. 可设置的圆角矩形
- + (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii
corners有以下几种类型:
UIRectCornerTopLeft,
UIRectCornerTopRight,
UIRectCornerBottomLeft,
UIRectCornerBottomRight,
UIRectCornerAllCorners
cornerRadii表示的是四个圆角拼成的椭圆的长、短半径尺寸。
5. 圆弧
- + (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
- - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
以下需要配合moveToPoint使用
- - (void)moveToPoint:(CGPoint)point
6. 直线
- - (void)addLineToPoint:(CGPoint)point
7. 曲线
- - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
8. 二元曲线
- - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint
UIBezierPath的属性设置(这些属性在使用
CAShapeLayer时,只遵循CAShapeLayer的设置)
1. 线宽
- @property(nonatomic) CGFloat lineWidth
2. 端点类型
- @property(nonatomic) CGLineCap lineCapStyle
3. 连接类型
- @property(nonatomic) CGLineJoin lineJoinStyle
4. 设置线型
- - (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase
pattern:C类型的线型数据。如:CGFloat dashStyle[] = { 1.0f, 2.0f };
count:pattern中的数据个数
phase: 开始画线型的起始位置
其他的我在这里就不多说了
CATextLayer
例子6.2
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *labelView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATextLayer *textLayer = [CATextLayer layer];
- textLayer.frame = self.labelView.bounds;
- [self.labelView.layer addSublayer:textLayer];
-
-
-
-
-
- textLayer.foregroundColor = [UIColor blackColor].CGColor;
- textLayer.alignmentMode = kCAAlignmentJustified;
-
- textLayer.wrapped = YES;
-
-
- UIFont *font = [UIFont systemFontOfSize:15];
-
-
- CFStringRef fontName = (__bridge CFStringRef)font.fontName;
- CGFontRef fontRef = CGFontCreateWithFontName(fontName);
- textLayer.font = fontRef;
- textLayer.fontSize = font.pointSize;
- CGFontRelease(fontRef);
-
-
- NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing \
- elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar \
- leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc \
- elementum, libero ut porttitor dictum, diam odio congue lacus, vel \
- fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet \
- lobortis";
-
-
- textLayer.string = text;
- }
-
- @end
仔细看文字周围很模糊,解决这个问题需要设置contentsScale
修改“textLayer.contentsScale = [UIScreen mainScreen].scale;”
Rich Text
例子6.3
代码不贴了
CATextLayer also renders much faster than UILabel. It’s a little-known fact that on iOS6 and earlier,UILabel actually uses WebKit to do its text drawing, which carries a significant performance overhead when you are drawing a lot of text.CATextLayer uses Core Text and is significantlyfaster.
例子6.4使用layer实现的label,有兴趣的完善一下
CATransformLayer
例子6.5
代码不贴了
修改一下,可以实现简单的拖动旋转(只是试验代码)
- @interface ViewController ()
- {
- CGPoint startPoint;
-
- CATransformLayer *s_Cube;
-
- float pix, piy;
- }
-
- @property (nonatomic, weak) IBOutlet UIView *containerView;
-
- @end
-
- @implementation ViewController
-
- - (CALayer *)faceWithTransform:(CATransform3D)transform
- {
-
- CALayer *face = [CALayer layer];
- face.frame = CGRectMake(-50, -50, 100, 100);
-
-
- CGFloat red = (rand() / (double)INT_MAX);
- CGFloat green = (rand() / (double)INT_MAX);
- CGFloat blue = (rand() / (double)INT_MAX);
- face.backgroundColor = [UIColor colorWithRed:red
- green:green
- blue:blue
- alpha:1.0].CGColor;
-
-
- face.transform = transform;
- return face;
- }
-
- - (CALayer *)cubeWithTransform:(CATransform3D)transform
- {
-
- CATransformLayer *cube = [CATransformLayer layer];
-
-
- CATransform3D ct = CATransform3DMakeTranslation(0, 0, 50);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- ct = CATransform3DMakeTranslation(50, 0, 0);
- ct = CATransform3DRotate(ct, M_PI_2, 0, 1, 0);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- ct = CATransform3DMakeTranslation(0, -50, 0);
- ct = CATransform3DRotate(ct, M_PI_2, 1, 0, 0);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- ct = CATransform3DMakeTranslation(0, 50, 0);
- ct = CATransform3DRotate(ct, -M_PI_2, 1, 0, 0);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- ct = CATransform3DMakeTranslation(-50, 0, 0);
- ct = CATransform3DRotate(ct, -M_PI_2, 0, 1, 0);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- ct = CATransform3DMakeTranslation(0, 0, -50);
- ct = CATransform3DRotate(ct, M_PI, 0, 1, 0);
- [cube addSublayer:[self faceWithTransform:ct]];
-
-
- CGSize containerSize = self.containerView.bounds.size;
- cube.position = CGPointMake(containerSize.width / 2.0,
- containerSize.height / 2.0);
-
-
- cube.transform = transform;
- return cube;
- }
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CATransform3D pt = CATransform3DIdentity;
- pt.m34 = -1.0 / 500.0;
- self.containerView.layer.sublayerTransform = pt;
-
-
- CATransform3D c1t = CATransform3DIdentity;
- c1t = CATransform3DTranslate(c1t, -100, 0, 0);
- CALayer *cube1 = [self cubeWithTransform:c1t];
- s_Cube = (CATransformLayer *)cube1;
- [self.containerView.layer addSublayer:cube1];
-
-
- CATransform3D c2t = CATransform3DIdentity;
- c2t = CATransform3DTranslate(c2t, 100, 0, 0);
- c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
- c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
- CALayer *cube2 = [self cubeWithTransform:c2t];
- [self.containerView.layer addSublayer:cube2];
- }
-
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
-
- startPoint = [touch locationInView:self.view];
- }
-
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
-
- CGPoint currentPosition = [touch locationInView:self.view];
-
- CGFloat deltaX = startPoint.x - currentPosition.x;
-
- CGFloat deltaY = startPoint.y - currentPosition.y;
-
- CATransform3D c1t = CATransform3DIdentity;
- c1t = CATransform3DTranslate(c1t, -100, 0, 0);
- c1t = CATransform3DRotate(c1t, pix+M_PI_2*deltaY/100, 1, 0, 0);
- c1t = CATransform3DRotate(c1t, piy-M_PI_2*deltaX/100, 0, 1, 0);
-
- s_Cube.transform = c1t;
- }
-
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
-
- CGPoint currentPosition = [touch locationInView:self.view];
-
- CGFloat deltaX = startPoint.x - currentPosition.x;
-
- CGFloat deltaY = startPoint.y - currentPosition.y;
-
- pix = M_PI_2*deltaY/100;
- piy = -M_PI_2*deltaX/100;
- }
-
- @end
CAGradientLayer
产生平滑过渡色,
例子6.6
- interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *containerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CAGradientLayer *gradientLayer = [CAGradientLayer layer];
- gradientLayer.frame = self.containerView.bounds;
- [self.containerView.layer addSublayer:gradientLayer];
-
-
- gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,
- (__bridge id)[UIColor blueColor].CGColor];
-
-
- gradientLayer.startPoint = CGPointMake(0, 0);
- gradientLayer.endPoint = CGPointMake(1, 1);
- }
-
- @end
1. 类型
- @property(copy) NSString *type
目前只有NSString * const kCAGradientLayerAxial
即线性梯度变化
2. 颜色
- @property(copy) NSArray *colors
3. 位置参数
- @property(copy) NSArray *locations
颜色的区间分布,locations的数组长度和colors一致, 取值范围(0, 1),而且必须是单调递增的
修改例子6.6,增加
- <p class="p1"> gradientLayer.<span class="s1">locations</span> = <span class="s2">@[</span>[<span class="s1">NSNumber</span> <span class="s3">numberWithFloat</span>:<span class="s2">0.0</span>], [<span class="s1">NSNumber</span> <span class="s3">numberWithFloat</span>:<span class="s2">0.2</span>]<span class="s2">]</span>;</p>
- gradientLayer.locations = @[[NSNumber numberWithFloat:0.5], [NSNumber numberWithFloat:0.7]];
4. startPoint和endPoint
- @property CGPoint startPoint, endPoint;
取值都是相对于layer的bounds的。startPoint默认值为(0.5, 0),endPoint默认值为(0.5, 1)
修改例子6.6
gradientLayer.startPoint 分别设为 CGPointMake(0, 0);
CGPointMake(
0.25,
0);
CGPointMake(
0.5,
0);
CGPointMake(
0.75,
0);
CGPointMake(1,
0);
综合修改例子6.6
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CAGradientLayer *gradientLayer = [CAGradientLayer layer];
- gradientLayer.frame = self.containerView.bounds;
- [self.containerView.layer addSublayer:gradientLayer];
-
-
- gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,
- (__bridge id)[UIColor blueColor].CGColor];
-
- gradientLayer.locations = @[[NSNumber numberWithFloat:0.5], [NSNumber numberWithFloat:0.7]];
-
-
- gradientLayer.startPoint = CGPointMake(0.75, 0.0);
- gradientLayer.endPoint = CGPointMake(1.0, 1.0);
- }
从以上可以看出startPoint和endPoint诗表示的渐变方向,locations是渐变区域。
也可以看出locations的取值是相对于startPoint和endPoint线段的。
在网上找的描述让我很是不能理解
CAReplicatorLayer
例子6.8,修改一下看得更清楚些
- @interface ViewController ()
-
- @property (nonatomic, weak) IBOutlet UIView *containerView;
-
- @end
-
- @implementation ViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
-
- CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
- replicator.frame = self.containerView.bounds;
- [self.containerView.layer addSublayer:replicator];
-
-
- replicator.instanceCount = 20;
-
-
- CATransform3D transform = CATransform3DIdentity;
- transform = CATransform3DTranslate(transform, 0, -10, 0);
- transform = CATransform3DRotate(transform, M_PI / 10.0, 0, 0, 1);
- transform = CATransform3DTranslate(transform, 0, 10, 0);
- replicator.instanceTransform = transform;
-
-
- replicator.instanceBlueOffset = -0.1;
- replicator.instanceGreenOffset = -0.1;
-
-
- CALayer *layer = [CALayer layer];
- layer.frame = CGRectMake(137.5f, 25.0f, 25.0f, 25.0f);
- layer.backgroundColor = [UIColor whiteColor].CGColor;
- [replicator addSublayer:layer];
- }
-
- @end
CAReplicatorLayer应用最多的可能是倒影了,下面的链接是个很好的图片倒影例子
https://github.com/nicklockwood/ReflectionView
后面的几个特殊layer我就不在这里列举了,自己去研究吧
下一次,就将进入真正的动画部分了