前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS动画-CALayer布局属性详解

iOS动画-CALayer布局属性详解

作者头像
梧雨北辰
发布2019-04-23 15:51:16
2.2K0
发布2019-04-23 15:51:16
举报

本篇主要内容: 1.Frame与Bounds的区别 2.中心点(position)与锚点(anchorPoint) 3.视图与图层的坐标系

一、Frame与Bounds的区别

我们已经知道UIView的很多布局属性其实都来自于图层;UIView的布局属性包括:frame、bouns、center,分别对应了CALayer中frame、bounds、position。为了能清楚区分,图层用了position,视图用了center,但它们都代表了同样的值。

UIView属性

CALayer属性

属性说明

frame

frame

表示相对于其父视图的坐标位置

bounds

bounds

表示相对于其自身的坐标位置,{0,0}通常是其左上角

center

position

相对于父图层锚点AnchorPoint所在位置

frame&&bounds.png

上图对原有视图做了旋转变换,之后的frame实际上代表了覆盖在图层旋转之后的整个轴对齐的矩形区域,此时frame的宽和高和bounds不再一致了。

其实,对于视图和图层来说,frame是根据bounds、position、和transform计算而来的;所以当其中的任何一个值发生变化时,frame就会发生变化,相反改变frame也同样影响他们当中的值。

六、中心点(position)与锚点(anchorPoint)

1.锚点的概念

position与anchorPoint是两个容易混淆的概念,我们首先从Xcode中找到关于它们的注释说明如下:

代码语言:javascript
复制
/* The position in the superlayer that the anchor point of the layer's
 * bounds rect is aligned to. Defaults to the zero point. Animatable. */
@property CGPoint position;

/* Defines the anchor point of the layer's bounds rect, as a point in
 * normalized layer coordinates - '(0, 0)' is the bottom left corner of
 * the bounds rect, '(1, 1)' is the top right corner. Defaults to
 * '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */
@property CGPoint anchorPoint;

我们可以看出,position被用于描述当前layer在superlayer中的位置,而且是通过当前layer的anchorPoint来确定的。换句话来讲就是:position是当前layer的anchorPoint在superLayer中的位置

我们也可以更确切理解为:position是相对于superLayer来讲,而anchorPoint是相对于当前layer来讲;只不过在默认情况下,anchorPoint与position是重合的;锚点是用单位坐标来描述的(即图层的相对坐标),图层的左上角是{0,0},右下角是{1,1},因此图层的默认锚点是{0.5, 0.5},表示图层的中间位置代表了其位置position。

下面的图示是将锚点从{0.5,0.5}改为了{0,0},我们在这里更容易看到position与anchorPoint之间的关系:

anchorPoint.png

如图,修改图层锚点会改变layer的frame,但是其position不会改变,这看起来似乎有点奇怪,但是我们依然可以通过一些计算方式看出端倪:

代码语言:javascript
复制
position.x = frame.origin.x + 0.5 * bounds.size.width;  
position.y = frame.origin.y + 0.5 * bounds.size.height; 

这里的0.5参数,其实就是由于锚点默认值得到的,所以改进公式如下:

代码语言:javascript
复制
position.x = frame.origin.x + anchorPoint.x * bounds.size.width;  
position.y = frame.origin.y + anchorPoint.y * bounds.size.height;

此时,我们如果通过修改anchorPoint的值来进行测试,就会发现改变的只有frame的origin,这就说明修改position与anchorPoint中任何一个属性都不能影响另一个属性,由此我们也可以再次改进公式:

代码语言:javascript
复制
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;  
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;

最后得出结论:frame的origin坐标由position与anchorPoint来共同决定;

2.锚点的作用

锚点就相当于一个支点,可以形象的理解为一颗固定了图层的图钉,尤其是我们在做旋转动画时,可能会需要设置此属性来决定图层是围绕哪一个点旋转的;但这时候我们又不得不考虑一个问题:修改锚点可以让我们的动画围绕非中心点旋转,但是这也改变了原有视图的位置frame,这是我们不想要的结果,该如何解决呢?这里提供一种方法如下:

代码语言:javascript
复制
- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view{
    CGPoint oldOrigin = view.frame.origin;
    view.layer.anchorPoint = anchorPoint;
    CGPoint newOrigin = view.frame.origin;
    
    CGPoint transition;
    transition.x = newOrigin.x - oldOrigin.x;
    transition.y = newOrigin.y - oldOrigin.y;
    
    view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
}

下面再来具体演示一下修改锚点改变动画状态的用法,我们分别创建橙色视图默认围绕中心旋转,而紫色视图围绕左顶点旋转,关键代码如下:

代码语言:javascript
复制
#import "TestLayerFiveVC.h"
@interface TestLayerFiveVC ()

@property (nonatomic,strong) UIView *viewA;
@property (nonatomic,strong) UIView *viewB;

@end

@implementation TestLayerFiveVC

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.orangeView];
    [self.view addSubview:self.purpleView];
    
    [self.orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(100);
        make.centerX.equalTo(self.view);
        make.width.height.mas_equalTo(100);
    }];
    
    [self.purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.orangeView.mas_bottom).offset(150);
        make.centerX.equalTo(self.view);
        make.width.height.mas_equalTo(100);
    }];
    [self.view layoutIfNeeded];
    
    //orangeView的旋转动画
    [self addRotationAnimation:self.orangeView withDuration:3];

    //修改purpleView的锚点,并恢复其原先的Frame,使其可以绕着左上角顶点旋转
    [self resetAnchorPoint:CGPointMake(0, 0) forView:self.purpleView];
    [self addRotationAnimation:self.purpleView withDuration:3];
}


- (void)resetAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view{
    CGPoint oldOrigin = view.frame.origin;
    view.layer.anchorPoint = anchorPoint;
    CGPoint newOrigin = view.frame.origin;
    
    CGPoint transition;
    transition.x = newOrigin.x - oldOrigin.x;
    transition.y = newOrigin.y - oldOrigin.y;
    
    //重新设置原来视图位置
    view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
    [view mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(view.superview).offset(view.center.y - transition.y);
        make.leading.equalTo(view.superview).offset(view.center.x - transition.x);
        make.width.mas_equalTo(view.width);
        make.height.mas_equalTo(view.height);
    }];
}

- (void)addRotationAnimation:(__kindof UIView *)view withDuration:(CFTimeInterval)dutation {
    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.toValue = [NSNumber numberWithFloat: -M_PI * 2.0];
    rotationAnimation.duration = dutation;
    rotationAnimation.cumulative = YES;
    rotationAnimation.repeatCount = MAXFLOAT;
    rotationAnimation.removedOnCompletion = NO;
    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}

效果图如下:

锚点动画.gif

三、视图与图层的坐标系

CALayer给不同坐标系之间的图层转换提供了一些工具类方法:

代码语言:javascript
复制
- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;

与此对应的UIView也具有相似的方法如下:

代码语言:javascript
复制
- (CGPoint)convertPoint:(CGPoint)point toView:(nullable UIView *)view;
- (CGPoint)convertPoint:(CGPoint)point fromView:(nullable UIView *)view;
- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
- (CGRect)convertRect:(CGRect)rect fromView:(nullable UIView *)view;

通过这些方法,我们可以把定义在一个图层(或视图)坐标系下的点或者矩形转换为另一个图层(或视图)坐标系下的点或者矩形;开发过程中我们通常操作的对象都是视图,所以下面以视图为例简单演示其用法:首先创建添加两个宽高都是100*100的橙色、紫色视图在控制器的View上,

坐标系.png

使用下面的代码进行测试,结果如下:

代码语言:javascript
复制
CGPoint targetPoint = CGPointMake(10, 10);

CGPoint point1 = [purpleView convertPoint:targetPoint toView:orangeView]; //代码1
CGPoint point2 = [orangeView convertPoint:targetPoint fromView:purpleView];
NSLog(@"\npoint1:%@\npoint2:%@",NSStringFromCGPoint(point1),NSStringFromCGPoint(point2));

/*测试结果:
 point1:{160, 60}
 point2:{160, 60}
 */

代码分析: 这里分别测试了convertPoint的两种用法(convertRect与其相似),我们可以将代码1理解为:参考organView为坐标系时,purpleView上坐标为target的点的坐标值;

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Frame与Bounds的区别
    • 六、中心点(position)与锚点(anchorPoint)
      • 三、视图与图层的坐标系
      相关产品与服务
      腾讯云代码分析
      腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档