layer.contents = (__bridge id)image.CGImage;//如果你没有使用ARC(自动引用计数),你就不需要__bridge这部分。但是,你干嘛不用ARC?!
self.layerView.layer.contentsGravity = kCAGravityResizeAspect;//和cotentMode一样,contentsGravity的目的是为了决定内容在图层的边界中怎么对齐,我们将使用 kCAGravityResizeAspect,它的效果等同于UIViewContentModeScaleAspectFit, 同时它还能在图层中等比例拉伸以适应图层的边界。
contentsScale:它用来判断在绘制图层的时候应该为寄宿图创建的空间大小,和需要显示的图片的拉伸度(假设并没有设置contentsGravity属性为kCAGravityResizeAspect
,如果我们把contentsGravity设置为kCAGravityCenter(这个值并不会拉伸图片),那将会有很明显的变化)。UIView有一个类似功能但是非常少用到的contentScaleFactor属性。如果contentsScale设置为1.0,将会以每个点1个像素绘制图片,如果设置为2.0,则会以每个点2个像素绘制图片;
contentsRect:CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域,它使用了单位坐标,单位坐标指定在0到1之间,是一个相对值(像素和点就是绝对值)。
UIView有一个叫做clipsToBounds的属性可以用来决定是否显示超出边界的内容,CALayer对应的属性叫做masksToBounds,把它设置为YES;
CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域;默认的contentsRect是{0, 0, 1, 1},这意味着整个寄宿图默认都是可见的,如果我们指定一个小一点的矩形,图片就会被裁剪(如图2.6)
翻转的几何结构
常规说来,在iOS 上,一个图层的position位于父图层的左上角,但是在Mac OS上,通常是位于左下角。Core Animation可以通过geometryFlipped属性来适配这两种情况,它决定了一个图层的坐标是否相对于父图层垂直翻转,是一个BOOL类 型。在iOS上通过设置它为YES意味着它的子图层将会被垂直翻转,也就是将会沿着底部排版而不是通常的顶部(它的所有子图层也同理,除非把它们的 geometryFlipped属性也设为YES)。
Z坐标轴
zPosition属性在大多数情况下其实并不常用。除了做变换之外,zPosition最实用的功能就是改变图层的显示顺序了。
通常,图层是根据它们子图层的sublayers出现的顺序来类绘制的,这就是所谓的画家的算法--就像一个画家在墙上作画--后被绘制上的图层将会遮盖 住之前的图层,但是通过增加图层的zPosition,就可以把图层向相机方向前置,于是它就在所有其他图层的前面了(或者至少是小于它的 zPosition值的图层的前面)。
self.greenView.layer.zPosition = 1.0f;
如果我们提高绿色视图的zPosition(清单3.3),我们会发现顺序就反了(图3.9)。其实并不需要增加太多,视图都非常地薄,所以给 zPosition提高一个像素就可以让绿色视图前置,当然0.1或者0.0001也能够做到,但是最好不要这样,因为浮点类型四舍五入的计算可能会造成 一些不便的麻烦。
Hit Testing
第一章“图层树”证实了最好使用图层相关视图,而不是创建独立的图层关系。其中一个原因就是要处理额外复杂的触摸事件。
CALayer并不关心任何响应链事件,所以不能直接处理触摸事件或者手势。但是它有一系列的方法帮你处理事件:-containsPoint:和-hitTest:。
-containsPoint: 接受一个在本图层坐标系下的CGPoint,如果这个点在图层frame范围内就返回YES。如清单3.4所示第一章的项目的另一个合适的版本,也就是使 用-containsPoint:方法来判断到底是白色还是蓝色的图层被触摸了 (图3.10)。这需要把触摸坐标转换成每个图层坐标系下的坐标,结果很不方便。
清单3.4 使用containsPoint判断被点击的图层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView; @property (nonatomic, weak) CALayer *blueLayer; @end @implementation ViewController - (void)viewDidLoad { [ super viewDidLoad]; //create sublayer self.blueLayer = [CALayer layer]; self.blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.blueLayer.backgroundColor = [UIColor blueColor].CGColor; //add it to our view [self.layerView.layer addSublayer:self.blueLayer]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //get touch position relative to main view CGPoint point = [[touches anyObject] locationInView:self.view]; //convert point to the white layer's coordinates point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer]; //get layer using containsPoint: if ([self.layerView.layer containsPoint:point]) { //convert point to blueLayer’s coordinates point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer]; if ([self.blueLayer containsPoint:point]) { [[[UIAlertView alloc] initWithTitle:@ "Inside Blue Layer" message:nil delegate:nil cancelButtonTitle:@ "OK" otherButtonTitles:nil] show]; } else { [[[UIAlertView alloc] initWithTitle:@ "Inside White Layer" message:nil delegate:nil cancelButtonTitle:@ "OK" otherButtonTitles:nil] show]; } } } @end |
图3.10 点击图层被正确标识
-hitTest: 方法同样接受一个CGPoint类型参数,而不是BOOL类型,它返回图层本身,或者包含这个坐标点的叶子节点图层。这意味着不再需要像使用 -containsPoint:那样,人工地在每个子图层变换或者测试点击的坐标。如果这个点在最外面图层的范围之外,则返回nil。具体使用 -hitTest:方法被点击图层的代码如清单3.5所示。
清单3.5 使用hitTest判断被点击的图层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //get touch position CGPoint point = [[touches anyObject] locationInView:self.view]; //get touched layer CALayer *layer = [self.layerView.layer hitTest:point]; //get layer using hitTest if (layer == self.blueLayer) { [[[UIAlertView alloc] initWithTitle:@ "Inside Blue Layer" message:nil delegate:nil cancelButtonTitle:@ "OK" otherButtonTitles:nil] show]; } else if (layer == self.layerView.layer) { [[[UIAlertView alloc] initWithTitle:@ "Inside White Layer" message:nil delegate:nil cancelButtonTitle:@ "OK" otherButtonTitles:nil] show]; } } |
注意当调用图层的-hitTest:方法时,测算的顺序严格依赖于图层树当中的图层顺序(和UIView处理事件类似)。之前提到的zPosition属性可以明显改变屏幕上图层的顺序,但不能改变事件传递的顺序。
这意味着如果改变了图层的z轴顺序,你会发现将不能够检测到最前方的视图点击事件,这是因为被另一个图层遮盖住了,虽然它的zPosition值较小,但是在图层树中的顺序靠前。我们将在第五章详细讨论这个问题。