Autolayout与CATransform3D共同使用引发的问题解决

来源:互联网 发布:mysql主从同步原理 编辑:程序博客网 时间:2024/05/21 09:23

0x00

最近项目中需要实现类似Tinder的翻牌效果。基本思路是自定义一个ContainerView,上面加入几个自定义的卡片(view),监听touch动作,根据手指移动的距离控制卡片的转动角度。手指离开屏幕后,如果移动距离大于门限值,将其remove,并从复用队列中加入新的卡片。

实现起来并不复杂,卡片的布局是使用Autolayout构建的。卡片的翻转通过如下代码实现

view.layer.transform=CATransform3DMakeRotation(angle,0,0,1);

但是在对subView翻转角度时,却产生了一个奇怪的问题。如图1.1所示,在卡片转动的时候,子视图居然出现的了放大的效果。所有的约束设置都没有问题,为什么会出现这种情况呢?


图1.1

通过查官方文档发现,改变transform属性时,实际上已经永久改变的view的位置。而当一个view的角度发生变化时,它的frame值也随之发生变换。



图1.2

图1.2中紫色方框代表一个旋转的view,绿色方框是前者的superView。由图可见,旋转后的view转换到superView后,frame被放大了。而transform属性每一次改变都会触发Autolayout的重绘。因而出现了图1.1所示的效果。

0x01

那么改怎么解决呢?一种方法是使用手码创建卡片视图,用frame写死大小。或者将卡片视图放入一个容器视图中,容器视图固定大小。

但是由于业务需求的问题,这几种方案都不合适。为了实现旋转效果并不改变视图的大小,很容易想到Core Animation。Core Animation动画不会改变视图本身的大小。官方文档是这样描述的:

It achieves this behavior by caching the contents of views into bitmaps that can be manipulated directly by the graphics hardware. 

他实际上操作的是layer维护的bitmaps缓存。现在我们使用CoreAnimation试试效果。

CABasicAnimation* ba = [CABasicAnimationanimationWithKeyPath:@"transform"];

ba.duration=0.15;

ba.toValue= [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(angle,0,0,1)];

[view.layeraddAnimation:baforKey:nil];



图1.3


没错,现在子视图已经不放大了。但还有点小问题,滑动过程中会有点颤抖的感觉。

这是因为动画时间只有0.15秒,当手指放在屏幕上不动时,动画结束,卡片又回到了原来的状态。

CALayer中存在三个tree,他们分别是:

Model Tree

Presentation Tree

Render Tree

Model Tree代表CALayer的真实属性,Presentation Tree对应动画过程中的属性。无论动画进行中还是已经结束,Model Tree都不会发生变化,变化的是Presentation Tree。而动画结束后,Presentation Tree就被重置回到了初始状态。为了让其保持旋转状态,需要在加两句代码:

ba.fillMode=kCAFillModeForwards;

ba.removedOnCompletion=NO;

好了,现在终于不抖了。



图1.4

注意:即使我们设置了removedOnCompletion = NO,其Model Tree依然不变。要回到初始状态只需调用layer的removeAllAnimations方法就可以复原了。

0 0