OC-布局Layout、CoreAnimation层面的动画-CALayer&CAAnimation

CoreAnimation层面的动画

CALayer层(显示的基础)

  • UIView核心显示功能就是依靠CALayer实现

UIView和CALayer的关系

  • 1.UIView显示能力是依赖底层的CALayer实现的,每一个UIView都包换一个CALayer对对象,修改CALayer,会影响表现出来的UIView的外观
  • 2.UIView与CALayer最大的不同在于,layer是不能够响应用户事件,UIView可以响应用户事件

如何获取UIView底层的那个CALayer对象

  • 通过.layer属性获取

可以使用CALayer做哪些操作?

常用属性
  • borderColor边框颜色
  • borderWidth边框宽度
  • cornerRadius圆角半径
  • shadowOpacity阴影透明度
  • shadowColor阴影颜色
  • shadowRadius阴影半径
  • shadowOffset阴影偏移量
  • masksToBounds是否按layer遮罩

与尺寸和位置相关的三个重要属性

  • bounds大小
  • position位置
  • anchorPosition锚点自身的参考点
- (void)viewDidLoad {
    [super viewDidLoad];
    //获取 view 中的 layer 对象
    CALayer *layer = self.redView.layer;
    //背景颜色
    layer.backgroundColor = [UIColor greenColor].CGColor;
    //设置边框宽度
    layer.borderWidth = 4;
    //设置边框颜色
    layer.borderColor = [UIColor redColor].CGColor;
    //设置圆角的 半径
//    layer.cornerRadius = self.redView.bounds.size.width * 0.5;
    //设置阴影 一定要先设置 阴影为不透明  默认是透明 0
    layer.shadowOpacity = 1;
    //设置阴影颜色
    layer.shadowColor = [UIColor blackColor].CGColor;
    //设置阴影的圆角
    layer.shadowRadius = 10;
    //设置阴影的 便宜量
    layer.shadowOffset = CGSizeMake(20, 20);


    //设置imageView的layer
    CALayer *imageViewLayer = self.imageView.layer;
    imageViewLayer.cornerRadius = self.imageView.bounds.size.height * 0.5;
    imageViewLayer.borderWidth = 5;
    imageViewLayer.borderColor = [UIColor lightGrayColor].CGColor;
    //要按照层的边缘进行遮罩
    imageViewLayer.masksToBounds = YES;


    //CALyer具有容器的特性  可以相互嵌套
    //创建CALayer对象
    CALayer *myLayer = [CALayer layer];
    myLayer.backgroundColor = [UIColor blueColor].CGColor;
//    myLayer.frame = CGRectMake(0, 0, 100, 100);

    //设置大小
    myLayer.bounds = CGRectMake(0, 0, 100, 100);
    //设置位置  当前layer 参考点(中心点)  在父layer坐标系中 的位置  默认是 0 0 点
//    myLayer.position = CGPointMake(50, 50);
    //设置当前 layer 参考点的位置  (锚点  默认 0.5 0.5)
//    myLayer.anchorPoint = CGPointZero;
    //设置 层的 旋转  (变形 是锚点操作的)
    myLayer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);


    //将自己创建的layer 添加到 self.redView的layer中
    [self.redView.layer addSublayer:myLayer];

}

创建新的Layer使用layer

  • 图片CALayer
    • 给contents属性赋值
  • 文字层CATextLayer
  • 图形层CAShapeLayer

图片Layer

-(CADisplayLink *)link {
    if (!_link) {
        //每刷新一次 调用一次 定时器的事件方法
        _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotation:)];
        //手动将定时器 加入到 事件循环队列中
        [_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        _link.paused = YES;
    }
    return _link;
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.link.paused = !self.link.paused;
}

-(void)rotation:(CADisplayLink*)sender {
    // 5秒钟 转一圈
//    360 / 5; 求出1秒钟多少度  72
//    1秒钟调用 60次  转72度
//    每次调用 转  72/60.0
    //不能用角度 因为参数 需要的是 弧度
    self.imageView.layer.transform = CATransform3DRotate(self.imageView.layer.transform, angleToRadian(72/60.0), 1, 1, 1);
}


- (void)viewDidLoad {
    [super viewDidLoad];

    self.imageView.layer.cornerRadius = self.imageView.bounds.size.width * 0.5;
    self.imageView.layer.borderWidth = 3;
    self.imageView.layer.borderColor = [UIColor redColor].CGColor;
    self.imageView.layer.masksToBounds = YES;
    self.imageView.layer.anchorPoint = CGPointMake(0.2, 0.2);

    for (NSInteger i = 0; i < 8; i++) {
        CALayer *layer = [CALayer layer];
        //为层添加图片内容
        layer.contents = (id)[UIImage imageNamed:@"60"].CGImage;
        layer.bounds = CGRectMake(0, 0, 20, self.imageView.bounds.size.height * 0.5);
        layer.position = CGPointMake(self.imageView.bounds.size.width * 0.5, self.imageView.bounds.size.height * 0.5);
        layer.anchorPoint = CGPointMake(0.5, 1);
        layer.transform = CATransform3DMakeRotation(M_PI_4 * i, 0, 0, 1);
        [self.imageView.layer addSublayer:layer];
    }

}

文字及图形层

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建文字类型的图层
    CATextLayer *tLayer = [CATextLayer layer];
    tLayer.string = @"这是一个文字";
    tLayer.fontSize = 20;
    tLayer.foregroundColor = [UIColor whiteColor].CGColor;
    tLayer.backgroundColor = [UIColor blackColor].CGColor;

    tLayer.bounds = CGRectMake(0, 0, 200, 40);
    tLayer.position = CGPointMake(100, 100);
    tLayer.anchorPoint = CGPointZero;

    //将文字层 添加到 self.view.layer 上
    [self.view.layer addSublayer:tLayer];


    //图形类型的图层
    CAShapeLayer *sLayer = [CAShapeLayer layer];
    self.sLayer = sLayer;
    sLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 100, 50) cornerRadius:10].CGPath;
    sLayer.strokeColor = [UIColor redColor].CGColor;
    sLayer.fillColor = [UIColor whiteColor].CGColor;

    sLayer.backgroundColor = [UIColor blackColor].CGColor;

    sLayer.bounds = CGRectMake(0, 0, 200, 200);
    sLayer.position = CGPointMake(50, 250);
    sLayer.anchorPoint = CGPointZero;

    //添加边框
    sLayer.borderColor = [UIColor blueColor].CGColor;
    sLayer.borderWidth = 5;
    sLayer.cornerRadius = 10;

    //将图形层 添加到 self.view.Layer 上
    [self.view.layer addSublayer:sLayer];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    arc4random_uniform 取出 0 ~ x-1
    self.sLayer.borderWidth = arc4random_uniform(25);
    self.sLayer.cornerRadius = arc4random_uniform(30);

}

CALayer的很多属性都有隐式动画,在修改该属性时,会自动出现动画效果,可以通过查看头文件中,属性上面出现animatable这样的说明时,意味可以有隐式动画

CAAnimation动画

  • CA的动画,只能施加在CALayer上
  • CA动画与UIView动画最大的一个区别:
    • CA动画是假的,视图看着好像位置改变了,但其实没有变
    • UIView动画中,由于明确的设定了动画结束时视图的状态,所以视图的数据会随着动画的结束而真的改变

CAAnimation的子类之一

  • CABasicAnimation基础动画

CAAnimation的子类之二

  • CAAnimationGroup动画组

CAAnimation的子类之三

  • CAKeyFrameAnimation关键帧动画

CAAnimation

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//    [self position];
//    [self transform];
//    [self groupAnimation];
    [self keyFrameAnimation];
}

-(void)position {//位移动画
    CABasicAnimation *basicAnim = [CABasicAnimation animation];
    //位移position  缩放scale  旋转 rotation
    //使用KVC的方式为对象赋值,说明要改的属性名是什么
    basicAnim.keyPath = @"position";
    basicAnim.fromValue =  [NSValue valueWithCGPoint:CGPointMake(50, 50)];
    //toValue  是 到 50 300的位置上
    basicAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(50, 300)];
    //byValue  在自身基础上 移动 50 300
//    basicAnim.byValue = [NSValue valueWithCGPoint:CGPointMake(50, 300)];

    //动画结束时 不把动画 从视图上 移除  如果需要固定动画结束时视图的位置 必须 再配合 fillMode 该属性一起使用
    basicAnim.removedOnCompletion = NO;

    /*
     kCAFillModeForwards  当动画结束后,layer会一致保持着动画最后的状态
     kCAFillModeBackwards 在动画开始前,只需要将动画加入一个Layer,layer便立即进入动画的初始状态并等待运行动画
     kCAFillModeBoth 上面两种效果叠加在一起
     kCAFillModeRemoved (默认) 当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
     */
    basicAnim.fillMode = kCAFillModeBoth;
    //设置动画 开始前等待时间
    basicAnim.beginTime = CACurrentMediaTime() + 3;


    //设置动画的 相关属性
    //动画持续时间
    basicAnim.duration = 2;
    //动画重复次数   一直重复只是给以非常大的值
//    basicAnim.repeatCount = MAXFLOAT;
    basicAnim.repeatCount = 1;

    //运行动画 注意:CA动画只能使用 CALayer对象运行
    [self.imageView.layer addAnimation:basicAnim forKey:nil];
}

-(void)transform {//变形动画

    CABasicAnimation *transformAnim = [CABasicAnimation animation];

//    transformAnim.keyPath = @"transform";
//    transformAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.2, 1)];

    //动画只能执行 最后设置的 keyPath  ,如果需要同时执行只能依赖 CAAnimation 的另外一个子类 CAAnimationGrop
    transformAnim.keyPath = @"transform.scale";
    transformAnim.toValue = @1.2;

    transformAnim.keyPath = @"transform.rotation";
    transformAnim.toValue = @(M_PI * 2);

    transformAnim.duration = 1;
    transformAnim.repeatCount = MAXFLOAT;
    [self.imageView.layer addAnimation:transformAnim forKey:nil];
}

-(void)groupAnimation {

    CABasicAnimation *positionAnim = [CABasicAnimation animationWithKeyPath:@"position.y"];
    positionAnim.toValue = @(self.imageView.center.y + 200);

    CABasicAnimation *transformAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    transformAnim.toValue = @(M_PI);


    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = @[positionAnim, transformAnim];
    group.duration = 3;
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;

    [self.imageView.layer addAnimation:group forKey:nil];
}

-(void)keyFrameAnimation {//关键帧动画
    CAKeyframeAnimation *keyFrameAnim = [CAKeyframeAnimation animation];
    keyFrameAnim.keyPath = @"position";
//    //设置中间行进的路线的关键值
//    keyFrameAnim.values = @[
//            [NSValue valueWithCGPoint:CGPointMake(50, 50)],
//            [NSValue valueWithCGPoint:CGPointMake(200, 200)],
//            [NSValue valueWithCGPoint:CGPointMake(60, 300)],
//            [NSValue valueWithCGPoint:CGPointMake(150, 80)]];


    keyFrameAnim.path = [UIBezierPath bezierPathWithRect:CGRectMake(80, 80, 200, 300)].CGPath;


    keyFrameAnim.duration = 3;
    keyFrameAnim.removedOnCompletion = NO;
    keyFrameAnim.fillMode = kCAFillModeForwards;

    [self.imageView.layer addAnimation:keyFrameAnim forKey:nil];
}

2.布局Layout

什么是布局

  • 是指在一个视图中,如何摆放它的子视图(设置子视图的位置和大小)
可能导致屏幕尺寸大小发生变化的原因
  • a.屏幕方向(横竖屏)
  • b.设备不同(3.5寸,4寸,4.7寸,5.5寸)
  • c.状态栏
    • 隐藏
    • 特殊的状态栏
      – 来电时绿色的状态栏
      – 开启个人热点蓝色的状态栏
      – 录音时红色的状态栏
  • d.各种bar
    • NaviationBar:竖屏时64点高横屏时52个点高
    • ToolBar:44/32个点
    • TabBar:49个点
  • e.键盘弹起和收回

如何布局?

  • 方法一:纯代码布局(古老的方法)
  • 理念:当屏幕发生变化时,自动执行一段代码,在代码中根据新的屏幕大小重新计算各个视图的frame,从而达到重新定位的目的
  • 特点:功能强大,非常繁琐
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *myView1;
@property (weak, nonatomic) IBOutlet UIView *myView2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIView *greenView1;
@property (weak, nonatomic) IBOutlet UIView *greenView2;
@property (weak, nonatomic) IBOutlet UIView *greenView3;

@end

@implementation ViewController

//该方法在view加载完会调用一次, 在屏幕发生旋转也会自动调用
-(void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    CGFloat space20 = 20;
    CGFloat space10 = 10;
    CGFloat viewWidth = (self.view.bounds.size.width - space20 * 2 - space10) * 0.5;
    CGFloat viewHeight = 40;
    CGFloat viewX = space20;
    CGFloat viewY = space20;

    CGFloat greenViewWidth = 20;
    CGFloat greenViewHeight = 20;

    CGRect frame = CGRectMake(viewX, viewY, viewWidth, viewHeight);
    self.myView1.frame = frame;

    //设置 第二个view的frame
    frame.origin.x += viewWidth + space10;
    self.myView2.frame = frame;

    //设置imageview的frame
    frame.origin.x = space20;
    frame.origin.y += viewHeight + space10;
    frame.size.width = self.view.bounds.size.width - space20 * 2;
    frame.size.height = self.view.bounds.size.height - space20 * 2 - space10 * 2 - greenViewHeight - viewHeight;
    self.imageView.frame = frame;

    //设置小绿view1的frame
    frame.origin.x = self.view.bounds.size.width - (space20 + greenViewWidth);
    frame.origin.y = self.view.bounds.size.height - space20 - greenViewHeight;
    frame.size.width = greenViewWidth;
    frame.size.height = greenViewHeight;
    self.greenView1.frame = frame;
    //设置小绿view2的frame
    frame.origin.x -= greenViewWidth + space10;
    self.greenView2.frame = frame;
    //设置小绿view3的frame
    frame.origin.x -= greenViewWidth + space10;
    self.greenView3.frame = frame;


}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值