CALyer解读--总有一个点会让你想起它

2018-06-13  本文已影响0人  八点钟IOS暮雨

1. contentsGravity

表示设置layer上面设置图片的的拉伸方式。和UIImage上面的contentModel一致,只是它设置的类型是NSString类型

 kCAGravityCenter
 kCAGravityTop
 kCAGravityBottom
 kCAGravityLeft
 kCAGravityRight
 kCAGravityTopLeft
 kCAGravityTopRight
 kCAGravityBottomLeft
 kCAGravityBottomRight
 kCAGravityResize
 kCAGravityResizeAspect
 kCAGravityResizeAspectFill         

2.contentsScale

表示当前layer设置的图片像素尺寸和试图大小的比例,可以当前图片在当下屏幕上面一个点显示的像素值。
注意:如果设置了contentsGravity的值为改变寄宿图片拉伸效果会导致设置的contentsScale设置的效果无效

3.contentsRect

表示要在layer区域显示的寄宿图片的区域。contentRect设置的单位坐标,默认为(0,0,1,1),如果我们设置为(0,0,0.5,0.5)时,layer上面显示的图片就是寄宿图片左上角四分之一。

4.contentsCenter

定义一个固定的边框和一个在图层上面可以拉伸的区域。设置的也是单位坐标。注意:此属性我们也是可以在XIB里面设置。stretching对应的

5.shadowOpacity 设置layer的阴影

1.需要设置阴影的时候,必须设置shadowOpacity的值是在0.0到1.0之间,0.0表示完全透明,1.0表示完全不透明
2.设置阴影的另外三个必不可少的方法shadowColor、shadowOffest、shadowRadius
shadowColor表示设置的阴影的颜色,一个CGColorRef对象
shadowOffest表示设置阴影的方向和距离。是一个CGSize的值,宽度控制阴影的位移,高度控制着纵向的位移。默认是{0,-3}为何为负数,因为在MAC OS 上面原点在左下角。所以在MAC OS上面是在下面显示的阴影。
shadowRadius控制着阴影的模糊度,当为0的时候,阴影和layer就会有一个明显的分界线,当值越来越大的时候,就会越来越自然和模糊。

注意:当设置阴影和裁剪的时候,会把阴影的裁剪掉,因为阴影是在layer以外的,而裁剪就是沿着layer边框裁剪,一般就需要在l试图层上面在添加一个试图,使用低试图的阴影,使用上试图的裁剪即可
注意:阴影不是根据边界和圆角路径来确认阴影形状的,而是将寄宿图(包括子试图等)考虑在内,然后通过这些来完美搭配图层形状从而创建一个阴影(也就是根据寄宿图形状来显示阴影,而不是边界),这也是有多个子图层时,计算阴影就会很耗资源的原因

6.layer上面的触摸判断hitTest来判断

我们可以根据触摸点来转换坐标(convertPoint:fromLayer)和包含当下坐标点(containsPoint:)判断是否在当前的layer来响应当下的事件
同样我们可以使用hitTest :来返回触摸点的layer来判断,从而响应事件

 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
 UITouch *touch = touches.anyObject;
 CGPoint point = [touch locationInView:self.contentView];
/**使用hitTest 来判断是否触摸点在那个layer图层上面 如果超过点超过了最外层layer上面,则会返回nil  测试下即使更改底部layer的zPosition让底部layer显示上面后,也是可以触摸到的,和书上面显示的有冲突,不知。。。。*/
 CALayer *layer =  [self.contentView.layer hitTest:point];
 if (layer == self.redLayer) {
     NSLog(@"触摸红色的layer");
}else if (layer == self.blueLayer){
     NSLog(@"触摸蓝色layer");
}
}

7.shadowPath设置阴影的图形

如果设置知道图层的阴影形状切图层层级比较多的时候,我们可以使用shadowPath来设置图层的阴影;

 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:contentLayer.bounds cornerRadius:50];
 contentLayer.shadowPath = path.CGPath;

8.mask遮盖来设置图层显示的形状

`mask`可以理解为遮盖,蒙版,父试图(就是设置`mask`的图层)显示的区域就是`mask`的区域与父图层区域相较的部分,超过的部分就会被`mask`剪切掉。
'' CALayer *layer = [CALayer layer];
'' UIImage *sex36m = [UIImage imageNamed:@"sex36m"];
'' layer.frame = contentLayer.bounds;
'' layer.contentsGravity = kCAGravityCenter;
'' layer.contents = (__bridge id _Nullable)sex36m.CGImage;
'' contentLayer.mask = layer;

9.minificationFilter(缩小图片)和magnificationFilter(放大图片)压缩和拉伸过滤

layer三种拉伸过滤模式
kCAFilterLinear 采用双线性滤波算法,通过对多个像素取样最终生成新的值,得到一个平滑的表现拉伸。当放大的倍数比较大的时候图片就会模糊不清。
kCAFilterTrilinearkCAFilterLinear非常相似,采用的是三线性滤波算法存储多个大小情况下的图片,并三维取样,同时结合大图和小图的存储进而得到最后的结果。
kCAFilterNearest 取样最近的单像素点而不管其他颜色,这样做非常快,也不会是图片模糊,但却是图片马赛克更严重。如果图片颜色分界线比较明显,那么使用此模式比较好。

image

10.layerClass

1.返回一个当前试图寄宿的图层类型,当我们想更换试图的寄宿的图层类型的时候,我们就需要重写这个方法返回试图类型。注意当我们创建试图后,就无法更改它的主寄宿图层,可以通过layerClass来返回一个试图的主寄宿图当然我们也是可以通过添加图层到试图上面,那样我们就浪费了试图创建的主试图,同样子图层也是无法跟踪试图边界的大小,所以就需要我们手动更新子图层边界,所以我们不建议添加子图层的方法来改变试图的显示图层。

11.2D变换

1.`view`上面的`transform`是一个`CGAffineTransform`类型,且`layer`上面与之对应的属性为`affineTransform`,而`layer`上面的`transform`属性是一个`CGTransform3D `类型。`CGAffineTransform`类型是一个2D平面的操作的,也是`CGTransform3D `类型里面沿着Z轴操作。
2.`CGAffineTransformMakeScale(<#CGFloat sx#>, <#CGFloat sy#>)`更改大小的
`CGAffineTransformMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>)`更改位置
`CGAffineTransformMakeRotation(<#CGFloat angle#>)` 旋转角度
3.组合操作
`CGAffineTransformIdentity `创建一个空的`transform`
`CGAffineTransformScale(<#CGAffineTransform t#>, <#CGFloat sx#>, <#CGFloat sy#>)` 基于上一个`transform`在添加改变大小操作
`CGAffineTransformTranslate(<#CGAffineTransform t#>, <#CGFloat tx#>, <#CGFloat ty#>)`基于上一个`transform` 在添加位移操作
`CGAffineTransformRotate(<#CGAffineTransform t#>, <#CGFloat angle#>)` 基于上一个`transform`在添加旋转操作
`CGAffineTransformConcat(<#CGAffineTransform t1#>, <#CGAffineTransform t2#>)`混合两个已经存在的操作,**放在前面的先执行**
**注意:当使用混合`transfoem`操作动画的时候,上一个添加的操作会直接影响到下一个`transform`的,特别是缩放和位移直接按的影响,如:一个view 宽高都是100 ,先执行x,y各缩小50%,再X轴位移100,相对于开始位置实际位移多少?(75)**

12.3D变换

1.3D变换比2D变换多了3d立体感的效果。其实也是使用`透视投影`来实现的
设置`透视投影`的值,就是设置m34的值。一般设置为`-1.0/(500~1000)`看着更想3D的时候,就是你想要的值
2.其实3D和2D的用法基本是一致的,再次我就不啰嗦了。但是还是要介绍两个属性`sublayerTransform`和`isDoubleSided`
3.`sublayerTransform`当我们想让父试图上面多个子试图都进行单独的3D变换的时候,那么我就需要设置各个子试图的`透视投影`,那样看起来很麻烦,简单一点,我就可以设置父试图的`sublayerTransform`属性来设置统一的`透视投影`,为什么要设置统一的`透视投影`,是为了让3D效果更逼真,是让`灭点`统一。
4.`isDoubleSided`来描述图层的背面是否会被绘制,当不需要绘制背面的图像的时候,我们就可以设置为`false`来节约GPU的开销,默认是`true`**注意:其实背面绘制的图和正面刚好产生的镜子效应**
**注意,说的是3d变化,其实并不是一个真正的3D,仅仅是平面绘制的,而是改变了大小加上`透视投影`来产生的一个假的3D效果 想看3DDemo的可以看下[]**

image

13,CAGradientLayer 颜色渐变的layer

1.colors用来显示变化颜色的数组,数组的顺序就是颜色渐变的吮吸
2.locations:表示每个颜色开始变化的数组。注意:值只能递增从0-1,切数组的count必须和colorscount一致
3.startPoint:表示开始颜色开始的位置,和endPoint一起来表示颜色渐变的方向。注意:开始的位置和locations设置的值是相对应的

14.CAReplicatorLayer 高效生成多个相似的layer

1.我可能可以使用CAReplicatorLayer来高效生成多个多个相似的图层。
2.instanceCount:表示图层重复多少次
3.instanceTransform:来制定图层是怎么样的变换,注意:图层的变换是基于上一个图层变换后的位置为基点做变换,而不是第一个图层
4.instanceGreenOffsetinstanceBlueOffsetinstanceRedOffsetinstanceAlphaOffset四个属性,来表示颜色和透明度的变化。

15,隐式动画

1.隐式动画:改变图层(不是试图上面寄宿的图层)上面动画属性所执行的动画(个人总结)
2,calyer上面更改属性(可以做动画的属性)的值,默认都是可以做动画(隐式)改变的,动画的时间默认0.25s
3.这些动画的类型和时间都是根据当前的事务来决定的。事务实际上是Core Animation用来包含一系列属性动画的集合的机制,注意:任何由事务来指定的做动画的属性都不会立马改变,而是只有当事务提交的时候,才可以改变。
4.在每一个runloop周期里面都自动开启一次新的事务,这也就是你不需要手动开启事务的原因。任何一次runloop循环中属性的改变都被集中起来,然后做一个事务时间动画
5.事务由CATransaction类来便是,begin表示开启一个事务,commit提交事务。setAnimationDuration设置事务的时间(也就是动画的时间)setCompletionBlock表示执行完毕动画之后,再执行的事件(block里面的事件的事务和外面你设置的事务是不一致的,当执行完毕之后,设置的事务以及移除栈,所以block执行的就是你当前runloop里面的事务)注意:如果你直接设置当前runloop里面事务的时间,那么就会更改这个runloop里面所有动画的时间

16.隐式动画实现原理

1.当CALyer的属性被修改的时后,它会调用-actionForKey:传递是属性名称
2.图层首先检测它是否有委托,并且是否是实现CALayerDelegate协议制定的-actionForlayer:forkey方法,如果有直接调用返回结果。
3.如果没有委托,或者委托没有实现-actionForLayer:forkey方法,图层接着检查包含属性名称对应行为映射的actions字典
4.如果actions字典没有包含对应的属性,那么图层接着在它的style字典接着搜索属性名称
5.最后如果在style里面也没找到对应的行为,那么图层将会直接调用自定义了每个是属性的标准行为的-defaultActionForKey:方法

补充:大家都知道,直接改变视图上layer的属性是不会做隐式动画的,那是为何?
每一个视图UIView对它关联的图层都扮演了一个委托,并且 提供了-actionLayer:forKey的实现方法。当不在一个动画块的实现中,UIView对所有的图层行为返回nil,但是在动画block(动画block)范围之内,它就会返回一个非空值,这就是为何我们直接作用在试图上面的layer不会由隐式动画

17.呈现图层:

1.当我们操作layer上面动画时,我们更改的属性值是立马改变的,但是给我们显示出来的却是动画改变。其实我们看到的动画改变就是一个呈现图层
2.呈现图层:其实就是图层的赋值,并且记录着图层在屏幕上当下时刻的属性值,我们可以使用'-presentationLayer'来访问。
3.众所周知,iOS屏幕刷新的频率是一秒60次,当我们执行动画的之间大于60分之一秒的时候,Core Animation就会在一次旧值和新值之间,对屏幕上的图层进行重新组织,更改呈现图层属性的值。

补充呈现图层用处:
4.1,一般我们是不会操作或者访问图层的呈现图层的。
4.2如果创建一个定时动画,这个时候就需要准确知道某一时刻图层显示的位置,这个时候我们可以需要获取呈现图层上面属性的位置,来操作动画了
4.3,当我们使用-hitTest来响应图层上面的触摸的时候,我们可以使用呈现图层来判断,例如:如果图层在移动中,来点击移动的图层,那么这个时候呈现试图就会非常有用

fileprivate func wj_testPresentationLayer(_ touches:Set<UITouch>)
    let  touch  = (touches as NSSet).anyObject() as! UITouch;
    let point =  touch.location(in: self.view);
    if((self.animationLayer.presentation()?.hitTest(point)) != nil){
        self.animationLayer.backgroundColor = UIColor.gray.cgColor;
    }else{
        CATransaction.begin();`
        CATransaction.setAnimationDuration(4.0);`
        CATransaction.setCompletionBlock({`
            self.animationLayer.backgroundColor = UIColor.red.cgColor;
        })
        self.animationLayer.position = point;`
        CATransaction.commit();`
    }
}

18视图上面由多个动画的时候,如何区分

1.使用-addAnimation:forKey:根据key我们可以区分不同的动画。如果是相同的动画操作不同的图层呢?
2,我们可以给显示动画通过**KVC**的方法设置对应的图层或者专属值。
3.在代理里面我们就可以直接可以区分图层或者动画了

  animation.setValue("七秒", forKey: "name")
 let anme = anim.value(forKey: "name");
 print(anme);

19 关键帧动画(CAKeyframeAnimation)的rotationMode

1.rotationMode设置为kCAAnimationRotateAuto的时候,我们可以让图层自动调整沿着path设置的曲线的切线方向

20设置图层旋转360°

    `animation.keyPath = "transform.rotation";`
    animation.byValue = (M_PI*2); `
    `transform.rotation` 称之为虚拟属性。

21动画组,也比较简单

    `CAAnimationGroup`动画组有个`animations`数组属性,来添加多个作用在图层上面的动画,添加动画没有前后顺序,一般我们都是根据动画的`beginTime`来区分前后顺序

22 过渡动画

1.当我们需要更改图层的底图或者文本的时候,想动画更改那别不能动画的属性的时候,这是我们就可以使用过渡动画。
2,过渡动画使用CATransition来表示,使用typesubType来表示变换效果,注意是CATransition,而不是事务CATransaction
3.type:表示想要使用那种变化效果,subType:表示效果从那个方向过来。
4.startProgress 开始动画的地方,默认是0
5.endProgress 结束动画的地方,默认是1.0

type:
kCATransitionFade :淡入淡出
kCATransitionMoveIn:从顶部滑动进来
kCATransitionPush: 一侧滑动进来,把旧图推送出去
kCATransitionReveal:把旧图层滑动出去,显示新的
以下为私有API,目前还可以使用:
    cube    立方体
    suckEffect  吸走的效果  
    oglFlip    前后翻转效
    rippleEffect  波纹效果 
    pageCurl   翻页起来  
    pageUnCurl   翻页下来  
    cameraIrisHollowOpen   镜头开  
    cameraIrisHollowClose   镜头关

subType:
kCATransitionFromRight:     从右侧划入
kCATransitionFromLeft:    从左侧滑入
kCATransitionFromTop         往头部方向滑动
kCATransitionFromBottom   往底部方向滑动

注意:过渡动画设置动画的key是一个常量transition,而不是你设置的key,在此我们可以使用kvc来区分动画

切换tabbar加上过渡动画

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    let animation = CATransition.init();
    animation.duration = 4.0;
    animation.type = "oglFlip";
    if viewController.isMember(of: ViewController.self) {
        animation.subtype = kCATransitionFromLeft;
    }else{
        animation.subtype = kCATransitionFromRight;
    }
    self.view.layer.add(animation, forKey: nil);
}

image

大家可以进群学习交流,一起分享更多的面试题和精彩的demo QQ:2673218363 QQ群:725611317

QQ图片20180606202832.jpg
上一篇下一篇

猜你喜欢

热点阅读