Source/GGBLayer.m
author Jens Alfke <jens@mooseyard.com>
Thu May 29 15:04:06 2008 -0700 (2008-05-29)
changeset 9 a59acc683080
parent 8 45c82a071aca
child 10 6c78cc6bd7a6
permissions -rw-r--r--
Finally fixed the slow animation performance of board games; all it took was changing the board's z index from 1 to 0, somehow. Games working well now.
jens@1
     1
//
jens@1
     2
//  GGBLayer.m
jens@1
     3
//  GGB-iPhone
jens@1
     4
//
jens@1
     5
//  Created by Jens Alfke on 3/7/08.
jens@1
     6
//  Copyright 2008 __MyCompanyName__. All rights reserved.
jens@1
     7
//
jens@1
     8
jens@1
     9
#import "GGBLayer.h"
jens@1
    10
#import "QuartzUtils.h"
jens@1
    11
jens@1
    12
jens@1
    13
@implementation GGBLayer
jens@1
    14
jens@1
    15
jens@1
    16
- (NSString*) description
jens@1
    17
{
jens@1
    18
    return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
jens@1
    19
}
jens@1
    20
jens@1
    21
jens@4
    22
- (void) redisplayAll
jens@4
    23
{
jens@4
    24
    [self setNeedsDisplay];
jens@4
    25
    for( CALayer *layer in self.sublayers )
jens@4
    26
        if( [layer isKindOfClass: [GGBLayer class]] )
jens@4
    27
            ((GGBLayer*)layer).redisplayAll;
jens@4
    28
        else
jens@4
    29
            [layer setNeedsDisplay];
jens@4
    30
}
jens@4
    31
jens@4
    32
jens@7
    33
/*
jens@7
    34
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key 
jens@7
    35
{
jens@7
    36
    NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
jens@7
    37
    [super addAnimation: anim forKey: key];
jens@7
    38
}
jens@7
    39
*/
jens@7
    40
jens@7
    41
jens@7
    42
- (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
jens@7
    43
{
jens@7
    44
    //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08
jens@7
    45
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath];
jens@7
    46
    anim.duration= duration;
jens@7
    47
    anim.fromValue = from;
jens@7
    48
    anim.toValue = to;
jens@7
    49
    anim.isRemovedOnCompletion = YES;
jens@7
    50
    anim.delegate = self;
jens@7
    51
    [self addAnimation:anim forKey: @"animateAndBlock:"];
jens@7
    52
    _curAnimation = (id)[self animationForKey: @"animateAndBlock:"];
jens@7
    53
    [self setValue: to forKeyPath: keyPath];    // animation doesn't update the property value
jens@7
    54
jens@7
    55
    // Now wait for it to finish:
jens@7
    56
    while( _curAnimation ) {
jens@7
    57
        [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode
jens@7
    58
                                 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
jens@7
    59
    }
jens@7
    60
}
jens@7
    61
jens@7
    62
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
jens@7
    63
{
jens@7
    64
    if( anim==_curAnimation ) {
jens@7
    65
        _curAnimation = nil;
jens@7
    66
    }
jens@7
    67
}
jens@7
    68
jens@7
    69
jens@8
    70
#if TARGET_OS_IPHONE
jens@1
    71
jens@1
    72
#pragma mark -
jens@1
    73
#pragma mark IPHONE VERSION:
jens@1
    74
jens@1
    75
jens@1
    76
- (id) copyWithZone: (NSZone*)zone
jens@1
    77
{
jens@1
    78
    GGBLayer *clone = [[[self class] alloc] init];
jens@1
    79
    clone.bounds = self.bounds;
jens@1
    80
    clone.position = self.position;
jens@1
    81
    clone.zPosition = self.zPosition;
jens@1
    82
    clone.anchorPoint = self.anchorPoint;
jens@1
    83
    clone.transform = self.transform;
jens@1
    84
    clone.hidden = self.hidden;
jens@1
    85
    clone.doubleSided = self.doubleSided;
jens@1
    86
    clone.sublayerTransform = self.sublayerTransform;
jens@1
    87
    clone.masksToBounds = self.masksToBounds;
jens@1
    88
    clone.contents = self.contents;                 // doesn't copy contents (shallow-copy)
jens@1
    89
    clone.contentsRect = self.contentsRect;
jens@1
    90
    clone.contentsGravity = self.contentsGravity;
jens@1
    91
    clone.minificationFilter = self.minificationFilter;
jens@1
    92
    clone.magnificationFilter = self.magnificationFilter;
jens@1
    93
    clone.opaque = self.opaque;
jens@1
    94
    clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
jens@1
    95
    clone.edgeAntialiasingMask = self.edgeAntialiasingMask;
jens@1
    96
    clone.backgroundColor = self.backgroundColor;
jens@1
    97
    clone.opacity = self.opacity;
jens@1
    98
    clone.compositingFilter = self.compositingFilter;
jens@1
    99
    clone.filters = self.filters;
jens@1
   100
    clone.backgroundFilters = self.backgroundFilters;
jens@1
   101
    clone.actions = self.actions;
jens@1
   102
    clone.name = self.name;
jens@1
   103
    clone.style = self.style;
jens@1
   104
    
jens@1
   105
    clone.cornerRadius = self.cornerRadius;
jens@1
   106
    clone.borderWidth = self.borderWidth;
jens@1
   107
    clone.borderColor = self.borderColor;
jens@1
   108
    
jens@1
   109
    for( GGBLayer *sublayer in self.sublayers ) {
jens@1
   110
        sublayer = [sublayer copyWithZone: zone];
jens@1
   111
        [clone addSublayer: sublayer];
jens@1
   112
    }
jens@1
   113
    return clone;
jens@1
   114
}
jens@1
   115
jens@1
   116
jens@1
   117
- (CGFloat) cornerRadius    {return _cornerRadius;}
jens@1
   118
- (CGFloat) borderWidth     {return _borderWidth;}
jens@9
   119
- (CGColorRef) backgroundColor {return _realBGColor;}
jens@1
   120
- (CGColorRef) borderColor  {return _borderColor;}
jens@1
   121
jens@1
   122
- (void) setCornerRadius: (CGFloat)r
jens@1
   123
{
jens@1
   124
    if( r != _cornerRadius ) {
jens@1
   125
        _cornerRadius = r;
jens@1
   126
        [self setNeedsDisplay];
jens@1
   127
    }
jens@1
   128
}
jens@1
   129
jens@1
   130
jens@1
   131
- (void) setBorderWidth: (CGFloat)w
jens@1
   132
{
jens@1
   133
    if( w != _borderWidth ) {
jens@1
   134
        _borderWidth = w;
jens@1
   135
        self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
jens@1
   136
        [self setNeedsDisplay];
jens@1
   137
    }
jens@1
   138
}
jens@1
   139
jens@1
   140
jens@1
   141
- (void) setBackgroundColor: (CGColorRef)color
jens@1
   142
{
jens@1
   143
    if( color != _realBGColor ) {
jens@1
   144
        CGColorRelease(_realBGColor);
jens@1
   145
        _realBGColor = CGColorRetain(color);
jens@1
   146
        [self setNeedsDisplay];
jens@1
   147
    }
jens@1
   148
}
jens@1
   149
jens@1
   150
jens@1
   151
- (void) setBorderColor: (CGColorRef)color
jens@1
   152
{
jens@1
   153
    if( color != _borderColor ) {
jens@1
   154
        CGColorRelease(_borderColor);
jens@1
   155
        _borderColor = CGColorRetain(color);
jens@1
   156
        self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
jens@1
   157
        [self setNeedsDisplay];
jens@1
   158
    }
jens@1
   159
}
jens@1
   160
jens@1
   161
jens@1
   162
- (void)drawInContext:(CGContextRef)ctx
jens@1
   163
{
jens@4
   164
    [super drawInContext: ctx];
jens@4
   165
    
jens@1
   166
    CGContextSaveGState(ctx);
jens@1
   167
jens@1
   168
    if( _realBGColor ) {
jens@1
   169
        CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
jens@1
   170
        CGContextSetFillColorWithColor(ctx, _realBGColor);
jens@1
   171
        if( _cornerRadius <= 0.0 ) {
jens@1
   172
            CGContextFillRect(ctx,interior);
jens@1
   173
        } else {
jens@1
   174
            CGContextBeginPath(ctx);
jens@1
   175
            AddRoundRect(ctx,interior,_cornerRadius);
jens@1
   176
            CGContextFillPath(ctx);
jens@1
   177
        }
jens@1
   178
    }
jens@1
   179
    
jens@1
   180
    if( _borderWidth > 0.0 && _borderColor!=NULL ) {
jens@1
   181
        CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0);
jens@1
   182
        CGContextSetStrokeColorWithColor(ctx, _borderColor);
jens@1
   183
        CGContextSetLineWidth(ctx, _borderWidth);
jens@1
   184
        
jens@1
   185
        if( _cornerRadius <= 0.0 ) {
jens@1
   186
            CGContextStrokeRect(ctx,border);
jens@1
   187
        } else {
jens@1
   188
            CGContextBeginPath(ctx);
jens@1
   189
            AddRoundRect(ctx,border,_cornerRadius);
jens@1
   190
            CGContextStrokePath(ctx);
jens@1
   191
        }
jens@1
   192
    }
jens@1
   193
    
jens@1
   194
    CGContextRestoreGState(ctx);
jens@1
   195
}
jens@1
   196
jens@1
   197
jens@1
   198
#else
jens@1
   199
jens@1
   200
#pragma mark -
jens@1
   201
#pragma mark MAC OS VERSION:
jens@1
   202
jens@1
   203
jens@1
   204
- (id) copyWithZone: (NSZone*)zone
jens@1
   205
{
jens@1
   206
    // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
jens@1
   207
    // a temporary data block, then unarchiving a new layer from that block.
jens@1
   208
    
jens@1
   209
    // One complication is that, due to a bug in Core Animation, CALayer can't archive
jens@1
   210
    // a pattern-based CGColor. So as a workaround, clear the background before archiving,
jens@1
   211
    // then restore it afterwards.
jens@1
   212
    
jens@1
   213
    // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
jens@1
   214
    // As a workaround, clear the contents before archiving, then restore.
jens@1
   215
    
jens@1
   216
    CGColorRef bg = CGColorRetain(self.backgroundColor);
jens@1
   217
    self.backgroundColor = NULL;
jens@1
   218
    id contents = [self.contents retain];
jens@1
   219
    self.contents = nil;
jens@1
   220
    
jens@1
   221
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
jens@1
   222
    
jens@1
   223
    self.backgroundColor = bg;
jens@1
   224
    self.contents = contents;
jens@1
   225
jens@1
   226
    GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
jens@1
   227
    clone.backgroundColor = bg;
jens@1
   228
    clone.contents = contents;
jens@1
   229
    CGColorRelease(bg);
jens@1
   230
    [contents release];
jens@1
   231
jens@1
   232
    return [clone retain];
jens@1
   233
}
jens@1
   234
jens@1
   235
jens@1
   236
#endif
jens@1
   237
jens@1
   238
jens@1
   239
@end
jens@9
   240
jens@9
   241
jens@9
   242
jens@9
   243
#pragma mark -
jens@9
   244
#pragma mark UTILITIES:
jens@9
   245
jens@9
   246
jens@9
   247
void BeginDisableAnimations(void)
jens@9
   248
{
jens@9
   249
    [CATransaction begin];
jens@9
   250
    [CATransaction setValue:(id)kCFBooleanTrue
jens@9
   251
                     forKey:kCATransactionDisableActions];
jens@9
   252
}
jens@9
   253
jens@9
   254
void EndDisableAnimations(void)
jens@9
   255
{
jens@9
   256
    [CATransaction commit];
jens@9
   257
} 
jens@9
   258
jens@9
   259
jens@9
   260
void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
jens@9
   261
{
jens@9
   262
    // Disable actions, else the layer will move to the wrong place and then back!
jens@9
   263
    [CATransaction flush];
jens@9
   264
    BeginDisableAnimations();
jens@9
   265
    
jens@9
   266
    CGPoint pos = layer.position;
jens@9
   267
    if( layer.superlayer )
jens@9
   268
        pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
jens@9
   269
    [layer retain];
jens@9
   270
    [layer removeFromSuperlayer];
jens@9
   271
    layer.position = pos;
jens@9
   272
    if( index >= 0 )
jens@9
   273
        [newSuperlayer insertSublayer: layer atIndex: index];
jens@9
   274
    else
jens@9
   275
        [newSuperlayer addSublayer: layer];
jens@9
   276
    [layer release];
jens@9
   277
    
jens@9
   278
    EndDisableAnimations();
jens@9
   279
}
jens@9
   280
jens@9
   281
jens@9
   282
void RemoveImmediately( CALayer *layer )
jens@9
   283
{
jens@9
   284
    [CATransaction flush];
jens@9
   285
    BeginDisableAnimations();
jens@9
   286
    [layer removeFromSuperlayer];
jens@9
   287
    EndDisableAnimations();
jens@9
   288
}    
jens@9
   289
jens@9
   290