简体   繁体   中英

CALayer flattening sublayers

I am creating a UI where we have a deck of cards that you can swipe off the screen.

What I had hoped to be able to do was create a subclass of UIView which would represent each card and then to modify the transform property to move them back (z-axis) and a little up (y-axis) to get the look of a deck of cards.

Reading up on it I found I needed to use a CATransformLayer instead of the normal CALayer in order for the z-axis to not get flattened. I prototyped this by creating a CATransformLayer which I added to the CardDeckView's layer, and then all my cards are added to that CATransformLayer. The code looks a little bit like this:

In init:

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

constructRootTransformLayer (the angle method is redundant, was going to angle the deck but later decided not to):

    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;

Then the code to add the cards looks like:

// 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];
}

Note that what I originally wanted was to simply add the RVCardView directly as a subview - I want to preserve touch events which adding just the layer doesn't do. Unfortunately what ends up happening is the following:

展平卡

If I add the cards to the rootTransformLayer I end up with the right look which is:

在此处输入图片说明

Note that I tried using the layerClass on the root view (CardDeckView) which looks like this:

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

I've confirmed that the root layer type is now CATransformLayer but I still get the flattened look. What else do I need to do in order to prevent the flattening?

When you use views, you see a flat scene because there is no perspective set in place. To make a comparison with 3D graphics, like OpenGL, in order to render a scene you must set the camera matrix, the one that transforms the 3D world into a 2D image.
This is the same: sublayers content are transformed using CATransform3D in 3D space but then, when the parent CALayer displays them, by default it projects them on x and y ignoring the z coordinate.

See Adding Perspective to Your Animations on Apple documentation. This is the code you are missing:

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

myParentDeckView.layer.sublayerTransform = perspective;

Note that for this, you don't need to use CATransformLayer , a simple CALayer would suffice:

CALayer可以处理3D投影

here is the transformation applied to the subviews in the picture ( 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;
}

The reason for using CATransformLayer is pointed out in this question . CALayer "rasterizes" its transformed sublayers and then applies its own transformation, while CATransformLayer preserves the full hierarchy and draws each sublayer independently; it is useful only if you have more than one level of 3D-transformed sublayers. In your case, the scene tree has only one level: the deck view (which itself has the identity matrix as transformation) and the card views, the children (which are instead moved in the 3D space). So CATransformLayer is superfluous in this case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM