iOS 动画学习

gitbook地址

CALayer的几个属性和方法

contents:

// 你给contents赋的不是CGImage,那么你得到的图层将是空白的
layer.contents = (__bridge id)image.CGImage;

contentGravity:

// UIView有一个叫做contentMode,CALayer对应的属性叫做contentGravity

contentsScale:

// 寄宿图的像素尺寸和视图大小的比例
// 如果contentsScale设置为1.0,将会以每个点1个像素绘制图片,如果设置为2.0,则会以每个点2个像素绘制图片
layer.contentsScale = [UIScreen mainScreen].scale;

maskToBounds:

// UIView有一个叫做clipsToBounds,CALayer对应的属性叫做masksToBounds

contentsRect:

// contentsRect是{0, 0, 1, 1},按比例
// contentsRect{0,0,0.5,0.5} 则是左上角四分之一的图

contentsCenter:

// contentsCenter其实是一个CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域
// 气泡拉伸,android的.9图片

Custom Drawing

  1. 通过继承UIView并实现-drawRect:方法来自定义绘制
  2. 实现CALayerDelegate:
/**
* 1.设置delegate
* 2. 调用display
* 3. 实现drawLayer:方法,方法内绘图
*/ 
  CALayer *blueLayer = [CALayer layer];
  blueLayer.delegate = self;
  [blueLayer display];
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
  //draw a thick red circle
  CGContextSetLineWidth(ctx, 10.0f);
  CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
  CGContextStrokeEllipseInRect(ctx, layer.bounds);
}

总结:
CALayerDelegate绘制:寄宿图不会自动重绘它的内容,决定权交给了开发者,通过调用display。
UIView的-drawRect绘制:UIView会帮你做完剩下的工作,无需手动调用display。

图层几何学

布局:

frame:代表了图层的外部坐标(也就是在父图层上占据的空间)
bounds:是内部坐标({0, 0}通常是图层的左上角)
center和position:都代表了相对于父图层anchorPoint所在的位置
注意:frame并不是一个非常清晰的属性,它其实是一个虚拟属性,是根据bounds,position和transform计算而来,改变frame的值同样会影响到他们当中的值
注意:当对图层做变换的时候,比如旋转或者缩放,frame实际上代表了覆盖在图层旋转之后的整个轴对齐的矩形区域,也就是说frame的宽高可能和bounds的宽高不再一致了,如图


iOS 动画学习_第1张图片
86114FA1-BE8E-4CFE-BFC0-9D1974E48DAD.png
锚点(anchorPoint)(注意:center和position都代表了相对于父图层anchorPoint所在的位置)

默认位置:anchorPoint位于图层的中点
大小范围:{0,0}->{1,1} (contentsRect和contentsCenter属性类似)
锚点的作用:eg 实现一个

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *hourHand;
@property (nonatomic, weak) IBOutlet UIImageView *minuteHand;
@property (nonatomic, weak) IBOutlet UIImageView *secondHand;
@property (nonatomic, weak) NSTimer *timer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //start timer
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];
                  
    //set initial hand positions
    [self tick];
}

- (void)tick
{
    //convert time to hours, minutes and seconds
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
    NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
    CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0;
    //calculate hour hand angle //calculate minute hand angle
    CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0;
    //calculate second hand angle
    CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0;
    //rotate hands
    self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle);
    self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle);
    self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle);
}
@end

效果图:

iOS 动画学习_第2张图片
257239BF-BFF4-4B78-9C16-42EA2A7CFF7B.png

改变锚点之后:

- (void)viewDidLoad 
{
    [super viewDidLoad];
    // adjust anchor points
    self.secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f); 
    self.minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f); 
    self.hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
}

效果图:

iOS 动画学习_第3张图片
FEBD2E9B-230C-4E7B-8C30-154BCA1DCCD7.png
坐标系

z坐标轴
zPosition:控制视图层级,越大的处于视图越上面,上面的视图会遮挡下面的视图(可以改变屏幕上图层的顺序,但不能改变事件传递的顺序)
anchorPointZ:z方向锚点

layer的方法:

containsPoint:接受一个在本图层坐标系下的CGPoint,如果这个点在图层frame范围内就返回YES
hitTest:返回图层本身,或者包含这个坐标点的叶子节点图层(测算的顺序严格依赖于图层树当中的图层顺序)

视觉效果

conrnerRadius: 圆角角度
masksToBounds: 裁剪
borderWidth:边框宽度
borderColor:边框颜色
shadowColor:阴影颜色
shadowOffset:阴影的方向和距离
shadowRadius:阴影模糊度
shadowPath:阴影的样式

// shadowPath使用:
 //create a square shadow
  CGMutablePathRef squarePath = CGPathCreateMutable();
  CGPathAddRect(squarePath, NULL, self.layerView1.bounds);
  self.layerView1.layer.shadowPath = squarePath; CGPathRelease(squarePath);

  //create a circular shadow
  CGMutablePathRef circlePath = CGPathCreateMutable();
  CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);
  self.layerView2.layer.shadowPath = circlePath; CGPathRelease(circlePath);
 // 如果是一个矩形或者是圆,用CGPath会相当简单明了。但是如果是更加复杂一点的图形,UIBezierPath类会更合适

阴影的裁剪:(用一个额外的视图来解决阴影裁切的问题)
内层:设置masksToBounds = yes
外层:设置masksToBounds = no and shadow。

CALayer属性mask,蒙板原理实现不规则裁剪

//create mask layer
  CALayer *maskLayer = [CALayer layer];
  maskLayer.frame = self.layerView.bounds;
  UIImage *maskImage = [UIImage imageNamed:@"Cone.png"];
  maskLayer.contents = (__bridge id)maskImage.CGImage;

  //apply mask to image layer
  self.imageView.layer.mask = maskLayer;
iOS 动画学习_第4张图片
CB9B3017-2752-46F8-922D-2874BA055958.png

CALayer蒙板图层真正厉害的地方在于蒙板图不局限于静态图。任何有图层构成的都可以作为mask属性,这意味着你的蒙板可以通过代码甚至是动画实时生成。

layer拉伸与裁剪

layer.magnificationFilter
// 图片拉伸算法
kCAFilterLinear
kCAFilterNearest
kCAFilterTrilinear

//set up digit views
  for (UIView *view in self.digitViews) {
    //set contents
    view.layer.contents = (__bridge id)digits.CGImage;
    view.layer.contentsRect = CGRectMake(0, 0, 0.1, 1.0);
    view.layer.contentsGravity = kCAGravityResizeAspect;
  }

组透明

CALayer属性:opacity(等同UIView的alpha的属性来确定视图的透明度。)
CALayer属性:shouldRasterize(来实现组透明的效果,如果它被设置为YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片,然后再透明)

 //enable rasterization for the translucent button
  button2.layer.shouldRasterize = YES;
  button2.layer.rasterizationScale = [UIScreen mainScreen].scale;
// 设置rasterizationScale属性去匹配屏幕,以防止出现Retina屏幕像素化的问题

变换


专用图层

CAShapeLayer:

你可能感兴趣的:(iOS 动画学习)