Archive for 五月, 2011

Cocoa notes (23) Images

星期二, 五月 31st, 2011

获取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

默认图片是空的
有提示文字“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

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

http://www.raywenderlich.com/tutorials

How To Make A Space Shooter iPhone Game

星期一, 五月 30th, 2011

How To Make A Space Shooter iPhone Game

source: SpaceGame

源码阅读

星期一, 五月 30th, 2011

TableViewSuite

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

PhotoPicker

Cocoa notes (22) Timer

星期一, 五月 30th, 2011

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

枚举, 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

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

Cocoa notes (19) Informal Protocol 、Category和Runtime

星期五, 五月 27th, 2011

Stackoverflow.com上的一个问题,还需要时间理解
http://stackoverflow.com/questions/2010058/informal-protocol-in-objective-c

一般informal protocol在NSObject上声明

1
2
3
4
@interface NSObject ( MyXMLSupport )
- initFromXMLRepresentation:(NSXMLElement *)XMLElement;
- (NSXMLElement *)XMLRepresentation;
@end

informal protocol所声明的方法都相当于formal protocol 声明的可选方法,对于informal protocol的作用看上面链接那个解释。。

category(分类)允许你给其他类添加方法
声明形式

1
2
3
4
5
6
7
// 给ClassName类添加(CategoryName)的方法
// 必须import原类头文件
#import "ClassName.h"
 
@interface ClassName ( CategoryName )
// method declarations
@end

Category类文件的命名习惯用ClassName+CategoryName命名

1
2
3
4
#import "ClassName+CategoryName.h"
@implementation ClassName ( CategoryName )
// method definitions
@end

* Category不能用来声明新的成员变量,但category可以访问到所有的该类的成员变量,包括标记未@private的
* 一个类可以实现多个Category,同一个类上的不同Category的方法名不能重名。
* 虽然现在使用category可以覆盖类的原有方法,但是不推荐这么做!
* 如果用category为NSObject增加方法,注意NSObject没有父类,不能在方法中用super

runtime.h
作用和Category差不多
涉及的头文件
一些方法

1
2
3
4
5
6
7
8
9
10
11
// 反射方法
Method *class_copyMethodList(Class cls, unsigned int *outCount);
SEL method_getName(Method m);
IMP method_getImplementation(Method m);
char *method_copyReturnType(Method m);
// 给类加一个方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
// 替换类的方法
IMP method_setImplementation(Method method, IMP imp);
// 交换(Method swizzling, 什么是Method Swizzling,Swizzle是搅合,调配的意思,so...)
void method_exchangeImplementations(Method m1, Method m2);
1
2
3
4
5
6
7
8
9
10
11
Method existingMethod = ...;
Method fancyNewMethod = ...;
method_exchangeImplementations(existingMethod, fancyNewMethod);

- (void)fancyNewMethod
{
// 这样看起来会形成一个死循环
// 但是交换之后他实际上执行的是“existingMethod”!
[self fancyNewMethod];
// Perform additional work here
}

例子:18_ObjCPlayground
参考:Categories