如何在同时切换子视图的同时围绕 x 轴翻转 UIView
Posted
技术标签:
【中文标题】如何在同时切换子视图的同时围绕 x 轴翻转 UIView【英文标题】:How to flip a UIView around the x-axis while simultaneously switching subviews 【发布时间】:2012-03-11 15:05:27 【问题描述】:以前有人问过这个问题,但方式略有不同,我无法得到任何答案来按我想要的方式工作,所以我希望有出色的 Core Animation 技能的人可以帮助我。
我的桌子上有一组卡片。当用户向上或向下滑动时,一组卡片会在桌子上上下移动。在任何给定时间,屏幕上都会显示 4 张卡片,但只有第二张卡片显示其正面。当用户刷卡时,第二张卡片会翻转回其正面,而下一张卡片(取决于刷卡方向)会落在其显示其正面的位置。
我已经这样设置了我的卡片视图类:
@interface WLCard : UIView
UIView *_frontView;
UIView *_backView;
BOOL flipped;
我已经尝试使用这段代码翻转卡片:
- (void) flipCard
[self.flipTimer invalidate];
if (flipped)
return;
id animationsBlock = ^
self.backView.alpha = 1.0f;
self.frontView.alpha = 0.0f;
[self bringSubviewToFront:self.frontView];
flipped = YES;
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
;
[UIView animateWithDuration:0.25
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:animationsBlock
completion:nil];
此代码有效,但它有以下我似乎无法弄清楚的问题:
-
x 轴上只有一半的卡片是动画的。
一旦翻转,卡片的正面就会倒置并镜像。
一旦我翻转了卡片,我就无法让动画再次运行。换句话说,我可以根据需要多次运行动画块,但只有第一次才会动画。随后,我尝试制作动画只会导致子视图之间的淡入淡出。
另外,请记住,我需要能够与卡片的表面进行交互。即它上面有按钮。
如果有人遇到这些问题,很高兴看到您的解决方案。更好的做法是为动画添加透视变换,使其更加真实。
【问题讨论】:
【参考方案1】:事实证明这比我想象的要简单得多,而且我不必使用任何 CoreAnimation 库来实现效果。感谢@Aaron Hayman 提供线索。我用transitionWithView:duration:options:animations:completion
我在容器视图中的实现:
[UIView transitionWithView:self
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromBottom
animations: ^
[self.backView removeFromSuperview];
[self addSubview:self.frontView];
completion:NULL];
诀窍是UIViewAnimationOptionTransitionFlipFromBottom
选项。顺便说一句,Apple 在他们的文档中有这段代码。您还可以将其他动画添加到块中,例如调整大小和移动。
【讨论】:
【参考方案2】:好的,这不是一个完整的解决方案,但我会指出一些可能有用的东西。我不是核心动画大师,但我在我的程序中做了一些 3D 旋转。
首先,没有“返回”视图。因此,如果您将某物旋转 M_PI(180 度),您将会像从背面一样查看该视图(这就是它被倒置/镜像的原因)。
我不确定你的意思:
x 轴上只有一半的卡片是动画的。
但是,考虑您的锚点(发生旋转的点)可能会有所帮助。它通常位于中心,但通常您需要它位于其他位置。请注意,锚点表示为比例(百分比/ 100)......所以值是 0 - 1.0f。您只需设置一次(除非您需要更改)。以下是访问锚点的方法:
layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center
动画只运行一次的原因是变换是绝对的,而不是累积的。考虑到您总是从身份转换开始,然后对其进行修改,这很有意义......但基本上,没有动画发生,因为第二次没有动画(视图已经处于您的状态)要求它在)。
如果您要从一个视图动画到另一个视图(并且不能使用[UIView transitionWithView:duration:options:animations:completion:];
),则必须使用两阶段动画。在动画的第一阶段,对于被翻转到背面的“卡片”,您将“向上/向下/随便”的视图旋转到 M_PI_2(此时它将“消失”,或不可见,因为它是旋转的)。在第二阶段,您将视图的背面旋转到 0(这应该是身份转换......也就是视图的正常状态)。此外,您必须对出现的“卡片”(正面)执行完全相反的操作。您可以通过在第一个的完成块中实现另一个[UIView animateWithDuration:...]
来做到这一点。不过我会警告你,这样做可能会有点复杂。特别是因为您希望视图有一个“背面”,这基本上需要对 4 个视图进行动画处理(视图消失、视图显示、视图消失和背面)视图到出现)。最后,在第二个动画的完成块中,您可以进行一些清理(重置旋转的视图并使其 alpha 为 0.0f 等...)。
我知道这很复杂,所以您可能需要阅读一些有关 Core-Animation 的教程。
【讨论】:
谢谢亚伦。 transitionWithView 方法为我提供了解决方案的线索。话虽如此,您确实让我更好地了解了 3D 动画的原理。是时候投资一本优秀的 Core Animation 书籍了。【参考方案3】:@Aaron 有一些你应该阅读的好信息。
最简单的解决方案是使用CATransformLayer,它允许您将其他CALayer 放置在其中并保持其3D 层次结构。
例如,要创建具有正面和背面的“卡片”,您可以执行以下操作:
CATransformLayer *cardContainer = [CATransformLayer layer];
cardContainer.frame = // some frame;
CALayer *cardFront = [CALayer layer];
cardFront.frame = cardContainer.bounds;
cardFront.zPosition = 2; // Higher than the zPosition of the back of the card
cardFront.contents = (id)[UIImage imageNamed:@"cardFront"].CGImage;
[cardContainer addSublayer:cardFront];
CALayer *cardBack = [CALayer layer];
cardBack.frame = cardContainer.bounds;
cardBack.zPosition = 1;
cardBack.contents = (id)[UIImage imageNamed:@"cardBack"].CGImage; // You may need to mirror this image
[cardContainer addSublayer:cardBack];
有了这个,您现在可以将您的转换应用到cardContainer
并拥有一张翻转卡片。
【讨论】:
虽然这种方法会起作用。我需要在卡片上有带有交互元素的 UIView,因为用户需要与卡片面进行交互。使用 CALayer 只允许我处理图像等。 将此层次结构放置在包含UIView
将处理交互...【参考方案4】:
@Paul.s
我在卡片容器上遵循了你的方法,但是当我在卡片容器上应用旋转动画时,只有第一张卡片的一半围绕自身旋转,最后出现整个视图。每次动画中都缺少一侧
【讨论】:
【参考方案5】:基于Paul.s
,这是针对 Swift 3 更新的,并且会沿对角线翻转卡片:
func createLayers()
transformationLayer = CATransformLayer(layer: CALayer())
transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30)
let black = CALayer()
black.zPosition = 2
black.frame = transformationLayer.bounds
black.backgroundColor = UIColor.black.cgColor
transformationLayer.addSublayer(black)
let blue = CALayer()
blue.frame = transformationLayer.bounds
blue.zPosition = 1
blue.backgroundColor = UIColor.blue.cgColor
transformationLayer.addSublayer(blue)
let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap))
view.addGestureRecognizer(tgr)
view.layer.addSublayer(transformationLayer)
制作一个完整的 360 度动画,但由于图层具有不同的 zPosition,因此图层的不同“侧面”将显示
func recTap()
let animation = CABasicAnimation(keyPath: "transform")
animation.delegate = self
animation.duration = 2.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0))
transformationLayer.add(animation, forKey: "arbitrarykey")
【讨论】:
以上是关于如何在同时切换子视图的同时围绕 x 轴翻转 UIView的主要内容,如果未能解决你的问题,请参考以下文章
使用OpenGL在C ++中同时围绕其(x,y&z)轴旋转对象