Cocoa notes (24) Animation

六月 2nd, 2011 Cocoa

动画主要分两种实现方式一种是UIView层面上,一种是底层的CATransition

1
2
3
4
5
6
7
8
9
    // Why 0.3f seconds? Because that is the time used to display a keyboard
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:0.3f];
   
    // change location/size/transparent..
   
    [UIView commitAnimations];

变形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    CGFloat angle = theta * (M_PI / 100.0f);
    CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
   
    // Theta ranges between 0% and 199% of PI, i.e. between 0 and 2*PI
    theta = (theta + 1) % 200;

    // For fun, scale by the absolute value of the cosine
    float degree = cos(angle);
    if (degree < 0.0) degree *= -1.0f;
    degree += 0.5f;
   
    // Create add scaling to the rotation transform
    CGAffineTransform scaled = CGAffineTransformScale(transform, degree, degree);
   
    // Apply the affine transform
    [[self.view viewWithTag:999] setTransform:scaled];

Swap(互换效果)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // hide the button
    self.navigationItem.rightBarButtonItem = nil;
   
    UIView *frontObject = [[self.view subviews] objectAtIndex:2];
    UIView *backObject = [[self.view subviews] objectAtIndex:1];
   
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0];
   
    frontObject.alpha = 0.0f;
    backObject.alpha = 1.0f;
    frontObject.transform = CGAffineTransformMakeScale(0.25f, 0.25f);
    backObject.transform = CGAffineTransformIdentity;
    [self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:2];

    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationFinished:)];
    [UIView commitAnimations];

Flip(翻转)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0];
   
    UIView *whiteBackdrop = [self.view viewWithTag:100];

    // Choose left or right flip
    if ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex])
        [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:whiteBackdrop cache:YES];
    else
        [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:whiteBackdrop cache:YES];

    NSInteger purple = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:999]];
    NSInteger maroon = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:998]];
    [whiteBackdrop exchangeSubviewAtIndex:purple withSubviewAtIndex:maroon];

    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationFinished:)];
    [UIView commitAnimations];

CAAnimation 的delegate方法(竟然没有相应的protocol??)
– animationDidStart: delegate method
– animationDidStop:finished: delegate method

CATransition
4种动画形式
NSString * const kCATransitionFade;
NSString * const kCATransitionMoveIn;
NSString * const kCATransitionPush;
NSString * const kCATransitionReveal;

4个方向
NSString * const kCATransitionFromRight;
NSString * const kCATransitionFromLeft;
NSString * const kCATransitionFromTop;
NSString * const kCATransitionFromBottom;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    // Set up the animation
    CATransition *animation = [CATransition animation];
    animation.delegate = self;
    animation.duration = 1.0f;
    animation.timingFunction = UIViewAnimationCurveEaseInOut;
   
    switch ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex])
    {
        case 0:
            animation.type = kCATransitionFade;
            break;
        case 1:
            animation.type = kCATransitionMoveIn;
            break;
        case 2:
            animation.type = kCATransitionPush;
            break;
        case 3:
            animation.type = kCATransitionReveal;
        default:
            br eak;
    }
    if (isLeft)
        animation.subtype = kCATransitionFromRight;
    else
        animation.subtype = kCATransitionFromLeft;

    // Perform the animation
    UIView *whitebg = [self.view viewWithTag:10];
    NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]];
    NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]];
    [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white];
    [[whitebg layer] addAnimation:animation forKey:@"animation"];

CALayer (疑惑),animation 没有commit?或许是代码笔误。。

此例疑惑,为什么最后theView又出现了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    // Adapted from Lucas Newman's sample code (www.lucasnewman.com)
    UIView *theView = [self.view viewWithTag:101];
    [CATransaction begin];
    [CATransaction setValue:[NSNumber numberWithFloat:ANIMATION_DURATION] forKey:kCATransactionAnimationDuration];
   
    // scale it down
    CABasicAnimation *shrinkAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    shrinkAnimation.delegate = self;
    shrinkAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    shrinkAnimation.toValue = [NSNumber numberWithFloat:0.0];
    [[theView layer] addAnimation:shrinkAnimation forKey:@"shrinkAnimation"];
   
    // fade it out
    CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeAnimation.toValue = [NSNumber numberWithFloat:0.0];
    fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    [[theView layer] addAnimation:fadeAnimation forKey:@"fadeAnimation"];
   
    // make it jump a couple of times
    CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    CGMutablePathRef positionPath = CGAutorelease(CGPathCreateMutable());
    CGPathMoveToPoint(positionPath, NULL, [theView layer].position.x, [theView layer].position.y);
    CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y, [theView layer].position.x, [theView layer].position.y);
    CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y * 1.5, [theView layer].position.x, [theView layer].position.y);
    CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y * 1.25, [theView layer].position.x, [theView layer].position.y);
    positionAnimation.path = positionPath;
    positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    [[theView layer] addAnimation:positionAnimation forKey:@"positionAnimation"];
   
    [CATransaction commit];

curling(翻页效果)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    // hide the button
    self.navigationItem.rightBarButtonItem = nil;
   
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0];
   
    UIView *whiteBackdrop = [self.view viewWithTag:100];

    // Choose up or down curl
    if ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex])
        [UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:whiteBackdrop cache:YES];
    else
        [UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:whiteBackdrop cache:YES];

    NSInteger purple = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:999]];
    NSInteger maroon = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:998]];
    [whiteBackdrop exchangeSubviewAtIndex:purple withSubviewAtIndex:maroon];

    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationFinished:)];
    [UIView commitAnimations];

翻页控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
    self.navigationItem.rightBarButtonItem = BARBUTTON(@"Curl", @selector(curl:));
    curlUp = !curlUp;
}

- (void) curl: (id) sender
{
    // hide the button
    self.navigationItem.rightBarButtonItem = nil;

    // Set up the animation
    CATransition *animation = [CATransition animation];
    animation.delegate = self;
    animation.duration = 0.3f;
    animation.timingFunction = UIViewAnimationCurveEaseInOut;
    animation.removedOnCompletion = NO;
    if (curlUp)
    {
        animation.type = @"pageCurl";
        animation.fillMode = kCAFillModeForwards;
        animation.endProgress = 0.7;
    }
    else
    {
        animation.type = @"pageUnCurl";
        animation.fillMode = kCAFillModeBackwards;
        animation.startProgress = 0.3;
    }

    // Perform the animation
    UIView *whitebg = [self.view viewWithTag:10];
    NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]];
    NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]];
    [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white];
    [[whitebg layer] addAnimation:animation forKey:@"page curl"];
   
    // Allow or disallow user interaction (otherwise you can touch "through"
    // the cover view to enable/disable the switch)
    if (purple < white)
        [self.view viewWithTag:99].userInteractionEnabled = YES;
    else
        [self.view viewWithTag:99].userInteractionEnabled = NO;
       
}

更多效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Set up the animation
    CATransition *animation = [CATransition animation];
    animation.delegate = self;
    animation.duration = 1.0f;
    animation.timingFunction = UIViewAnimationCurveEaseInOut;
   
    switch ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex])
    {
        case 0:
            animation.type = @"rippleEffect";
            break;
        case 1:
            animation.type = @"pageCurl";
            break;
        case 2:
            animation.type = @"pageUnCurl";
            break;
        case 3:
            animation.type = @"suckEffect";
            break;
        default:
            break;
    }

    switch (direction)
    {
        case 0:
            animation.subtype = kCATransitionFromRight;
            break;
        case 1:
            animation.subtype = kCATransitionFromTop;
            break;
        case 2:
            animation.subtype = kCATransitionFromLeft;
            break;
        case 3:
            animation.subtype = kCATransitionFromBottom;
            break;
        default:
            break;
    }

    // Perform the animation
    UIView *whitebg = [self.view viewWithTag:10];
    NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]];
    NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]];
    [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white];
    [[whitebg layer] addAnimation:animation forKey:@"animation"];
   
    if (++direction > 3) direction -= 4;

有以下效果,奇怪为什么ios library里面找不到:
pageCurl 向上翻一页
pageUnCurl 向下翻一页
rippleEffect 滴水效果
suckEffect 收缩效果,如一块布被抽走
cube 立方体效果
oglFlip 上下翻转效果

Modal Animation(模态动画。动画会等前一段结束后再开始第二段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    [self.view viewWithTag:101].alpha = 1.0f;
   
    // Bounce to 115% of the normal size
    [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:0.4f];
    [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(1.15f, 1.15f);
    [UIView commitModalAnimations];

    // Return back to 100%
    [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:0.3f];
    [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(1.0f, 1.0f);
    [UIView commitModalAnimations];
   
    // Pause for a second and appreciate the presentation
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]];
   
    // Slowly zoom back down and hide the view
    [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0f];
    [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(0.01f, 0.01f);
    [UIView commitModalAnimations];

逐帧动画

1
2
3
4
5
6
7
8
9
10
11
12
    // Load butterfly images
    NSMutableArray *bflies = [NSMutableArray array];
    for (int i = 1; i <= 17; i++)
        [bflies addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"bf_%d", i] ofType:@"png"]]];
   
    UIImageView *butterflyView = [[UIImageView alloc] initWithFrame:CGRectMake(40.0f, 300.0f, 60.0f, 60.0f)];
    butterflyView.tag = 300;
    butterflyView.animationImages = bflies;
    butterflyView.animationDuration = 0.75f;
    [self.view addSubview:butterflyView];
    [butterflyView startAnimating];
    [butterflyView release];

演示:两种不同的实现倒影的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#define CGAutorelease(x) (__typeof(x))[NSMakeCollectable(x) autorelease]

#import "ImageHelper-Reflections.h"

@implementation ImageHelper
+ (CGImageRef) createGradientImage: (CGSize)size
{
    CGFloat colors[] = {0.0, 1.0, 1.0, 1.0};
   
    // Create gradient in gray device color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpace, kCGImageAlphaNone);
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
    CGColorSpaceRelease(colorSpace);

    // Draw the linear gradient
    CGPoint p1 = CGPointZero;
    CGPoint p2 = CGPointMake(0, size.height);
    CGContextDrawLinearGradient(context, gradient, p1, p2, kCGGradientDrawsAfterEndLocation);
   
    // Return the CGImage
    CGImageRef theCGImage = CGBitmapContextCreateImage(context);
    CFRelease(gradient);
    CGContextRelease(context);
    return theCGImage;
}

+ (UIImage *) reflectionOfView: (UIView *)theView withPercent: (CGFloat) percent
{
    // Retain the width but shrink the height
    CGSize size = CGSizeMake(theView.frame.size.width, theView.frame.size.height * percent);

    // Shrink the view
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [theView.layer renderInContext:context];
    UIImage *partialimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
   
    // build the mask
    CGImageRef mask = [ImageHelper createGradientImage:size];
    CGImageRef ref = CGImageCreateWithMask(partialimg.CGImage, mask);
    UIImage *theImage = [UIImage imageWithCGImage:ref];
    CGImageRelease(ref);
    CGImageRelease(mask);
    return theImage;
}

const CGFloat kReflectDistance = 10.0f;

+ (void) addReflectionToView: (UIView *) theView
{
    theView.clipsToBounds = NO;
    UIImageView *reflection = [[UIImageView alloc] initWithImage:[ImageHelper reflectionOfView:theView withPercent: 0.45f]];
    CGRect frame = reflection.frame;
    frame.origin = CGPointMake(0.0f, theView.frame.size.height + kReflectDistance);
    reflection.frame = frame;
    [theView addSubview:reflection];
    [reflection release];
}

const CGFloat kReflectPercent = 0.5f;
const CGFloat kReflectOpacity = 0.5f;

+ (void) addSimpleReflectionToView: (UIView *) theView
{
    CALayer *reflectionLayer = [CALayer layer];
    reflectionLayer.contents = [theView layer].contents;
    reflectionLayer.opacity = kReflectOpacity;
    reflectionLayer.frame = CGRectMake(0.0f, 0.0f, theView.frame.size.width, theView.frame.size.height * kReflectPercent);
    CATransform3D stransform = CATransform3DMakeScale(1.0f, -1.0f, 1.0f);
    CATransform3D transform = CATransform3DTranslate(stransform, 0.0f, -(kReflectDistance + theView.frame.size.height), 0.0f);
    reflectionLayer.transform = transform;
    reflectionLayer.sublayerTransform = reflectionLayer.transform;
    [[theView layer] addSublayer:reflectionLayer];
}
@end

UIView
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState
如果没有停止(+ setAnimationDidStopSelector:)就开始下一段动画,将fromCurrentState设置为YES,会让动画从当前状态开始变化。

可能有用,留着到家看brow-leaves-67b10ed

Cocoa notes (23) Images

五月 31st, 2011 Cocoa

获取Image

1
myImage = [UIImage imageNamed:@"icon.png"];

这个方法的缺陷是,image对象将会被IPhone系统cache住,并不受到该app的内存控制,不适合用于读取大图片。
一般使用此方法从app bundle中获取图片

1
2
3
NSString *path = [[NSBundle mainBundle]
    pathForResource:@"icon" ofType:@"png"];
myImage = [UIImage imageWithContentsOfFile:path];

获取app路径下的Documents路径的途径:

1
2
3
NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
return [paths lastObject];

1
2
return [NSHomeDirectory()
stringByAppendingPathComponent:@"Documents"];

从URL获取图片

1
2
3
4
NSURL *url = [NSURL URLWithString:
    @"http://image.weather.com/images/maps/current/curwx_600x405.jpg"];
UIImage *img = [UIImage imageWithData:
    [NSData dataWithContentsOfURL:url]];

从Photo Album Library获得图片
通过UIImagePickerController,3个途径
UIImagePickerControllerSourceTypePhotoLibrary
UIImagePickerControllerSourceTypeSavedPhotosAlbum
UIImagePickerControllerSourceTypeCamera

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    SETIMAGE([info objectForKey:@"UIImagePickerControllerOriginalImage"]);
    [self dismissModalViewControllerAnimated:YES];
    [picker release];
}

- (void) pickImage: (id) sender
{
    UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
    ipc.sourceType =  UIImagePickerControllerSourceTypePhotoLibrary;
    ipc.delegate = self;
    ipc.allowsImageEditing = NO;
    [self presentModalViewController:ipc animated:YES];
}

如果允许编辑,返回图片时
SETIMAGE([info objectForKey:@"UIImagePickerControllerEditedImage"]);
保存图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (NSString *) findUniqueSavePath
{
    int i = 1;
    NSString *path;
    do {
        // iterate until a name does not match an existing file
        path = [NSString stringWithFormat:@"%@/Documents/IMAGE_%04d.PNG", NSHomeDirectory(), i++];
    } while ([[NSFileManager defaultManager] fileExistsAtPath:path]);
   
    return path;
}

....
[UIImagePNGRepresentation(image) writeToFile:[self findUniqueSavePath] atomically:YES];

Page Scroll滚动图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
UIScrollView *sv = [[[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, BASEHEIGHT)] autorelease];
sv.contentSize = CGSizeMake(NPAGES * 320.0f, sv.frame.size.height);
sv.pagingEnabled = YES;
sv.delegate = self;

// Load in all the pages
    for (int i = 0; i < NPAGES; i++)
    {
        NSString *filename = [NSString stringWithFormat:@"image%d.png", i+1];
        UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:filename]];
        iv.frame = CGRectMake(i * 320.0f, 0.0f, 320.0f, BASEHEIGHT);
        [sv addSubview:iv];
        [iv release];
    }
   
    [self.view addSubview:sv];

绘图并绘制文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Draw centered text into the context
void centerText(CGContextRef context, NSString *fontname, float textsize, NSString *text, CGPoint point, UIColor *color)
{
    CGContextSaveGState(context);
    CGContextSelectFont(context, [fontname UTF8String], textsize, kCGEncodingMacRoman);
   
    // Retrieve the text width without actually drawing anything
    CGContextSaveGState(context);
    CGContextSetTextDrawingMode(context, kCGTextInvisible);
    CGContextShowTextAtPoint(context, 0.0f, 0.0f, [text UTF8String], text.length);
    CGPoint endpoint = CGContextGetTextPosition(context);
    CGContextRestoreGState(context);
   
    // Query for size to recover height. Width is less reliable
    CGSize stringSize = [text sizeWithFont:[UIFont fontWithName:fontname size:textsize]];
   
    // Draw the text
    [color setFill];
    CGContextSetShouldAntialias(context, true);
    CGContextSetTextDrawingMode(context, kCGTextFill);
    CGContextSetTextMatrix (context, CGAffineTransformMake(1, 0, 0, -1, 0, 0));
    CGContextShowTextAtPoint(context, point.x - endpoint.x / 2.0f, point.y + stringSize.height / 4.0f, [text UTF8String], text.length);
    CGContextRestoreGState(context);
}

- (UIImage *) createImageWithColor: (UIColor *) color
{
    UIGraphicsBeginImageContext(CGSizeMake(SIDE_LENGTH, SIDE_LENGTH));
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Create a filled ellipse
    [color setFill];
    CGContextAddEllipseInRect(context, CGRectMake(0.0f, 0.0f, SIDE_LENGTH, SIDE_LENGTH));
    CGContextFillPath(context);
   
    // Label with a number
    [[UIColor whiteColor] setFill];
    NSString *numstring = [NSString stringWithFormat:@"%d", count++];
    centerText(context, @"Georgia", 18.0f, numstring, CGPointMake(SIDE_LENGTH / 2.0f, SIDE_LENGTH / 2.0f), [UIColor whiteColor]);
   
    // Outline the circle
    CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);
    CGContextAddEllipseInRect(context, CGRectMake(INSET_AMT, INSET_AMT, SIDE_LENGTH - 2.0f * INSET_AMT, SIDE_LENGTH - 2.0f * INSET_AMT));
    CGContextStrokePath(context);

    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return theImage;
}

Draw Image

1
2
3
4
5
6
7
8
9
10
11
12
13
// calculate the fitted size
    CGSize size = [ImageHelper fitSize:image.size inSize:viewsize];
   
    UIGraphicsBeginImageContext(viewsize);

    float dwidth = (viewsize.width - size.width) / 2.0f;
    float dheight = (viewsize.height - size.height) / 2.0f;
   
    CGRect rect = CGRectMake(dwidth, dheight, size.width, size.height);
    [image drawInRect:rect];
   
    UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

HOW TO – 如何向IPhone模拟器的photos添加图片

五月 31st, 2011 Cocoa, how to, 全部

默认图片是空的
有提示文字“You can sync photos and videos onto your iPad Simulator using iTunes.”
结果有人发现,其实这是坑爹的文字模板”You can sync photos and videos onto your {DEVICE_NAME} using iTunes.”,{DEVICE_NAME}会被[[UIDevice currentDevice] model]替换,如果是在真机上测试,itunes当然是可以同步图片的,但是模拟器上无法用itunes。

解决办法:
首先打开模拟器,并进入photo
然后用safari浏览到一张图片,然后拖拽图片到iphone模拟器
模拟器会用模拟器的safari打开图片
然后在图片上长按鼠标,弹出对话框,选择保存,这时再回到phot里就可以看到图片了。

(转载)10个步骤:如何成为iPhone游戏开发者

五月 30th, 2011 Cocoa

Neil Ferguson,是iPhone 游戏「病毒攻击 | Virus Strike」的开发者。在这篇文章里分享了他自己独立开发一款iPhone游戏的经验和感受。

虽然是一名「老」程序员,目前在伦敦从事全职软件创业公司的Neil Ferguson,觉得开发一款成功的iOS 游戏也许并不需要你有太多的程序开发和编程经验。

对编程算是零基础的我,也一直有过想法在业余的时间学习下开发。

我们来看看,Neil Ferguson 总结出来开发一款iPhone(或者说 iOS平台)游戏(程序)的10个步骤。

(一)原创的想法

我大概是在1年前才有了这个 Virus Strike的想法。我一直在玩一个基于物理原理的游戏叫Linerider ,还有飞行控制方面的游戏。我觉得如果有一款游戏通过物理引擎,画一根线让3个相同东西匹配(译者注:类似俄罗斯方块),一定会很有趣。于是我就到App store查遍了所有的益智游戏(puzzle game),看看是不是有这类的游戏。花了几天时间,结果我一个都没看到。那时候我就意识到,我第一个想到这个游戏的创意,至少还没有人做出,我为何不开发一款这样的游戏呢?于是就开始了这个 Virus Strike的开发。

Nooidea:并不一定是惊天动地的想法,一点点的创新点子都可以成就一个出色的产品。大多时候你未必是第一个,可也许你稍加用心,你就可以成为最棒的那个。


(二)使用正确的工具

如果是一个初学的程序员,你可以尝试使用“托-放” 形式的游戏制作库,比如 GameSalad。这让你可以在没有多少编程知识的情况下一样创建你的游戏,而且GameSalad 是特别为iPhone设计的工具。除此,你也许会发现在Flash平台下写游戏比在Objective C(iPhone 程序开发的标准语言)下要容易一些。你现在可以转换Flash的游戏在iPhone上运行,而且对于初学者,也有很多不错的 Flash游戏开发方面的书籍。

如果你一定要使用 Objective C开发,那你一定要用游戏框架,在游戏编码上会容易很多。我个人使用的是 Cocos2D,这是一个非常棒的iPhone游戏开发的框架工具,而且是免费开源的。它还具有一个集成的物理引擎,给我当时开发 Virus Strike带来了不少方便。

(三)充分利用免费教程

Virus Strike是我的第一个 iPhone游戏,而且我以前也从来没有使用过 Objective C编写程序,所以在开发这个游戏的时候,我也确实学到了很多。很多在线的教程确实帮了我不少,比如说 Ray Wenderlich 的网站 www.raywenderlich.com,提供了很多关于 iOS 编程的免费教程。非常的有用!在苹果的官方开发者网站资源也很多,developer.apple.com

Nooidea:欢迎大家分享更多的在线教程、社区、资源等 :)

(四)外包你做不来的东西

如果你自己本身不是一个程序员,我觉得一开始你最好是把你的最初好的想法外包给经验丰富的人来替你做。比如,你可以将你 app的想法发到 odesk.com ,会有程序员来申请包办你的项目。同样,如果在你的应用程序app开发过程中,你有一个单独的部分做不成,你也最好外包出去。只是提醒你的是,你外包应用出去时你要给程序员提供尽可能多的信息和细节,这样App开发出来的时候才会更让你满意。


(五)想想关于iPhone特有的功能

App Store里最成功的游戏一定是那些符合iPhone特点的、适合在iPhone上玩的游戏。我们来看看 Virus Strike,我采用了经典的俄罗斯方块类似的游戏玩法,结合iPhone特有的触摸屏和加速体验。你在屏幕上划一道线,用来指引病毒,你倾斜iPhone的屏幕,这些病毒也会跟着倾斜。在你开发游戏的时候,你一定要想着如何把iPhone的一些独特的控制方式融入到你的游戏当中。尽可能的实现原创,有特色!


(六)确保游戏有挑战性

在我搞定了最基本游戏的技术部分 —划线条、色彩匹配、还有物理引擎之后,最大的问题是我怎样才能把我当初的想法和概念转化成一个有挑战性的游戏,而且可以让玩家很快地上手。

对 于一个游戏来说,我想玩家每玩一次游戏所耗费的时间和游戏的挑战性非常重要。游戏要逐渐加大难度,但同时要有公正性— 也就是要让玩家觉得是因为自己的失误才丢了一局。另外必须可以让玩家觉得他在游戏当中有所进展,在整个游戏的过程当中随着更多级别的游戏,不管是通过更高 的得分还是其他形式的奖励,要让玩家有一种成就感。


(七)免费的声音效果

我游戏里的所有声音效果都来自 freesound.org。这是一个很厌烦的过程,所以我建议最好多问一问其他人的观点和建议,看有些你喜欢的声音是不是别人会觉得讨厌。在编辑声音效果的时候,我还用到了一个免费的程序,Audacity ,这样可以让声音更加搭配游戏。


(八)获得反馈

你可不要以为这么游戏就开发完毕了。直到你从其他人那里得到反馈,你才算真的了解到底有多少人觉得你的游戏有挑战性,有意思,值得一玩。而且你未必知道是不是人们也许都会玩你的这个游戏。

不要指望从你朋友那里得到真实的反馈意见,也不要给别人演示怎么去玩你的游戏。你要让他们独自拿着你的游戏试一试,最好能站在一旁看一看,看他们是如何玩,是不是会遇到一些问题。

你也可以轻易的从一些 iPhone论坛找到测试版尝鲜的人,他们可以免费的给你提供些反馈意见。


(九)做一个视频

我的测试用户让我意识到做一个使用教程的视频是很有帮助的。我使用ScreenFlow 做了个 一分钟长的游戏的视频,测试后我有添加了 一页纸的文字描述,方便那些第一次打开这个游戏,跳过视频介绍的用户可。

一段视频是非常值得的,这可以大大的帮助确保人们明白如何去玩这款游戏。对于我的妻子 Donna,这段视频也非常有用,她负责 Virus Strike的公关推广。报道的人员可以很快的去看这段在线视频,这样他们可以在发布会的演示上不必要真实的体验过也可以知道这款游戏是如何操作的,当然通过视频他们可以确保自己喜欢,再去花时间下载。


(十)推广你的游戏

不论你的游戏有多么的棒,如果你不去做市场推广,有怎么会有人在 App Store找到你的游戏下载呢?你要做好准备花大量的时间在一些 App Review(应用评测)的网站,包括其他的一些科技网站。

我妻子在我推出 Virus Strike时,给我写的一篇新闻发布稿件就有相当不错的效果。当然你只能羡慕我有一个记者老婆,她知道怎么弄出来一篇好的稿件,放一些会吸引其他报道者眼球的故事。我们当时付给 PRMac $20美金的发行费用,事实证明是非常值得的。这个稿件基本上传的整个网络都是,很多网站甚至是直接全文转载。

Nooidea

好吧,我也决定给作者 Neil Ferguson的游戏 Virus Strike做个免费的宣传把,点击这个 ▶链接进入iTunes察看游戏的信息。

翻译:由 Nooidea.com | 装傻充愣 提供

(转载请注明翻译信息。)
原文:http://article.yeeyan.org/view/116885/154154

不少教程

五月 30th, 2011 Cocoa, resources

http://www.raywenderlich.com/tutorials

How To Make A Space Shooter iPhone Game

五月 30th, 2011 Cocoa, Cocos2D, how to, 源码

How To Make A Space Shooter iPhone Game

source: SpaceGame

源码阅读

五月 30th, 2011 Cocoa, 源码

TableViewSuite

tableview的范例比较重要,第四第五个例子展示了如何用自定义视图显示表格,并且有用到计时器。

PhotoPicker

Cocoa notes (22) Timer

五月 30th, 2011 Cocoa, 全部, 技术

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Timers/Articles/usingTimers.html#//apple_ref/doc/uid/20000807-CJBJCBDE

Cocoa notes (21) Fast Enumeration

五月 27th, 2011 Cocoa, 全部

枚举, for … in 语法

1
for ( Type newVariable in expression ) { statements }

1
2
Type existingItem;
for ( existingItem in expression ) { statements }

使用for…in语法的expression必须实现 NSFastEnumeration协议,NSArray, NSDictionary, NSSet, NSDictionary 等 实现了此协议。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSArray *array = [NSArray arrayWithObjects:
        @"One", @"Two", @"Three", @"Four", nil];
 
for (NSString *element in array) {
    NSLog(@"element: %@", element);
}
 
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
    @"quattuor", @"four", @"quinque", @"five", @"sex", @"six", nil];
 
NSString *key;
for (key in dictionary) {
    NSLog(@"English: %@, Latin: %@", key, [dictionary objectForKey:key]);
}

也可以使用NSEnumerator对象进行遍历

1
2
3
4
5
6
7
8
9
10
11
12
NSArray *array = [NSArray arrayWithObjects:
        @"One", @"Two", @"Three", @"Four", nil];
 
NSEnumerator *enumerator = [array reverseObjectEnumerator];
for (NSString *element in enumerator) {
    if ([element isEqualToString:@"Three"]) {
        break;
    }
}
 
NSString *next = [enumerator nextObject];
// next = "Two"

Cocoa notes (20) Associative

五月 27th, 2011 Cocoa

Associative 应该翻译成关联吧?
简单说就是把两个对象的生命周期关联起来
使用方法:objc_setAssociatedObject,这个方法有4个参数:源对象,一个key,一个value,一个关联的策略常量。
* key是void 类型指针,每个关联的key必须唯一,一般用static声明。
* 策略常量描述关联对象应该用assign,retain还是copy、关联是自动还是不自动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static char overviewKey;
 
NSArray *array =
    [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];
// For the purposes of illustration, use initWithFormat: to ensure
// the string can be deallocated
NSString *overview =
    [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];
 
objc_setAssociatedObject (
    array,
    &overviewKey,
    overview,
    OBJC_ASSOCIATION_RETAIN
);
 
[overview release];
// (1) overview valid
[array release];
// (2) overview invalid

(1) overview此时仍然有效,因为关联策略使用的是 OBJC_ASSOCIATION_RETAIN
(2) 此时overview变量和array同时被release

从关联源对象获取关联对象

1
2
NSString *associatedObject =
    (NSString *)objc_getAssociatedObject(array, &overviewKey);

断开关联对象,调用objc_setAssociatedObject函数,第三个参数设为nil

1
objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);

完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
    static char overviewKey;
 
    NSArray *array = [[NSArray alloc]
        initWithObjects:@ "One", @"Two", @"Three", nil];
    // For the purposes of illustration, use initWithFormat: to ensure
    // we get a deallocatable string
    NSString *overview = [[NSString alloc]
        initWithFormat:@"%@", @"First three numbers"];
 
    objc_setAssociatedObject (
        array,
        &overviewKey,
        overview,
        OBJC_ASSOCIATION_RETAIN
    );
    [overview release];
 
    NSString *associatedObject =
        (NSString *) objc_getAssociatedObject (array, &overviewKey);
    NSLog(@"associatedObject: %@", associatedObject);
 
    objc_setAssociatedObject (
        array,
        &overviewKey,
        nil,
        OBJC_ASSOCIATION_ASSIGN
    );
    [array release];
 
    [pool drain];
    return 0;
}