资讯专栏INFORMATION COLUMN

图层几何学 -- iOS Core Animation 系列二

Doyle / 1640人阅读

摘要:图层树和寄宿图系列一介绍了图层的基础知识和一些属性方法。的值实际指的是图层旋转之后整个轴对齐的矩形区域。和系列一中提到的类似,用单位坐标来表示默认情况是。可以通过指定和值小于或者大于,使它放置在图层范围之外。

《图层树和寄宿图 -- iOS Core Animation 系列一》介绍了图层的基础知识和一些属性方法。这篇主要内容是学习下图层在父图层上怎么控制位置和尺寸的。

1.布局

首先看一张例图:

对于图上的frameboundscenterpostion的概念我就不赘述了。如果有不明白的自行搜索下了解一下。

frame代表了图层的外部坐标(也就是在父图层上占据的空间),bounds是内部坐标({0, 0}通常是图层的左上角),centerposition都代表了相对于父图层anchorPoint所在的位置

视图的frameboundscenter属性仅仅是存取方法,当操纵视图的frame时,实际上是在改变视图对应的CALayerframe, 不能独立于图层之外改变视图的frame.

如果对图层做了变换,比如旋转缩放等。frame的值实际指的是图层旋转之后整个轴对齐的矩形区域。此时frame的宽高可能和bounds的宽高不一致:


2.锚点

默认来说,anchorPoint位于图层的中点。这个属性没有被UIView直接暴露出来。但是图层的anchorPoint可以被移动。我们可以把anchorPoint置于图层frame的左上角。将会出现下图右侧的情况:

注意上图,改变anchorPointposition的值并没变。

和系列一中提到的contentsRect类似,anchorPoint单位坐标来表示(默认情况是{0.5, 0.5})。可以通过指定x和y值小于0或者大于1,使它放置在图层范围之外。

2.1 示例

为了学习这个anchorPoint属性,下面创建一个闹钟的示例demo。
资源文件我是从原文上截图下来的


创建4个UIImageView并设置好约束(都是居中显示)。


我们用NSTimer来更新闹钟,使用视图的transform属性来旋转钟表。
代码如下:

@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];
  
  self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];

  [self tick];
}
- (void)tick
{
  //获取对应的hours mins seconds
  NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
  NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
  
  CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0;
  CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0;
  CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0;
  //旋转对应的视图
  self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle);
  self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle);
  self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle);
}

运行项目如下图:

除了指针图片的位置,其他的都正常。
可能这时候我们最先想到的方法,是调整对应图片的位置来解决。但是这样的话,你可以试试,并不能解决问题。不用卖关子了。这时候就是要用到anchorPoint的时候。处理代码如下:

// 在viewdidload中添加
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);

运行完美。


3. 坐标系

众所周知,一个图层的position依赖于父图层的bounds,如果父图层移动,所有子图层也会跟着移动。
CALayer也给我们提供了一些获取一个图层的绝对位置的方法,或者相对于另一图层的位置(而不是它当前父图层的位置):

- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
常规来说,一个图层的postion位于父图层的左上角,但在 Mac OS 中,通常位于左下角。
3.1 z坐标轴

UIView的二维坐标不同,CALayer存在于一个三维空间中,它还提供了zPostionanchorPointz属性。
zPosition属性大多数不常用,除了三维动画之外,它最实用的功能是可以改变图层的显示顺序。

3.2 zPosition演示代码

我们演示下改变zPosition会怎么改变视图的显示顺序。
首先我在SB中设置两个视图,如下图:

如果我们不做任何操作,运行后,两个视图显示的顺序就是我们现在设置的这样。但是假如我们对yellowView设置zPosition,哪怕很小的值,都会发现显示的顺序反了。

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *cyanView;
@property (weak, nonatomic) IBOutlet UIView *yellowView;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.yellowView.layer.zPosition = 1.f;
}

现在的显示效果如下:

虽说图层基本没有厚度,但是我们也尽量不要设置zPosition = 0.01f之类的。因为浮点类型的四舍五入可能导致难以察觉的麻烦。

4. Hit Testing

虽说CALayer不关心响应链事件,但是它提供了一些方法让我们处理事件-containsPoint:-hitTest:

4.1 -containsPoint:

-containsPoint:接受一个在本图层坐标系下的CGPoint,如果这个点在图层frame范围内就返回YES.我们可以使用这个方法判断是哪个图层被触摸了。

4.1.1 containsPoint 示例

代码如下:

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *layerView;
@property (nonatomic, strong) CALayer *blueLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
  self.blueLayer = [CALayer layer];
  self.blueLayer.frame = CGRectMake(20.f, 20.f, 100.f, 100.f);
  self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
  
  [self.layerView.layer addSublayer:self.blueLayer];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  // 获取触摸点
  CGPoint point = [[touches anyObject] locationInView:self.view];
  // 转换触摸点在layerView的图层的位置
  point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer];
  // 判断是否包含在layerview里面
  if ([self.layerView.layer containsPoint:point]) {
    point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer];
    if ([self.blueLayer containsPoint:point]) {
      NSLog(@"点击蓝色图层");
    } else {
      NSLog(@"点击了白色图层");
    }
  }
}

运行点击可以在控制台看到NSLog的输出信息。

4.2. -hitTest:

-hitTest:方法同样接受一个CGPoint参数,但是返回的是图层本身,而不是BOOL类型。这使我们不用像-containsPoint:一样每个子图层去测试点击的坐标。如果这个点是在最外面的图层,则返回nil

4.2.1 hitTest示例

把上面-containsPoint:示例的代码下面的部分修改一下即可:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  // 获取点击点
  CGPoint point = [[touches anyObject] locationInView:self.view];
  // 获取这个点所在的图层
  CALayer *layer = [self.layerView.layer hitTest:point];
  if (layer == self.blueLayer) {
    NSLog(@"点击蓝色图层");
  } else if (layer == self.layerView.layer) {
    NSLog(@"点击了白色图层");
  }
}
尝试修改self.layerViewzPosition,会有不同的结果。有兴趣的可以自己测试一下。
-- 系列二完 --

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/17138.html

相关文章

  • 图层树和寄宿图 -- iOS Core Animation 系列

    摘要:提供和两个平行的层级关系,应该也是为了解耦,做职责分离。以便能适应和的系统。因为默认情况下,仍会绘制超过边界的内容,在也不例外。的属性允许我们在图层边框里显示寄宿图的一个子域。因为当图层显示在屏幕上时,不会自动重绘,这和不同。 本系列文章算是一系列读书笔记,想了解更多,请看原文 1.图层树 1.1 视图 一个视图就是在屏幕上显示的一个矩形块(比如图片,文字或者视频),它能够拦截类似于鼠...

    My_Oh_My 评论0 收藏0
  • 视觉效果 -- iOS Core Animation 系列

    摘要:边框绘制在图层边界里面,在所有子图层之前。对上面的示例代码坐下调整运行效果如下如上面的示例的结果一样,边框并不会把寄宿图或子图层的相撞计算出来。属性可以实现组透明,如果设置成,图层和它的子图层会被合成一个整体图片。 本片文章前三章内容大家比较常用,后面的可能会不那么常用,前面的基础内容不想看了可以直接从第4段开始 圆角 conrnerRadius 这个功能还是很常见的,本来不想记了,为...

    idealcn 评论0 收藏0
  • 关于Core Animation动画(上)

    摘要:关于动画上与对于和,大家应该都很熟悉。需要注意的是,所关联的,是禁用了隐式动画的。而称为展现图层,它实际上是的一份拷贝,表示了任意时刻屏幕显示的的真实值。 关于Core Animation动画(上) 1. UIView与CALayer 对于UIView和CALayer,大家应该都很熟悉。通常我们了解到UIView是通过视图树的结构来组织起来的,实际上,UIView管理并维护了另一个图层...

    cjie 评论0 收藏0
  • CALayer 新手指南

    摘要:为了能够缓解这个问题,苹果创建了,它提供了更高级一些的方法,代码量也随之更少。为了使的使用更简单,苹果创建了。当苹果认为中的很多高级高级功能在常规应用中并不总是需要的时候,就诞生了,它提供了中最顶层的图像访问权限。希望你喜欢这个新手指南。 作者:Pranjal Satija,原文链接,原文日期:2016-08-16译者:Cwift;校对:Cee;定稿:CMB 欢迎!这篇文章将教你一项 ...

    Crazy_Coder 评论0 收藏0
  • iOS开发UI篇--iOS动画(Core Animation)总结

    摘要:能够检测动画的执行和结束。关键帧动画中的执行路径过渡动画的动画类型,系统提供了四种过渡动画。关键帧动画和都属于的子类。里面的元素称为关键帧。动画对象会在指定的时间内,依次显示数组中的每一个关键帧可以设置一个让层跟着路径移动。 一、简介 IOS 动画主要是指Core Animation框架。官方使用文档地址为:Core Animation Guide。Core Animation是IOS...

    miracledan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<