【问题标题】:CALayer flattening sublayersCALayer 展平子层
【发布时间】:2014-06-18 19:19:21
【问题描述】:

我正在创建一个 UI,其中我们有一副牌,您可以从屏幕上滑出。

我希望能够做的是创建一个 UIView 的子类来表示每张卡片,然后修改转换属性以将它们向后移动(z 轴)并向上移动一点(y 轴)以获得一副纸牌的样子。

阅读后我发现我需要使用 CATransformLayer 而不是普通的 CALayer,以使 z 轴不会变平。我通过创建一个添加到 CardDeckView 层的 CATransformLayer 对此进行了原型设计,然后将所有卡片添加到该 CATransformLayer。代码看起来有点像这样:

在初始化中:

// Initialize the CATransformSublayer
_rootTransformLayer = [self constructRootTransformLayer];
[self.layer addSublayer:_rootTransformLayer];

constructRootTransformLayer(角度方法是多余的,本来打算给甲板倾斜但后来决定不这样做):

    CATransformLayer* transformLayer = [CATransformLayer layer];
transformLayer.frame = self.bounds;

// Angle the transform layer so we an see all of the cards
CATransform3D rootRotateTransform =  [self transformWithZRotation:0.0];
transformLayer.transform = rootRotateTransform;
return transformLayer;

那么添加卡片的代码如下:

// Set up a CardView as a wrapper for the contentView
RVCardView* cardView = [[RVCardView alloc] initWithContentView:contentView];

cardView.layer.cornerRadius = 6.0;
if (cardView != nil) {
    [_cardArray addObject:cardView];
    //[self addSubview:cardView];
    [_rootTransformLayer addSublayer:cardView.layer];
    [self setNeedsLayout];
}

请注意,我最初想要的只是将 RVCardView 直接添加为子视图 - 我想保留仅添加图层无法执行的触摸事件。不幸的是,最终发生的情况如下:

如果我将卡片添加到 rootTransformLayer,我最终会得到正确的外观:

请注意,我尝试在根视图 (CardDeckView) 上使用 layerClass,如下所示:

+ (Class) layerClass
{
    return [CATransformLayer class];
}

我已经确认根层类型现在是 CATransformLayer,但我仍然得到扁平化的外观。为了防止扁平化,我还需要做什么?

【问题讨论】:

    标签: ios objective-c core-animation calayer


    【解决方案1】:

    当您使用视图时,您会看到一个平坦的场景,因为没有设置透视。要与 3D 图形(如 OpenGL)进行比较,为了渲染场景,您必须设置相机矩阵,将 3D 世界转换为 2D 图像。
    这也是一样的:在 3D 空间中使用 CATransform3D 转换子层内容,但是当父级 CALayer 显示它们时,默认情况下它会将它们投影到 xy 忽略 z 坐标。

    请参阅 Apple 文档中的 Adding Perspective to Your Animations。这是您缺少的代码:

    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / eyePosition; // ...on the z axis
    
    myParentDeckView.layer.sublayerTransform = perspective;
    

    请注意,为此,您不需要使用CATransformLayer,一个简单的CALayer 就足够了:

    这是应用于图片中子视图的转换(eyePosition = -0.1):

    // (from ViewController-viewDidLoad)
    for (UIView *v in self.view.subviews) {
        CGFloat dz = (float)(arc4random() % self.view.subviews.count);
        CATransform3D t = CATransform3DRotate(CATransform3DMakeTranslation(0.f, 0.f, dz),
                                              0.02,
                                              1.0, 0.0, 0.0);
        v.layer.transform = t;
    }
    

    this question 中指出了使用CATransformLayer 的原因。 CALayer“栅格化”其转换后的子层,然后应用自己的转换,而CATransformLayer 保留完整的层次结构并独立绘制每个子层;仅当您有多个级别的 3D 转换 子层时,它才有用。在您的情况下,场景树只有一层:甲板视图(其本身具有单位矩阵作为转换)和卡片视图,即子视图(而是在 3D 空间中移动)。所以CATransformLayer 在这种情况下是多余的。

    【讨论】:

    • 将仔细检查我的代码并验证这是否有帮助。不过很好的答案,真的很有帮助!
    • @Pietro :感谢您的回答。但是,如果我想要透视模式下的视图框架怎么办?我在这种模式下得到错误的帧。
    • @girish_pro “视图框架”是什么意思?我不熟悉这个布局概念。也许您想更改图层的frame property
    • @PietroSaccardi 你能看看我的问题吗? stackoverflow.com/questions/41264038/…
    • 如果我得到 CATransformLayer 的“contents”属性,我会得到一个由所有子层合成的 CGImageRef 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 2011-01-05
    相关资源
    最近更新 更多