CoreDataを使っていて、リレーションなどごちゃごちゃやって、関連するデータを取り出してみたら、そのデータがソートされていない。単純にDBに保持されているものを持ってくるだけだから、並び替えしていないってことで、自前で並び替えなければならない。SQLが使えれば楽なのに。。。

で、調べてみたので、残しておく。

※ 下記ソースの簡単な説明として、CoreDataから1つのteamDataを取得し、それに紐付くNSSet型のmembersのデータ(Member *)を、複数の条件で並び替えすることを想定している。

// CoreDataのリレーションで関係づけられたデータは、NSSet型で保存される.

NSSet *members = teamData.members;


// ソートする条件を指定。

NSSortDescriptor *sort1 = [NSSortDescriptor

                           sortDescriptorWithKey:@"memberNo"

                           ascending:YES] ;

NSSortDescriptor *sort2 = [NSSortDescriptor

                           sortDescriptorWithKey:@"activeFlag"

                           ascending:NO] ;


// ソートする条件を配列で保持。

NSArray *sortDescriptors = [NSArray arrayWithObjects:

                            sort1,sort2,nil];

// NSSet型のmembersをソートして配列membersArrayに保持。

NSArray *membersArray = [members sortedArrayUsingDescriptors:

                         sortDescriptors];


// 結果を見てみよう。

for (Member* memberData in membersArray) {

    NSLog(@"memberData   %@ %@",

               memberData.memberNo,

               memberData.activeFlag);

}


これで並び替えができているはず。(ただし、このソースはサンプルで、実際にコンパイルはしていないので悪しからず)

また、もしソート条件が1つだけの場合には、arrayWithObject: を使えばよい。複数の場合の arrayWithObjects: は最後の"nil"が大事。忘れてコンパイルが通らなくて焦った。

NSArray *sortDescriptors = [NSArray arrayWithObject:sort1];



PHPだったらsortOn("memberNo"); とかやればいいので便利だけど、Objective-Cはまだ慣れない。。。


普通にDBを使っていて、プライマリキーとかユニークキーとか当たり前に使うものだと思っていたけど、CoreDataを利用するにあたってはその概念が当てはまらない。とはいえ、SQLiteをラップしているので、裏側ではちゃんとプライマリキーとか持っているようだが、表だって使うことができないのだ。

でもいろいろなviewを行き来するのに、そのテーブル(エンティティ)内の一意のデータのキーを持ちまわるのが妥当と判断したため、どうしても PRIMARY KEY に AUTO INCREMENT を設定したカラムを使いたかったのだが、いろいろ調べた結果、直接、PRIMARY KEY も AUTO INCREMENT も使うことができないことが判った。(実は良い方法があるのかもしれないが、そこまでCoreDataを熟知していない。SQLiteを直接実行する方法もあるのかな?)

で、もっと調べて、代わりに利用できそうなのが NSManagedObjectID である。これは、TableView(っていうかfetchedResultsController) で利用される indexPath と似ているので理解が早い。


使い方は簡単。
まず最初に、取得した ManagedObject から objectID を保持。
 
NSManagedObjectID *objID = [managedObject objectID]; 

NSLog(@"entryID == %d", objID );

 
保持した objectID を使って、managedObjectContext から目的の ManagedObject を取り出す。

NSManagedObject *managedObject

           = [managedObjectContext objectWithID:objID];


とりあえず、これで目的の挙動の代用とすることができる。

ただ、この objectID の注意点としては、 AUTO INCREMENT のように1からの連番ではないということ。
また、コンパイル毎に違う数字が返される。ってことは、DB上で持っている数字ではなく、システムで与えられる数字なのだろう。この点は TableView で利用される indexPath も同じことが言える。

 

まだまだ勉強中のため、間違いがあればご指摘ください。

CoreDataの勉強中でまだまだ自分のものにできていないのだが、そんな自分の強力なサポートとして、CoreDataの発行するSQLをデバッグログに表示してくれる方法がある。

ずいぶん前に設定したのだが、新しいプロジェクトに設定するのに忘れないよう残しておく。

まずは「プロジェクト」タブの一番下「アクティブな実行可能ファイル・・・」を選択。

screenshot_1.png

「引数」タブの「起動時に渡される引数」に下記のコードを追加。

-com.apple.CoreData.SQLDebug 1

screenshot_2.png
下の変数はデバッグ用のコード。

これでコンソールにCoreDataのログが出力される。

[31597:207] CoreData: sql: BEGIN EXCLUSIVE

[31597:207] CoreData: sql: SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?

[31597:207] CoreData: sql: UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?

[31597:207] CoreData: sql: COMMIT

[31597:207] CoreData: sql: BEGIN EXCLUSIVE

[31597:207] CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'

[31597:207] CoreData: sql: DELETE FROM Z_METADATA WHERE Z_VERSION = ?

[31597:207] CoreData: sql: INSERT INTO Z_METADATA (Z_VERSION, Z_UUID, Z_PLIST) VALUES (?, ?, ?)

[31597:207] CoreData: sql: INSERT INTO ZBOOK(Z_PK, Z_ENT, Z_OPT, ZTITLE, ZWRITER) VALUES(?, ?, ?, ?, ?)

[31597:207] CoreData: sql: COMMIT

[31597:207] CoreData: sql: pragma page_count

[31597:207] CoreData: annotation: sql execution time: 0.0018s

[31597:207] CoreData: sql: pragma freelist_count

[31597:207] CoreData: annotation: sql execution time: 0.0012s

前回から1カ月以上たっているが、開発中のiPadアプリで、本体の回転に関するバグが発生。
前回の「iPhone SDK 本体を回転させた時の処理で悩んだのでメモ」の記事で本体の回転についての記事を書いているが、これには考慮不足があったため、改めてメモを残す。
 
要件としては、本体のローテート状態を取得する [[UIDevice currentDevice] orientation] について、orientationのプロパティ値について、本体画面の上・下・左・右の他に、水平方向の裏・表が存在しているというもの。
これを知らず、単純にIF分で「横向きの場合とそれ以外」と指定していたため、テーブルの上に置いて操作した場合に、画面が横向きにもかかわらず、処理の途中で縦向きの計算をしてしまっていたのだ。
 
で、判別するプロパティ値のイメージは以下の通り。

UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

if (orientation == UIDeviceOrientationUnknown ){

    NSLog(@"デバイスの向きが不明");

}else if (orientation == UIDeviceOrientationPortrait ){

    NSLog(@"ホームボタンが下にある状態");

}else if (orientation == UIDeviceOrientationPortraitUpsideDown ){

    NSLog(@"ホームボタンが上にある状態");

}else if (orientation == UIDeviceOrientationLandscapeLeft ){

    NSLog(@"ホームボタンが右にある状態");

}else if (orientation == UIDeviceOrientationLandscapeRight ){

    NSLog(@"ホームボタンが左にある状態");

}else if (orientation == UIDeviceOrientationFaceUp ){

    NSLog(@"ホームボタンが真上を向いている状態");

}else if (orientation == UIDeviceOrientationFaceDown ){

    NSLog(@"ホームボタンが真下を向いている状態");

}


 
回避方法としては、willRotateToInterfaceOrientation()で、toInterfaceOrientationの値を変数に保持してしまい、各所の処理ではその値を利用するのが得策であるろう。
 
ちなみに、willRotateToInterfaceOrientation()での引数値には、UIDeviceOrientationFaceUp 、UIDeviceOrientationFaceDown の値は入ってこないようだ。これは、引数 toInterfaceOrientation が「これから画面の向きを変える方向」なので、水平の場合でも画面の向きが優先されると考えられる。
そうでないと、純粋に回転状況が取れず、どうしてよいか分からなくなるので助かる。
そんなこんなで覚えておきたい。
iPhone SDK でiPadアプリを製作中。
そこら中に落ちているサンプルを拾い集めながら何とか進めているけど、やはり理解しながら進めないと自分の実力にならないので、合間を見つけて勉強を進めている。

で、まずはメモとして、本体を回転した時にビューの向きを変える設定。というか、Controller.m に以下を書くだけ。

// 自動回転を有効にするサブクラス
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  return YES;
}


これで、自動でビューを回転してくれる。
でも、これだけではビューを画面にフィットさせることはできない。
回転時に画像のサイズ変更とかボタンの表示位置とかを修正する必要がある。
で、それを実装するのが、 willRotateToInterfaceOrientation() か didRotateFromInterfaceOrientation() で、画面を回転させる処理を開始する前と完了した後に呼び出される関数である。この他に、willAnimateFirstHalfOfRotationToInterfaceOrientation()、willAnimateSecondHalfOfRotationFromInterfaceOrientation() という処理もあり、回転のアニメーションの前半と後半に分けた場合にそれぞれ呼び出される。これ以外にもいくつかあるけど試す余裕はない・・・。

で、実際に回転した時に縦方向とか横方向を判定する方法についてだが、引数で取得する回転方向が、正常・反転・右・左のどれにあたるかを判別すればよい。
ここでいう、正常・反転・右・左とは、ホームボタンの位置と考えれば判りやすい。

正常: UIInterfaceOrientationPortrait
反転: UIInterfaceOrientationPortraitUpsideDown
右 : UIInterfaceOrientationLandscapeRight
左 : UIInterfaceOrientationLandscapeLeft

以下に、willRotateToInterfaceOrientation()、 didRotateFromInterfaceOrientation() でのテスト用コードを書く。実際にはこのif文をいじって、ボタンの位置とかを変えればよい。

// 回転を開始する前に行っておく処理
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
  NSLog(@"ローテイト");
  if(toInterfaceOrientation == UIInterfaceOrientationPortrait){
    NSLog(@"will to   Portrait");
  }else if(toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ){
    NSLog(@"will to   UpsideDown");
  }else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight ){
    NSLog(@"will to   LandscapeRight");
  }else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft){
    NSLog(@"will to   LandscapeLeft");
  }else{
    NSLog(@"will 何が起きた?!");
  }
}


// 回転が完了した後に行っておく処理
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
  if(fromInterfaceOrientation == UIInterfaceOrientationPortrait){
    NSLog(@"did  from Portrait");
  }else if(fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ){
    NSLog(@"did  from UpsideDown");
  }else if(fromInterfaceOrientation == UIInterfaceOrientationLandscapeRight ){
    NSLog(@"did  from LandscapeRight");
  }else if(fromInterfaceOrientation == UIInterfaceOrientationLandscapeLeft){
    NSLog(@"did  from LandscapeLeft");
  }else{
    NSLog(@"did  何が起きた?!");
  }
}


ここで、注意しなければならないのは、それぞれの関数で受け取る回転方向の情報である。
will~の方は、toInterfaceOrientation で、did~の方は、fromInterfaceOrientation を受け取っている。
"to"と"from"の違いは、意味通り、回転前はこれから回転する方向で、回転後はどの方向から回転されたかを示すのだ。
この引数を意識していなかったため、NSLogの結果の相違にパニクってしまった。

これで本体の回転については理解することができた。

関連記事として iPhone SDK 本体の回転 [[UIDevice currentDevice] orientation] で落とし穴が を追加。合わせてお読みください。

ビューンが復活

| コメント(0) | トラックバック(0)
iPad発売当初、色々な雑誌が読めると話題になったが、利用者が多すぎたせいか、重すぎて何も表示されず不評だったビューンが、Wi-Fi 対応のみで復活した。

mzl.jpqumane.480x480-75.jpg
現在はプレ配信期間ということで無料。また、正式再開後も30日間は無料で利用でき、それ以降は30日間で450円とのこと。
複数の新聞や雑誌の主要記事を読むことができ、この値段なら安いと思うが、現在Wi-Fiでしか利用できない。電車の中で見るには、その雑誌を最初に一通り表示してキャッシュに入れておけば、Wi-Fi接続していなくても読むことができるようだ。ただ、キャッシュの量がどのくらいかは未確認。

実際に試してみたところ、前のように読み込みが遅すぎるということもなく快適。操作性も悪くないし、目次表示+ページ移動などもできるので読みたい部分だけピンポイントで読むことができる。また、リアルタイムのニュースも表示されるのも便利である。
ただ、Wi-Fi につながっているはずなのだが、「Wi-Fi接続してご覧ください」とでた。何でだろう?

ビューン
URL:http://www.viewn.co.jp/
http://itunes.apple.com/jp/app/id372350361?mt=8

2011年2月

    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          

最近のコメント

アイテム

  • screenshot_1.png
  • screenshot_2.png
  • mzl.jpqumane.480x480-75.jpg
Powered by Movable Type 5.02