Trying to make a card from CALayers that can flip over
I've been trying to develop a CALayer
based "CardView" object that has two layers that face away from each other to present the two sides of a card. I've been working with things like the doublesided
property of the CALayer
and 开发者_JAVA技巧find the results to be confusing. My base class is CALayer
and I'm adding two sublayers
to it, one with a M_PI/2 transform and both with doublesided = NO
but as it stands, I get the front showing no matter how I rotate the card and the back doesn't show at all. If I don't create the front, the back shows through both sides and the text is in one corner, not 48 point and blurry.
Here's a link to a screenshot showing the card just finishing a full revolution in a UIViewController's
view. You're seeing the back of the front. This should be invisible, and the back should be showing... I think...
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
The trick ended up being the use of CATransformLayer.
This a a layer that's just for containing other layers (it can't have things like backgroundColor
or borderWidth
). It does however maintain the true 3D relationship of its child layers (i.e., it doesn't flatten them like a regular CALayer
). So you can make two layers, flip one and offset its zPosition
just a hair and put them both in a CATransformLayer
and now you can flip this parent layer six ways from Sunday and the child layers stay locked together and always render properly. Pretty sweet and low overhead!
There is a much easier way to do this, as long as you're only trying to do a normal 'flip'.
Look into the UIView animation system, and there is a 'transitionWithView'. You can apply a flip animation there, and it will provide the same effect, with near no work.
I also read many of the versions for UIView. After piecing them all together, this is what I got and it works.
To setup I set
1)the card under interface builder with a view object that contains an UIImageView object which shows the front of the card. so View->View->UIImageView->image file of front of card
2)Then the View object containing UIImageView is changed to UIController Class (so that it responds to touches).
3)In the header file of the viewcontroller, I created IBOutlet UIImageView cardView linked to the UIImageView and a IBAction flipCard linked to the View object (which is now a UIController object) via "touch down“ event。
4) IBAction flipCard is implemented by dynamically creating another subview View object containing another UIImageView, but init with the backside image.
5) using CGRect, the frame and center of the backside object are aligned with the front.
6) then the animation methods of UIView are called to swap the subviews with animation.
And that does the trick.
I am not at my mac right now so I don't have the codes. If anyone is interested, I can post it later.
精彩评论