【问题标题】:Trying to make a card from CALayers that can flip over试图用 CALayers 制作一张可以翻转的卡片
【发布时间】:2025-11-22 07:20:02
【问题描述】:

我一直在尝试开发一个基于CALayer 的“CardView”对象,该对象有两个彼此背对的层以呈现卡片的两侧。我一直在处理CALayerdoublesided 属性之类的东西,发现结果令人困惑。我的基类是CALayer,我添加了两个sublayers,一个带有M_PI/2 变换,两个都带有doublesided = NO,但就目前而言,无论我如何旋转卡片和背面根本不显示。如果我不创建正面,则背面会通过两侧显示,并且文字在一个角落,而不是 48 点和模糊。

这里是一个屏幕截图的链接,它显示了这张卡片刚刚在UIViewController's 视图中完成了一次完整的旋转。你看到的是正面的背面。这应该是不可见的,背面应该是显示出来的……我想……

https://files.me.com/gazelips/lj2aqo


//  CardView.h

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

#define kCardWidth 100.0f
#define kCardHeight 150.0f

@interface CardView : CALayer {
    CALayer *cardFront, *cardBack;
}

@property (nonatomic, retain) CALayer *cardFront, *cardBack;

- (id)initWithPosition:(CGPoint)point;
- (void)addPerspective;
- (CALayer *)createFront;
- (CALayer *)createBack;

@end

//  CardView.m

#import "CardView.h"

@implementation CardView

@synthesize cardFront, cardBack;

static CATransform3D kPerspectiveTransform;

- (id)initWithPosition:(CGPoint)point {
    NSLog(@"--initWithPosition:CardView");
    self = [super init];
    if (self != nil) {
        [self addPerspective];
        self.bounds = CGRectMake(0, 0, kCardWidth, kCardHeight);
        self.position = point;
        self.edgeAntialiasingMask = 0;
        self.backgroundColor = [[UIColor clearColor] CGColor];
        self.borderColor = [[UIColor blackColor] CGColor];
        self.borderWidth = 0.5;
        self.doubleSided = YES;

        cardBack = [self createBack];
        [self addSublayer: cardBack];

        cardFront = [self createFront];
        [self addSublayer: cardFront];
    }
    return self;
}

- (void)addPerspective {
    NSLog(@"--prepare:CardView");
    kPerspectiveTransform = CATransform3DIdentity;
    kPerspectiveTransform.m34 = -1.0/800.0;
    self.transform = kPerspectiveTransform;
}   

- (CALayer *)createFront {
    NSLog(@"--createFront:CardView");
    CALayer *front = [[CALayer alloc] init];
    front.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
    front.position = CGPointMake(kCardWidth / 2, kCardHeight / 2);
    front.edgeAntialiasingMask = 0;
    front.backgroundColor = [[UIColor whiteColor] CGColor];
    front.cornerRadius = 8 * (kCardHeight/150);
    front.borderWidth = 1;
    front.borderColor = [[UIColor grayColor] CGColor];
    front.doubleSided = NO;

    return [front autorelease];
}

- (CALayer *)createBack {
    NSLog(@"--createBack:CardView");
    CALayer *back = [[CALayer alloc] init];
    back.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
    back.position = CGPointMake(kCardWidth / 2, kCardHeight / 2);
    back.backgroundColor = [[UIColor blueColor] CGColor];
    back.contentsGravity = kCAGravityResize;
    back.masksToBounds = YES;
    back.borderWidth = 4 * (kCardHeight/150);
    back.borderColor = [[UIColor grayColor] CGColor];;
    back.cornerRadius = 8 * (kCardHeight/150);
    back.edgeAntialiasingMask = 0;
    back.doubleSided = NO;

    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.font = [UIFont boldSystemFontOfSize:48];
    textLayer.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
    textLayer.position = CGPointMake(50.0f, 75.0f);
    textLayer.string = @"Steve";
    [back addSublayer:textLayer];

    back.transform = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
    return [back autorelease];
}

@end

【问题讨论】:

    标签: objective-c ios core-animation calayer


    【解决方案1】:

    最后的诀窍是使用CATransformLayer. 这是一个仅用于包含其他层的层(它不能包含backgroundColorborderWidth 之类的东西)。然而,它确实保持了其子层的真实 3D 关系(即,它不会像普通的 CALayer 那样将它们展平)。因此,您可以制作两层,翻转一层并将其zPosition 仅偏移一根头发并将它们都放在CATransformLayer 中,现在您可以从周日开始以六种方式翻转此父层,子层保持锁定在一起并始终正确渲染.非常甜蜜且开销低!

    【讨论】:

    • 如果你正在寻找一个很好的教程,这对我很有帮助:invasivecode.tumblr.com/post/29307073330/…
    • 不得不对此表示赞同,不仅因为它是正确的答案,而且因为它使用了“从周日开始的六种方式”
    【解决方案2】:

    有一种更简单的方法可以做到这一点,只要您只是尝试进行正常的“翻转”。

    查看UIView动画系统,有一个'transitionWithView'。您可以在那里应用翻转动画,它会提供相同的效果,几乎没有工作。

    【讨论】:

    • 谢谢!我已经有了一个使用 UIView 的版本(我认为这是#6),我可以在移动它时围绕所有 3 个轴翻转它。我刚刚制作了一个 UIView 并将其图层添加到我的 ViewController 中。但我无法弄清楚双面的东西。
    • 我希望看到您最终采用的解决方案。
    【解决方案3】:

    我还阅读了许多 UIView 的版本。将它们拼凑在一起后,这就是我得到的并且有效。

    设置我设置

    1)界面生成器下的卡片,带有一个视图对象,其中包含一个显示卡片正面的UIImageView对象。 所以View->View->UIImageView->卡片正面的图片文件

    2)然后将包含 UIImageView 的 View 对象改为 UIController 类(以便响应触摸)。

    3)在viewcontroller的头文件中,我创建了IBOutlet UIImageView cardView链接到UIImageView和一个IBAction flipCard链接到View对象(现在是一个UIController对象)通过“touch down”事件。

    4) IBAction flipCard 是通过动态创建另一个包含另一个 UIImageView 的子视图 View 对象来实现的,但使用背面图像进行初始化。

    5) 使用CGRect,将背面对象的边框和中心与正面对齐。

    6) 然后调用 UIView 的动画方法将子视图与动画交换。

    这就是诀窍。

    我现在不在我的 mac,所以我没有代码。如果有人有兴趣,我可以稍后发布。

    【讨论】:

    • 嗨 boobami,我正在对您的代码感兴趣...您可以发布吗?谢谢
    • 已经有一段时间了,让我试着找到它。
    最近更新 更多