2011年8月10日水曜日

自作アプリ「TaskTodo」おんぼろ開発秘話 後編


前回の記事の続きとなります。
僕が気まぐれで作ったおんぼろTodoアプリの作り方をまとめました。

詳細は以下から........
さて、前回に引き続きおんぼろ解説でお届けいたします。

残った項目は、



4 コードをつらつらと書く 

5 コード内の処理や設定を3で作ったパーツにくっつける 

6 必要があれば3に戻って機能を積み重ねて行く 

7 アイコンやプログラムの詳細事項をざざっとまとめる 

8 完成&公開 



の5項目ですね。



4 コードをつらつらと書く 

はい。つらつらと参考書とか片手に書き込んで行きます。
できあがる一つのファイルは、大体こんな感じです。

(絶対に読み飛ばしてください。こんな長いの僕でも読めません。)


#import "RootViewController.h"
@implementation RootViewController
@synthesize fetchedResultsController, managedObjectContext;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
// Set up the edit and add buttons.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:0.767 green:0.727 blue:0.967 alpha:1.0];
    //self.navigationController.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(moveAddView)];
    self.navigationItem.rightBarButtonItem = addButton;
    [addButton release];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
/*
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}
*/
/*
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}
*/
/*
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
*/
/*
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
*/
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
/*
 // Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
 */
#pragma mark -
#pragma mark Add a new object
- (void)insertNewObject {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
[newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
[newManagedObject setValue:@"予定" forKey:@"title"];
[newManagedObject setValue:@"内容" forKey:@"schedule"];
// Save the context.
    NSError *error = nil;
    if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
 
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
    }
#pragma mark -
#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    static NSString *CellIdentifier = @"Cell"
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]
// Configure the cell.
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:@"title"] description];
cell.detailTextLabel.text = [[managedObject valueForKey:@"schedule"] description];
    return cell;
}
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 40.5// 行の高さを50にする
}
- (void)moveAddView/*:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath*/ {
    // Navigationogic may go here -- for example, create and push another view controller.
AddView *detailViewController = [[AddView alloc] initWithNibName:@"AddView" bundle:nil];
detailViewController.fetchedResultsController = self.fetchedResultsController;
//NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
//[detailViewController release];
}
/*
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here -- for example, create and push another view controller.
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
     NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
     // ...
     // Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release]
}
*/

/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the managed object for the given index path
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    // The table view should not be re-orderable.
    return NO;
}
#pragma mark -
#pragma mark Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController != nil) 
        return fetchedResultsController
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
    aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
// NSFetchedResultsControllerDelegate method to notify the delegate that all section and object changes have been processed. 
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
/*
 Instead of using controllerDidChangeContent: to respond to all changes, you can implement all the delegate methods to update the table view in response to individual changes.  This may have performance implications if a large number of changes are made simultaneously.
// Notifies the delegate that section and object changes are about to be processed and notifications will be sent. 
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
// Update the table view appropriately.
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
// Update the table view appropriately.
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
 */
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
// Relinquish ownership of any cached data, images, etc that aren't in use.
}
- (void)dealloc {
[fetchedResultsController release];
[managedObjectContext release];
    [super dealloc];
}
@end


省略しないで書いた自分が馬鹿だというのは言われないでも気がついてます。
まあ、ぶっちゃけ長いんです。でも、もちろんこれは全部自分で打ち込んだ
わけではありません。もちろん。

もともと、必要であろう部分は全部Xcodeがプロジェクトを作るときに自動的
に作成してくれちゃうんですね。
ですから、数行書き換えるだけで完成したりもします。
今回は、まあ大して書き込んではいません。

ちなみに、例えば

@property (nonatomic,retain) IBOutlet UITextField *textField;

と打ちたかったとします。
これ打つだけでも億劫ですよね。
でも、例えば始めの単語の「@property」は、「@pr」まで入力すると、
自動的に後の部分を補ってくれるのです。

他の単語もその様にして入力していくので、長いコードも負担なく
書くことができるんです。

こんな便利な機能の中で、つらつらーっとソースを書き上げます。



5 コード内の処理や設定を3で作ったパーツにくっつける 

iPhoneアプリを作る過程で、一番楽しい(と言われる)行程です。
ソース内で示した、例えばテキストボックスと、レイアウトファイルで
示したテキストボックスを、ドラック&ドロップでくっつけます。






こんな風にくっつけるんです!
この手順で、処理とレイアウトが関連づけられて、アプリ本体が完成します。



6 必要があれば3に戻って機能を積み重ねて行く 


えー、一回でレイアウト並べて、ソース全部まとめて、繋げるだけで完成
することはほとんどないので積み重ねる必要があります。

前記事の3から、今回の5までを何回もやります。



7 アイコンやプログラムの詳細事項をざざっとまとめる 


ソースを書く手順は随分と流し読みをしました。
この勢いで最後まで手順を見てしまいましょう。

アイコンは、何かしらを使ってアイコンを作ります。
僕はちょっとドットエディタで書いてみたりしますが、最近は写真撮って
GIMPで編集して終了です。

ちなみにGIMPとは、"GNU Image Manipulation Program" の略で、
要するにGNUのイメージエディタです。
Photoshopと同じくらい使えて、かつフリーです。




アイコンファイルは一応作るサイズは決まっていますが、表示するとき
自動的に縮小されるのでPineは適当に正方形のアイコンで済ませます。
今回は写真からこんなアイコンにしました。




Todoっぽければそれでいいや、と思って適当に作りました。
が、アイコンはApp Storeに出すときには大きな武器になります。
逆に、有名でもアイコンがださいアプリはダウンロードされなかったりします。


それから最後に、プログラムについての詳細情報をまとめます。
プロジェクトの中にある、アプリケーション名_info.plistというファイル
の中に、バージョン、名前、各種設定等を書き込みます。






拡張子plistのファイルは、プロパティーリストと呼ばれるファイルです。
XMLで記述されているファイルですが、Xcodeを使うと上の様に、超
視覚的に編集することができます。

これもざざざっと書き上げ、iPhone Simulatorでの検証も終わったら
いよいよ最後です。



8 完成&公開 

ここまでで、ようやっと完了です。
最後に実機で動かすためにコンパイルを行います。

が。

この段階で、Appleと契約をしていない人はストップです。
”普通のやり方では” 実機向けのコンパイルは、Appleと契約して
いないとできません。普通のやり方では。


.......当然のごとくここを突破して実機向けにコンパイルする手段は
あるのです。
実は、iPhoneアプリ作り始めた当初はこの手段を知らなかったので
僕Yahoo知恵袋で質問したんです。

∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧
pinemukuro



500枚!iPhoneの脱獄ネイティブアプリのコンパイルのやり方を教えて!

pinemukuroさん
僕は今、Xcodeを使ってiPhoneアプリをくんでいるのですが、
コンパイルはAppleと契約して鍵ファイルをもらわないとでき
ないので、仕方なしにシミュレーターで我慢しています。
そこでなんとなく思ったのが、コンパイルだけgccを直接使って
コンパイルし、そして出来上がったバイナリとアイコン等をApp
にまとめて脱獄iPhoneに転送して、実行する方法なんです。
(当たり前なのかもしれませんが.......)
ただ、iPhone向けのgccでコンパイルするときにエラーがでて
しまい、うまくコンパイルできません。
いったいどのようにコンパイルすればよいのでしょうか?
使うコマンドと、引数をどう指定するのかまで教えてください。

∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧
osaka_o_2005




ベストアンサーに選ばれた回答

何を言っているのか分からんのだが?Jailbreakの意味

∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧


なんてツンデレ.......w


きつめに言いつつ、きちんと参考資料を教えてくれたosaka_o_2005
さんには本当に感謝しています。
ということで、教えてくださったページを参考にしてXcodeを設定して、
コンパイルができるようにしました。
AppStoreにリリースすることはできませんが、アプリは完成です。

プロジェクトのフォルダ/build/Release-iphoneos/アプリ名.app

がアプリ本体なので、これをiPhoneなどの

/Applications
又は
/var/stash/Applications.xxxxx

に移します。
ちなみにこの作業は脱獄をおこなっていないとできません。
そしてリスプリングを行うと、作ったプログラムのアイコンが表示され
インストールが完了します。


このことを参考に、Cydiaのリポジトリに作ったプログラムのパッケージ
をアップロードして、管理する手段は今回省きます。



とりあえず、アプリを作る過程はここで全部終了しました。
少しでも、これから始めようとしている人の参考になれば幸いです。

僕はまだ「超初心者」なので、上級者の人からみたら、この記事は改善点
がいくつも見つかることと思います。
が、まあそこは初心者の情弱の書いた記事だと思って見逃してやって
ください...w


堅苦しいですが、最後まで読んでくださりありがとうございます。
ではー。

0 コメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Walgreens Printable Coupons