几个概念
布局
UIView有三个重要的布局属性:frame bounds center;CALayer有frame bounds position.
Frame 代表的是占据父视图的位置和大小,bounds是内部坐标{0,0,width,height},center和position是相对父视图中心点的位置.当操作视图的frame实际上是改变位于视图下方CALayer的frame,不能够独立于图层之外改变的视图的frame.真正改变的frame是根据bounds position transform计算而来,同理改变frame值同样会影响到他们的值.
锚点
anchorPoint就是位于图层的中心点,通过UIView的属性center进行
这里会以一个大家所熟悉的钟表demo来说明
//
// TestView.m
// CGLine
//
// Created by edz on 2020/1/26.
// Copyright © 2020 mende. All rights reserved.
//
#import "TestView.h"
#define kAngleToRadion(angle) ((angle) / 180.0 * M_PI)
@interface TestView ()
{
__weak NSTimer *_timer;
}
@property (nonatomic,strong) CALayer *hourLayer;
@property (nonatomic,strong) CALayer *minuteLayer;
@property (nonatomic,strong) CALayer *secondLayer;
@end
@implementation TestView
- (instancetype)initWithFrame:(CGRect)frame imageName:(NSString *)imageName{
self = [super initWithFrame:frame];
if (self) {
UIImage *image = [UIImage imageNamed:imageName];
self.layer.contents = (__bridge id)(image.CGImage);
CGFloat selfWidth = self.frame.size.width;
//实例化时分秒图层
self.hourLayer = [self layerWithBgColor:[UIColor blackColor] size:CGSizeMake(3, selfWidth/2-40)];
self.minuteLayer = [self layerWithBgColor:[UIColor blackColor] size:CGSizeMake(3, selfWidth/2 - 20)];
self.secondLayer = [self layerWithBgColor:[UIColor redColor] size:CGSizeMake(1, selfWidth/2 - 20)];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeUpdate:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
_timer = timer;
[self buildUI];
}
return self;
}
- (void)releaseTimer{
[_timer invalidate];
_timer = nil;
}
- (void)timeUpdate:(NSTimer *)timer{
[self buildUI];
}
- (void)buildUI{
//获取时间
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *date = [calendar components:NSCalendarUnitSecond|NSCalendarUnitMinute|NSCalendarUnitHour fromDate:[NSDate date]];
NSInteger second = date.second;
NSInteger minute = date.minute;
NSInteger hour = date.hour;
CGFloat perHourMove = 360.0 / 12.0;
CGFloat hourAngle = hour * perHourMove + minute*(1.0/60.0)*perHourMove;
self.hourLayer.transform = CATransform3DMakeRotation(kAngleToRadion(hourAngle), 0, 0, 1);
CGFloat minuteAngle = minute * 360.0 / 60.0;
self.minuteLayer.transform = CATransform3DMakeRotation(kAngleToRadion(minuteAngle), 0, 0, 1);
CGFloat secondAngle = second * 360.0 / 60.0;
self.secondLayer.transform = CATransform3DMakeRotation(kAngleToRadion(secondAngle), 0, 0, 1);
}
- (void)dealloc{
[_timer invalidate];
}
- (CALayer *)layerWithBgColor:(UIColor *)color size:(CGSize)size{
CALayer *layer = [CALayer layer];
layer.backgroundColor = color.CGColor;
layer.anchorPoint = CGPointMake(0.5, 0.9);
//设置position为中心
layer.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
//hour min s的长度
layer.bounds = CGRectMake(0, 0, size.width, size.height);
layer.cornerRadius = 4;
[self.layer addSublayer:layer];
return layer;
}
@end
Z坐标轴
与UIView严格的二维坐标不同,CALayer存在一个三维空间当中,有zPosition,anchorPointZ,当出现三维空间于东和旋转图层的时候需要用到zPosition来改变图层的显示顺序.
Hit Testing
CALayer并不关心任何响应链事件,所以不能直接处理触摸事件和手续,但是它有一系列方法帮你处理事件,-containsPosition 和hitTest
-constainsPoint:接受一个在本图层坐标系下的CGpoint 返回YES
从大到小一点点捕获到触摸点的位置
#import "ViewController.h"
#import "TestView.h"
@interface ViewController ()<CALayerDelegate>
{
CGContextRef _currentContext;
}
@property (nonatomic, strong) UIView *layerView;
@property (nonatomic, strong) CALayer *blueLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
_layerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
[_layerView setBackgroundColor:[UIColor redColor]];
_layerView.clipsToBounds = YES;
[self.view addSubview:_layerView];
self.blueLayer = [CALayer layer];
self.blueLayer.frame = CGRectMake(50, 50, 100, 100);
self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
[self.layerView.layer addSublayer:self.blueLayer];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGPoint point = [[touches anyObject] locationInView:self.view];
point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer];
if ([self.layerView.layer containsPoint:point]) {
point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer];
if ([self.blueLayer containsPoint:point]) {
NSLog(@"蓝色图层触摸到了");
}
else{
NSLog(@"红色图层触摸到了");
}
}
}
-hitTest:接受一个在本图层坐标系下的CGpoint 返回图层本身
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGPoint point = [[touches anyObject] locationInView:self.view];
CALayer *layer = [self.layerView.layer hitTest:point];
if ([layer isEqual:self.blueLayer]) {
NSLog(@"蓝色图层触摸到了");
}else{
NSLog(@"红色图层触摸到了");
}
}
反观用hitTest更加简洁一些