2010年6月13日日曜日

サイトをリニューアルしました

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
新しいサイトはこちらです↓

Cocoaの日々

※このサイトは閉鎖せずに今後も公開しておきます。

長い間ありがとうございました。
よければ新しいサイトの方も訪問してみて下さい。

2010年6月11日金曜日

UITableView の editingモードで左側の削除アイコンを消す

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
これを

こうする

UITableViewDelegate の2つのメソッドを実装すれば良い。


- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
 editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
- (BOOL)tableView:(UITableView *)tableView
 shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}

2010年6月4日金曜日

NSFetchedResultsController でグルーピング(Section分け)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
概要
NSFetchedResultsController のインスタンスを作る時に sectionNameKeyPath: へグルーピング条件となるKeyPathを渡すと、簡単に UITableView でグルーピングができる。


例えばこれが
こうなる。


sectionNameKeyPath:
インスタンス作成時のコードはこんな感じ。


NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:coreDataManager.managedObjectContext
sectionNameKeyPath:@"treatedYear" cacheName:@"Root"];



@"treatedYear" を渡している。これがグルーピング条件(今回は「年」)となる。
さて、この treatedYear(年) だが、検索対象のエンティティには実は存在しない。
treatedDate が存在するが、これは NSDate(年月日時分秒)の属性。


treatedDate そのままでは年単位のグルーピング条件に成り得ないので、年を別途用意する必要がある。それが treatedYear。この treatedYear はメソッドとして存在するだけのテンポラリな値で永続化対象にはしていない。




@implementation Record (Extension) ← RecordはNSManagedObjectのサブクラスで、カテゴリでメソッドを追加
- (NSInteger)treatedYear
{
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateComponents =
[calendar components:NSYearCalendarUnit
fromDate:self.treatedDate];
return [dateComponents year];
}

NSFetchedResultsController がグルーピングを行う際に、初期化時に指定された sectionNameKeyPath:@"treatedYear" を元にこのメソッドを呼び出す。このメソッドでは treatedDate(NSDat型:年月日時分秒)から年の値だけを取り出して返している。これでグルーピング条件を提供できる。


表示するセクション名
グルーピングするにはさらに UITableViewDataSource の tableView:titleForHeaderInSection: を定義する必要がある。このメソッドが定義されていない場合はグルーピング表示されない。

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
  return [NSString stringWithFormat:@"%@",
[[[fetchedResultsController sections] objectAtIndex:section] name], nil];
}

これで出来上がり。



- - - -
件数が極端に多い場合は「年」をエンティティの属性として追加しておいた方がパフォーマンス的にはいいかもしれない。

2010年1月9日土曜日

(本)iPhoneアプリネットワーク+GPSプログラミング

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
iPhoneアプリネットワーク+GPSプログラミング を入手。

目次
  1. iPhone OS ネットワークプログラミング
  2. App Store のネットワークアプリ
  3. ネットワーク&GPSプログラミング技術解説
  4. ネットワークプログラミング解説
  5. パフォーマンス・チューニング
  6. 遠方への iPhoneアプリの配布
  7. iPhoneの魅力とは - アプリ開発スタイル
感想
iPhone におけるネットワークプログラミングと地図/GPSまわりのプログラミングを手っ取り早く知るには良い(その方面の)入門書。アーキテクチャやソースコードの解説があるので、ざっと眺めるだけでも地図を使うプログラミングの雰囲気がわかる。またサーバサイド(PHPやGoogleMap)との連携も取り上げられているので、アプリ単体開発だけでなく iPhoneを活用する Webサービス全体の構成イメージを掴むのにも役立つ。その他パフォーマンスチューニングや AdHoc配布の解説などは他分野の iPhoneアプリ開発でも参考になる。

なお C言語や Objective-C に関しては一切解説は無い。

以下気になった点。
  • プッシュ通知の解説
  • デバッグの方法
  • XMLパース
  • WordPress の活用
  • ネットワーク接続チェックの仕方(WiFi接続の落とし穴)
  • AR
  • 逆ジオコーディング
  • Instruments/Shark を活用したパフォーマンスチューニング
  • AdHoc 配布の手順


2009年12月11日金曜日

写真撮影後に viewDidLoad が呼び出される

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
UIImagePickerController を使って写真を撮影したのち、呼び出し元の UIViewConroller の viewDidLoadが呼び出されていることに気がついた。このメソッドはてっきり最初に一回だけ呼び出されるものと思っていたのでここへ初期化処理を書いておいたのだが、そのせいで写真撮影毎に初期化処理が呼び出されてしまった。また view上の UIImageView へ設定していた画像が表示されない。中身を確認すると nil になっていた。

調べてみると、どうもメモリ不足が原因で UIViewController が view を再作成しているのが原因ということだった。
2009-01-21 - f-shinの日記 - iPhoneアプリ開発グループ


実際、didReceiveMemoryWarning が呼び出されているのがわかった。

- (void)didReceiveMemoryWarning {
 // Releases the view if it doesn't have a superview.
 NSLog(@"didReceiveMemoryWarning");
 [super didReceiveMemoryWarning];
 // Release any cached data, images, etc that aren't in use.
}


おー、そうなのか。

参考本で紹介されていた下記のコードは使えないことがわかる(実際、OS v3.1.2の実機で画像が表示できない)。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissModalViewControllerAnimated:YES];
photoView.image =
 [info objectForKey:UIImagePickerControllerOriginalImage];   
}


なぜなら photoView(UIImageView)はメモリ不足が生じた場合、この直後に再作成されるので。


対策方法の1つとしては viewを開放させない(再作成させない)ことが紹介されていた。
もう一つは viewが再作成されることを前提に実装する方法。


正攻法はやっぱり後者なんだろうな。
その場合、撮影した画像は UIViewController のプロパティへ直接設定するのではなく別の場所へ保存し、表示する時にそれを使うようにする必要がある。

2009年11月25日水曜日

画像を保存する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
UIImagePickerController から取得した画像を保存する。

ファイル名

ユニークにしたいので何かランダムなものにしたい。UUID を使うことにする。


- (NSString*) stringWithUUID {
CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID
//get the string representation of the UUID
NSString *uuidString = (NSString*)CFUUIDCreateString(nil, uuidObj);
CFRelease(uuidObj);
return [uuidString autorelease];
}

- (NSString*)createImageFilename
{
NSString* filename =
[NSString stringWithFormat:@"%@.jpg", [self stringWithUUID]];
return filename;
}



(参考)Cocoaの日々: UUID を作る - CFUUIDCreate と globallyUniqueString


保存先

標準のドキュメントフォルダ直下とする。


- (NSString*)getImageFilepath
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectoryNSUserDomainMaskYES);
NSString* path = [paths objectAtIndex:0];
return [path stringByAppendingPathComponent:self.memo.photo];
}


保存処理

UIImageJPEGRepresentation を使う。


- (void)saveImage
{
NSData* data = UIImageJPEGRepresentation(self.photoView.image,0.5);
[data writeToFile:[self getImageFilepath] atomically:NO];
}

2009年11月20日金曜日

UIKit Functions

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
いわゆるユーティリティ系の関数があるのに気がついた。

画像系だとこんなのがある。

NSData * UIImageJPEGRepresentation (
UIImage *image,
   CGFloat compressionQuality
);
NSData * UIImagePNGRepresentation (
UIImage *image
);
void UIImageWriteToSavedPhotosAlbum (
UIImage  *image,
   id       completionTarget,
   SEL      completionSelector,
   void     *contextInfo
);

画像を保存する時に使えそう。


(追加)画像の処理については下記の記事が詳しい。
UIImagePickerController, UIImage, Memory and More! - Stack Overflow

基本的なことから画像の縮小や保存まで書かれていて参考になる。