
打印动画过程视图的frame,并且增加定时器打印视图的frame
打印frame是动画结束后view和layer的frame,也就是说,执行动画后,view和layer的frame一次性修改到最终值
打开CALayer头文件,发现以下两个属性:
这时我们打印presentationLayer,结果如下:
打印结果证明presentationLayer才是动画执行时变化的视图。
动画的整个过程其实经历了三个树状结构,才显示到屏幕上:模型树-->呈现树-->渲染树
通常我们 *** 作的是模型树。
在重绘周期后,我们会将模型树相关内容(层次结构、图形属性和动画)序列化,通过IPC传递给专门负责屏幕渲染的渲染进程。渲染进程拿到数据并反序列化出树状结构--呈现树。
这个呈现树图层实际上是模型图层的复制,但是它的属性值代表了在任何指定时刻当前外观效果。换句话说,你可以通过呈现图层的值来获取当前屏幕上真正显示出来的值。
我们可以通过CALayer的presentationLayer来访问对应的呈现树图层。
注意:呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候创建,所以在那之前调用-presentationLayer将会返回nil。
-modelLayer方法:在呈现树图层上调用-modelLayer将会返回它正在呈现所依赖的CALayer。通常在一个图层上调用-modelLayer会返回-self(实际上我们已经创建的原始图层就是一种数据模型)。
一个移动的图层是如何通过数据模型呈现的:
大多数情况下,你不需要访问呈现图层,你可以通过和模型图层的交互,来让Core Animation更新显示。
两种情况下presentationLayer呈现图层会变得很有用:
当模型树上带有动画特征是,提交到渲染进程后,渲染进程会根据动画特征,不断修改呈现树上的图层属性,并同时不断的在屏幕上渲染出来,这样我们就看到了动画。
在CALayer内部,它控制着两个属性presentationLayer(以下简称P)和modelLayer(以下简称M)。
P值负责显示,M只负责数据的存储和获取。
我们对layer的各种属性赋值比如frame,实际上是直接对M赋值
而P将在每一次屏幕刷新的时候回到M的状态。
比如此时M的状态是1,P的状态也是1,然后我们把M的状态改为2,那么此时P还没有过去,也就是我们看到的状态P还是1,在下一次屏幕刷新的时候,P才变为2而我们几乎感知不到两次屏幕刷新之间的间隙,所以感觉就是我们一对M赋值,P就过去了。
P就像个瞎子,M就像是瘸子,瞎子背着瘸子,瞎子每走一步(也就是屏幕每次刷新的时候)都要去问瘸子应该怎样走(这里的走路就是绘制内容到屏幕上),瘸子没法走,只能指挥瞎子背着自己走。
重点:动画完成回到原地
可以简单的理解为:一般情况下,任何时刻P都会回到M的状态。
而当一个CAAnumation(以下简称A)加到layer上面后,A就把M从P身上挤下去了,现在P背着的是A,P同样在每次屏幕刷新的时候去问他背着的那个家伙,A就指挥它从fromValue到toValue来改变值。而动画结束后,A会自动被移除,只是P没有了指挥,就只能大喊“M你在哪”,M说我还在原地没动呢,于是P就顺声回到了M的位置。
这就是为什么动画结束后我们看到这个视图又回到了原来的位置,是因为我们看到在移动的是P,而指挥它移动的是A,M永远停在原来的位置没有动,动画结束后A被移除,P就回到了M的怀里。
动画结束后,P会回到M的状态(当然这是有前提的,因为动画已经被移除了,我们可以设置fillMode来继续影响P),但是这通常不是我们动画想要的效果。我们通常想要的是,动画结束后,视图就停留在结束的地方,并且此时我们去访问该视图的属性(也就是M的属性),也应该就是当前看到的样子。按照官方文档的描述,我们的CAAnimation动画都可以通过设置modelLayer到动画结束的状态来实现P和M的同步。
iOS中,实现动画的方式主要分两大类:
CoreAnimation动画:
CoreAnimation动画,即基于事务的动画,是最常见的动画实现方式。动画执行者是专门负责渲染的渲染进程, *** 作的是呈现树presentationLayer。我们应该尽量使用CoreAnimation来控制动画,因为CoreAnimation是充分优化过的:
非CoreAnimation:
非CoreAnimation动画执行者是单签进程, *** 作的是模型树modelLayer。常见的有定时器动画和手势动画。定时器动画是在定时周期触发时修改模型树的图层属性;手动动画是手势事件(比如UIScrollView的didScroll)触发时修改模型树的图层属性。两者都能达到视图随着时间不断变化的效果,即实现了动画。
非CoreAnimation动画过程中实际上不断改动的是模型树,而呈现树紧紧成了模型树的复制品,状态与模型树保持一致。整个过程中,主要是CPU在主线程不断的调整图层属性、布局计算、提交数据,没有充分利用到CoreAnimation强大的动画控制功能。
以上部分关于layer的描述摘自文章 链接
如果你想让你做动画的图层响应用户事件:
你可以使用-hitTest:方法来判断指定图层是否被触摸,这时候对呈现树图层而不是模型图层调用-hitTest:会显得更有意义,因为呈现图层代表了用户当前看到的图层位置,而不是当前动画结束之后的位置。
方案一:直接使用看了CGRectContainsPoint(CGRect rect, CGPoint point)方法,该方法返回一个BOOL值,判断point是否在rect内部,刚好可以穿给presentationLayer的frame和当前点击的point。
方案二:直接使用了hitTest:(CGPoint)p方法,该方法返回一个CALayer对象,如果点击的point在其内部,返回一个layer对象。
这里还要注意一点,使用button时,需要将userInteractionEnabled设置为NO。
从图中我们可以看到,在通过命中测试找到第一响应者之后,会将 UITouch 分发给 UIResponder 的 touches 系列方法,同时也会分发给手势识别系统,让这两个处理系统同时工作。
首先要注意的是,上图中蓝色部分的流程并不会只执行一次,举例来说:当我们用一根手指在一个视图上缓慢滑动时,会产生一个 UITouch 对象,这个 UITouch 对象会随着你手指的滑动,不断的更新自身,同时也不断地触发 touches 系列方法。一般来说,我们会得到如下类似的触发顺序:
UITouch 的 gestureRecognizers 属性中的存储了在寻找第一响应者的过程中收集到的手势,而在不断触发 touches 系列方法的过程中,手势识别系统也在在不停的判断当前这个 UITouch 是否符合收集到的某个手势。
当手势识别成功: 被触摸的那个视图,也就是第一响应者会收到 touchesCancelled 的消息,并且该视图不会再收到来自该 UITouch 的 touches 事件。同时也让该 UITouch 关联的其他手势也收到 touchesCancelled,并且之后不再收到此 UITouch 的 touches 事件。这样做就实现了该识别到的手势能够独占该 UITouch。具体表现参考如下:
当手势识别未成功: 指暂时未识别出来,不代表以后不会识别成功,不会阻断响应链。注意这里指的是未成功,并不一定是失败。在手势的内部状态中,手势大部分情况下状态是 possible,指的是 UITouch 暂时与其不匹配,但之后可能有机会识别成功。而 fail 是真的识别失败,指的是以目前的触摸情况来看已经不可能是这个手势了,并且在下个 runloop 会从 gestureRecognizers 中移除该手势。
下面举个简单的例子模拟一下响应链和手势的相互影响。现在用一根手指,在一个视图上触摸并滑动一段距离。下图给出了视图不带手势的情况,和带一个 UIPanGestureRecognizer 手势的情况。
从图中我们可以看到,当不带手势的情况下,手指按下去的时候,响应者的 touchBegan 方法会触发,随着手指的移动,touchMoved 会不断触发,当手指结束移动并抬起来的时候,touchEnded 会触发。在这个过程中,我们接收到一直是一个不断更新的 UITouch。
在该视图有添加一个 UIPanGestureRecognizer 手势的情况下,我们多了下方这一条来表示与响应链同时工作的手势识别系统,可以看到手势识别系统也是在手指按下去那一刻就开始工作的,前半段处于一直正在识别的状态。在我们拖动了很小一段距离之后(注意这时候我们的手指还没抬起), 手势识别系统确定了该 UITouch 所做的动作是符合 UIPanGestureRecognizer 的特点的,于是给该视图的响应链发送了 touchCancelled 的信息,从而阻止这个 UITouch 继续触发这个视图的 touches 系列方法(同时也取消了别的相关手势的 touches 系列方法,图中未体现)。在这之后,被调用的只有与手势关联的 target-action 方法(也就是图中的墨绿色节点 call PanFunction)。
为了的美观和易读,在中我隐去了不少细节,在此列出:
参考链接:
链接1
链接2
链接3
链接4
WMS添加window过程,最后会执行到sessionwindowAddedLocked
创建 SurfaceSession 对象,并将当前 Session 添加到 WMSmSessions 成员变量
SurfaceSession 的创建会调用 JNI,在 JNI 调用 nativeCreate()
构造了一个SurfaceComposerClient对象。并返回它的指针。这个对象一个应用程序就有一个,它是应用程序与SurfaceFlinger沟通的桥梁
通过SurfaceFlinger创造了一个Client对象,每一个APP都有一个Client对象向对应,通过这个代理对象可以跟SurfaceFlinger通信
构造了一个Client对象,Client实现了ISurfaceComposerClient接口。是一个可以跨进程通信的aidl对象。除此之外它还可以创建Surface,并且维护一个应用程序的所有Layer
一个ViewRootImpl就对应一个Surface
直接看ViewRootImpl的绘制流程
winAnimatorcreateSurfaceLocked实际上是创建了一个SurfaceControl。即上面是先构造SurfaceControl,然后在构造Surface
通过SurfaceControl的构造函数创建了一个SurfaceControl对象,这个对象的作用其实就是负责维护Surface,Surface其实也是由这个对象负责创建的
创建时传入了一个对象 sp<IGraphicBufferProducer> gbp, 后面会说吗应用所渲染的每一帧,实际上都会添加到IGraphicBufferProducer中,来等待SurfaceFlinger的渲染
Client将应用程序创建Surface的请求转换为异步消息投递到SurfaceFlinger的消息队列中,将创建Surface的任务转交给SurfaceFlinger,因为同一时刻可以有多个应用程序请求SurfaceFlinger为其创建Surface,通过消息队列可以实现请求排队,然后SurfaceFlinger依次为应用程序创建Surface
该函数中根据flag创建不同的Layer,Layer用于标示一个图层。
除了SurfaceFlinger需要统一管理系统中创建的所有Layer对象外,专门为每个应用程序进程服务的Client也需要统一管理当前应用程序进程所创建的Layer,因此在addClientLayer函数里还会通过Client::attachLayer将创建的Layer和该类对应的handle以键值对的方式保存到Client的成员变量mLayers表中
SurfaceFlinger为应用程序创建好Layer后,需要统一管理这些Layer对象,因此通过函数addClientLayer将创建的Layer保存到当前State的Z秩序列表layersSortedByZ中,同时将这个Layer所对应的IGraphicBufferProducer本地Binder对象gbp保存到SurfaceFlinger的成员变量mGraphicBufferProducerList中
上面完成了 surfaceController的创建跟踪,下面分析从surfaceController获取surface过程
JNI构建方法获取到一个指针,
创建一个native层的Surface对象,并将该对象指针返回给Java层的Surface,从而建立Java层的Surface和native层Surface的关联关系
另外Surface和SurfaceControl都持有mGraphicBufferProducer用于 *** 作位于SurfaceFlinger中的图形buffer
推荐阅读: 图形系统总结
CAD中常用的命令和变量
CAD的命令大多数与菜单命令和对话框中的参数相对应,有时输入命令比到对话框中去寻找参数或在下拉菜单中找命令更快捷,但也有一些命令可能菜单、对话框中找不到。这里把一些我认为比较有用的命令记录下来,希望对大家有所帮助。
scaletext:缩放文字。浩辰CAD 2006版就加入了的命令,可以对单行文字、多行文字和属性文字整体进行缩放,在缩放时可以选择文字的对齐方式,方便调整文字大小和位置。如对一组文字进行缩小到07倍,且不改变已有每个文字的定位点(下图每个Text是左点定位的)。
layuniso:
取消图层隔离。当对某个图层进行图层隔离 *** 作后,可以使用此命令恢复到图层隔离前的图层状态。如果在图层隔离前所有图层都是打开状态,直接使用打开所有图层(layopa)或layon命令将所有图层打开,如果在图层隔离前已经有一些图层被关闭,用这个命令就比较方便了。
layerpmode:
上一图层模式。在工具栏中有“上一个图层”(LayerP)按钮,此命令可以恢复成之前的图层状态,对于经常要开关处理图层的设计师非常有用。但要使用此功能,必须先用LayerPmode命令打开上一图层模式,只有打开此命令后,浩辰CAD才会记录图层状态,才能返回上一图层状态。
isavebak:
此变量控制保存是否生成备份文件(BAK),在“选项”对话框的“打开和保存”选项卡中有对应的的选项。
pickadd:
累加选择选项。当设置为1时,通过单击可以累加选择多个对象,设置为0时单击只能选中当前对象,之前选择的对象会被取消。属性框的上方有一个按钮可以切换此状态,当显示加号时表示此变量为1,显示为1时表示只能进行单选。“选项”对话框中也有相应的选项。但用命令来调整更简单。
pickstyle:
在“选项”对话框中有两个选项“对象编组”和“填充关联”,此变量记录了这两个选项的状态。“填充关联”选项很少使用,通常是关闭状态。“对象编组”则需要了解一下,当创建一个组(group)后,打开“对象编组”时组在选择时会作为一个整体,而关闭此选项时组内对象可以单独被选中。在浩辰CAD平台的电气软件中线缆是用组来生成的,用户如果意外将“对象编组”选项关闭,会发现线缆全是散的,被分成了一段段线和一些文字,给用户 *** 作带来很多麻烦,但有的设计师想对线缆用平台命令进行编辑时却难以 *** 作。使用PICKSTYLE命令就可以很好解决这个问题,当需要对组内对象进行编辑时,将此变量设置为0,当需要将组作为一个整体时,将此变量设置为1。
viewres:
此参数用于控制圆弧和圆的平滑度。在CAD中圆或弧都是按多边形显示的,显示的平滑度取决与显示的边数多少。浩辰CAD的viewres的默认设置为1000,软件会根据圆在视图中的大小决定使用的边数,但当一个圆从很小一下放到很大时,会看到圆会显示为多边形,这种情况使用RE命令重生成一下就可以恢复正常。如果此参数设置得比较大时,由于采用的边数相对较多,放大和缩小时圆的显示效果都相对较好,不必反复的RE,但这样会降低显示速度。对于一些制造业用户,图纸不大,圆又比较多时,可以将此参数适当增大。此参数在选项对话框中也可以设置,最大值为:20000,通常不建议这样设置。
dimassoc:
控制标注关联的参数。当有如果发现画出的标注不是一个整体,是散的线和文字,就知道可能这个变量被调整了,说明此变量被设置为0了。当此变量设置为0时,画出来的标注就是散的,但不影响之前画好的完整标注。此参数设置为1时,标注是整体,但不与图形关联,设置为2时,标注与图形关联。
toolbar:
工具栏配置对话框,这个命令可以一次性地打开或关闭多个工具栏,不必像右键菜单那样一个个的处理,另外,当用户将所有工具栏都关闭后,用这个命令可以将工具栏重新打开是最好的办法。
fill(fillmode):
pline:控制填充空心与否。On为空心,off为实心。在浩辰CAD中PL线是否实心不受影响。如果用户在任意图中填充后填充不显示,就需要注意一下是否此参数被关闭了。打开此参数,RE一下,填充就会出来了。
filedia:
打开保存文件的对话框是否出现。On为出现,off为不出现。如果打开保存文件时只有命令行提示,而没有出现对话框,就要检查此一下此变量的设置。
startup:
启动时是否显示启动对话框。On为显示,off为不显示。
pickauto:
选择对象方面是否可以框选。On为不可以框选只能用鼠标点取,off是可以。
attdisp:
属性块属性显示模式。On显示,off关闭。
aperture:
以像素为单位设置对象捕捉靶框的显示尺寸。 输入值(1 至 50)。数值越高,靶框越大。也可以更改“选项”对话框的“草图”选项卡上的此设置。APERTURE 控制对象捕捉靶框,而不是“选择对象”提示下显示的拾取框。对象选择拾取框由 PICKBOX 系统变量控制。
blipmode:
点标记。打开此参数在绘制图形时会在单击处留小点标记,RE后可消除。在浩辰CAD中此参数是默认关闭状态,如果在绘图时发现鼠标点过后图面上会遗留一些点标记,就要检查一下此变量是否打开。
cursorsize:
按屏幕大小的百分比确定十字光标的大小。有效设置的范围为 1% 到 100% 。 当设置为 100% 时,十字光标为全屏显示且看不到十字光标的末端。当设置值小于 100% 时,光标移到屏幕边缘时,可以看到十字光标的末端。浩辰CAD默认设置为5%,很多用户习惯于100%的设置,可以在选项对话框中设置此参数,也可以用此命令设置,当然这个参数设置一次就可以了。
gridunit:
指定当前视口栅格间距(X,Y方向)。可通过右键单击“栅格”按钮,在草图设置对话框中设置。
ltscale:
设置全局线性比例因子。也可在线型资源管理器对话框里设置。
mirrtext:
镜像文字时文字是否反转,默认为镜像时文字方向和顺序不变。
pellipse:
创建真正的椭圆对象/创建用多段线组成的椭圆。
pickfirst:
控制是否可以在执行命令前选择对象。打开此参数时,可以先选对象,再执行命令,关闭此变量后,必须执行命令后再选对象。
polarang:
设置极轴角增量。
textfill:在AUTOCAD中控制打印和渲染时 TrueType 字体的填充方式。以轮廓线形式显示文字/以填充图像形式显示文字。浩辰CAD中此参数暂时不起作用,如果你希望打印空心的TTF文字,可以在打印的特性对话框中关闭TrueType 字体的填充。
textsize:
设置用当前文字样式绘制的新文字对象的默认高度。如果当前文字样式具有固定的高度,那么 TEXTSIZE 将无效。
thickness:
设置当前的三维厚度,设置此参数后,绘制的有厚度参数的图形如PL线、样条线将使用此厚度。
tooltips:
是否显示工具栏提示。
plinewid:
设置Pl线的默认宽度。如果你在绘制PL线时默认就带宽度,肯定是这个参数不再是默认值0了。
elevation:
设置z坐标初始值。如果你画的图形Z坐标不为0,肯定是不小心设置了这个参数,将他设置为0就可以。
还有很多的类似命令,但有一些在状态栏单击或用快捷键更方便,也就不在此列举了。
;/
@see 鼠标点击拖拽的效果(页面可以同时拖动多个框)
@param boxId 整个对象(框)的id(一般为div或table)
@param event 内置对象(必须传入)
/
function mousePlead1(event, boxId) {
var o = getO(boxId);
var isIE = documentall true : false;
var e = event;
var x = eoffsetX || elayerX;
var y = eoffsetY || elayerY;
documentonmousemove = function(e) {
ostylefilter = 'Alpha(opacity=70)';
ostyleopacity = '07';
if (isIE) {
osetCapture();
} else {
windowcaptureEvents(EventMOUSEMOVE);
}
var e = windowevent || e;
if (eclientX - x >= 0 && eclientY - y >= 0 && eclientX - x <= getWinSize()[0] - getO(boxId)offsetWidth
&& eclientY - y <= getWinSize()[1] - getO(boxId)offsetHeight) {
ostyleleft = (eclientX - x) + "px";
ostyletop = (eclientY - y) + "px";
}
};
documentonmouseup = function(e) {
documentonmousemove = function() {
};
if (isIE) {
oreleaseCapture();
} else {
windowreleaseEvents(oMOUSEMOVE);
}
ostylefilter = "";
ostyleopacity = "";
};
}
/
@see 获得对象
@param id 对象的id(表单元素和其他标签都可以)
@return Object
/
function getO(id) {
return documentgetElementById(id);
}
/
@see 获得当前窗体的大小(width,height)
@return Array
/
function getWinSize() {
var width = parseInt(documentdocumentElementclientWidth);
var height = parseInt(documentdocumentElementclientHeight);
return new Array(width, height);
}
使用layerd出框使用ajax方法:
1你可以自定义按钮,写在你的页面里面
2使用 layer 的按钮的话,按钮的回调函数的参数中有两个参数,一个是d层的对象,一个是d层的 index,你可以使用这个d层的对象读取d层中的数据,比如 layerofind('xxx')val()
1GameObjectFind()
通过场景里面的名子或者一个路径直接获取游戏对象。
GameObject root = GameObjectFind(“GameObject”);
我觉得如果游戏对象没再最上层,那么最好使用路径的方法,因为有可能你的游戏对象会有重名的情况,路径用“/”符号隔开即可。
GameObject root = GameObjectFind(“GameObject/Cube”);
GameObjectFind()使用起来很方便,但是它有个缺陷如下图所示,就是如果你的这个GameObject天生acive = false的话。那么你用GameObjectFind()是永远也无法获取它的对象的。如果对象都获取不到,那么对象身上脚本啊 组件啊啥的都是获取不到的,变成了没有意义的对象。
就这个问题我查过很多资料,最终也无果。。但是我用另外一个巧妙的办法可以解决它。(后面详解)或者你也可以提前把所有的游戏对象保存在内存中。
GameObjectFind()方法在游戏中的使用频率很高。但是它也很消耗性能,你可以想想它的原理肯定也是用类似递归的形式来做的,那么我们就要尽量更少的调用GameObjectFind()方法,可以把获取的游戏对象,保存在内存里,这是再好不过的选择了。 尤其是在Update方法中不要去 Find()游戏对象!!
2 TransformFind()
还记得上面我说过用GameObject无法获取天生acive = false的游戏对象,如果你用TransformFind()的话就可以很好的获取,另外Unity还提供了一个TransformFindChind()的方法,这个方法未来会被unity废弃,大家最好就别用了,用TransformFind()可以取代。
如下代码,我们先获取顶级对象root 。接着用Find()去找它的子节点”xxxx”的对象,无论”xxxx”对象是否active = true 都是可以直接找到对象的。
值得注意的是,unity规定了比如父节点active = true 并且子节点的 active = true 都满足的情况下 才能全部显示。使用TransformFind()可以很方便的获取游戏对象,因为有了游戏对象,那么它身上的脚本啊组件啊什么的都可以很方便的获取到。
但是TransformFind()必须要保证你的顶级父对象的activity = true。举个例子,你做了一个场景有一些地图你在场景里面预先activie = false了, 你希望在游戏中的某个时间点把它们都打开 setActive(true)
你可以把“map”节点放在一个active = true的GameObject上,无论是关闭 或者 显示 代码中写起来都很方便。 假如你的map节点就是顶级节点,那么它一旦天生acive = false ,那么你将无法得到它的对象,更无法设置它的属性了。
GameObject root = GameObjectFind(“GameObject”);
GameObject map = roottransformFind(“map”)gameObject;
mapSetActive(true);
3 unity 还提供了几个获取游戏对象的方法,但是我个人觉得使用频率不高,这里也简单说两句。
GameObjectFindGameObjectsWithTag(“tag”)
GameObjectFindWithTag(“tag”)
根据一个标记来获取游戏对象,返回一个 或者 一个数组,我个人觉得这个两个方法没啥用,因为既然需要用到标记那么相比这个游戏对象必然是非常特殊的一个,所以我会把它存在内存中。
ObjectFindObjectOfType
ObjectFindObjectsOfType
ResourcesFindObjectsOfTypeAll
根据一个类型返回Object,比如 GameObject 、Texture、Animation 、甚至还可以是你自己写的一个脚本 的范型。它找起来很方便,可以返回一个 或者一个数组。 我觉得这几个方法其实游戏中也没啥用,不过在编辑器中使用的确实很频繁,比如你要做批量检查场景的工具,查找场景中有没有使用某个特殊类型的对象。 或者查看内存的占用量,看看当前内存中那些Texture没有被释放掉。 等等。
还有一个方法,如果你知道自对象的索引,还可以用下面的方法来获取,参数是index的索引。
transformGetChild(0)
layer tree 分为 model layer tree(模型图层树) 、presentation layer tree(表示图层树) 、render layer tree(渲染图层树)
这三种图层树有什么作用呢?说到有啥作用,就不得不提Core Animation 核心动画了。因为这三个图层在核心动画中才能显示出它们的特点和用处。下面是官方文档的说明:
有没有感觉看了官方文档的说明还是有点点小懵逼的。那我就用我的理解再解释下。这三个图层在CALayer中可以使用的属性有两个,分别是: modelLayer (模型图层)、 presentationLayer (表现图层)。渲染图层在CALayer没有提供直接的属性给我们使用,是core Animation私有的。这里就不用说它了。
一说到"模型"大家第一反应是什么?是不是用来装数据用的,是不是会想到对象模型的概念。"模型图层" 其实就是这个作用,就是创建一个layer 然后给这个layer赋上你需要的数据。大家是不是在创建layer后,都会给生成的layer 赋值各种属性,通过这种赋值,有没感觉到这个layer本身就是modelLayer呢?那我告诉大家,必须的、必须的、必须的。重要的事情说三边。。。 layer = layermodelLayer (后面的栗子会证明这点)
"表现图层" 就是当前显示在屏幕上的图层。屏幕刷新时,就会调用presentationLayer。在core animation 动画中,可以通过这个属性,获取动画过程中每个时刻动画图层的数据,这样如果在动画过程中需要做什么处理,就可以动态的获取layer上相关的数据了。动画的过程中presentationLayer是时刻变化的,而modelLayer是不会变的。
使用basicAnimation 改变positionx的值
通过打印selfcontainerViewlayermodelLayer 和 selfcontainerViewlayerpresentationLayer的frame值对比:
第一组值:
layerpresentationLayerframe = {{ 15375329732894897 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
第二组值:
layerpresentationLayerframe = {{ 15470316648483276 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
第三组值:
layerpresentationLayerframe = {{ 17095646381378174 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
第四组值:
layerpresentationLayerframe = {{ 18573218822479248 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
第五组值:
layerpresentationLayerframe = {{ 20187994480133057 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
第六组值:
layerpresentationLayerframe = {{ 20441292762756348 , 100}, {100, 467}}
layermodelLayerframe = {{1375, 100}, {100, 467}}
通过六组值的观察,可以看出,动画一开始到结束,presentationLayer的frame一直在改变,而modelLayer的frame一直没变过,为什么presentationLayer的frame会发生变化呢?因为做动画的时候改变了layer的positionx值,position值的改变,会影响frame的值。
从这里我们就可以看出modelLayer 和 presentationLayer本质区别了,modelLayer 负责存储动画的目标值的模型对象。每当更改图层的属性时,它都会把数据存储下来 。当动画开始执行时,presentationLayer就上场了。屏幕上显示的就是presentationLayer,动画的过程中,你可以时刻访问动态变化的数据。例如:视频中的滚动d幕如果是使用layer做动画的,当d幕正在滚动时,你需要点击它以处理需要做的事情,这时候你就会需要presentationLayer。再结合hintTest方法来做判断:
从这个截图可以看出 selflayer = selflayemodelLayer 、 selflayer != selflayerpresentationLayer。 就是layer本身其实就是一个模型layer,只不过它拥有 presentationLayer。
CALayer是Core Animation的基础,layer tree更是Core Animation 核心动画执行过程中直接使用的对象,了解清楚layer的内在层级关系,才能更好的从细节上处理核心动画相关的事情。
iOS中所有view都是用底层的layer来驱动的。view 和它的 layer 之间有着紧密的联系,view 其实直接从 layer 对象中获取了绝大多数它所需要的数据。在 iOS 中也有一些单独的 layer,比如 AVCaptureVideoPreviewLayer 和 CAShapeLayer ,它们不需要附加到 view 上就可以在屏幕上显示内容。两种情况下其实都是 layer 在起决定作用。当然了,附加到 view 上的 layer 和单独的 layer 在行为上还是稍有不同的。
UIView 相比 CALayer 最大区别是 UIView 可以响应用户事件,而 CALayer 不可以。 UIView 侧重于对显示内容的管理, CALayer 侧重于对内容的绘制。
万物归根, UIView 和 CALayer 的老祖都是 NSObjet 。
UIView的继承结构为: UIResponder : NSObject。
UIResponder 是用来响应事件的,也就是 UIView 可以响应用户事件。
CALayer 的继承结构为: NSObject 。
直接从 NSObject 继承,因为缺少了 UIResponder 类,所以 CALayer 不能响应任何用户事件。
CALayer 定义了 position 、 size 、 transform 、 animations 等基本属性。
UIView 可以响应事件, Layer 不可以
UIKit 使用 UIResponder 作为响应对象,来响应系统传递过来的事件并进行处理。 UIApplication 、 UIViewController 、 UIView 、和所有从 UIView 派生出来的 UIKit 类(包括 UIWindow )都直接或间接地继承自 UIResponder 类。
在 UIResponder 中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject ,并没有相应的处理事件的接口。
下面列举一些处理触摸事件的接口
以上就是关于button在动画过程中点击无响应全部的内容,包括:button在动画过程中点击无响应、Android系统_Surface创建过程分析、CAD中常用的命令和变量等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)