« 2010年6月 | トップページ | 2010年9月 »

2010年7月

2010年7月 7日 (水)

QuartzDemoを読む 1

iPhone OS Reference Libraryに、QuartzDemoというサンプルプロジェクトがある。
今回はこれを読みます。

なんかファイルがいっぱいあるんで関係を整理したい。

Classes/AppDelegate.h/m:
ビューとナビゲーションコントローラを設定する。

Classes/MainViewController.h/m:
デモへのメインインタフェースを実行する。ユーザは表示するデモを選択する。
Implements the main interface to the demo application, allowing the user to display which of Quartz's drawing facilities to demonstrate.

Classes/QuartzView.h/m:
UIViewのサブクラス。これは他のデモのスーパークラス。

Classes/QuartzViewController.h/m:
UIViewControllerのサブクラス。一つのQuartzViewを管理し、ズームやパンをする。

以下は、 QuartzViewControllerのサブクラス
Classes/QuartzBlendingViewController.h/m:
Classes/QuartzPolyViewController.h/m:
Classes/QuartzGradientController.h/m:
Classes/QuartzLineViewController.h/m:
Classes/QuartzDashViewController.h/m:


以下はQuartzViewのサブクラス
Quartz/QuartzLines.h/m:
Quartz/QuartzPolygons.h/m:
Quartz/QuartzCurves.h/m:
Quartz/QuartzImages.h/m:
Quartz/QuartzRendering.h/.m:
Quartz/QuartzBlending.h/.m:
Quartz/QuartzClipping.h/m:

○MainViewController

これは、UITableViewControllerのサブクラス。
メソッド:viewDidLoadで、各ビューコントローラを生成し、テーブルビューの要素に追加している。
行が選択されると、対応するビューコントローラが表示対象になる。

コントローラを生成する方法に2つある。
1.controller = [[QuartzViewController alloc] initWithNibName:@"DemoView" viewClass:[QuartzLineView class]];
2.controller = [[QuartzLineViewController alloc] init];

2つ目の方も、QuartzLineViewControllerのinitメソッドで下記のようになる。
-(id)init
{
return [super initWithNibName:@"LineView" viewClass:[QuartzCapJoinWidthView class]];
}

NibNameは、UIViewControllerの指定で、viewClassは、そこに乗せるUIViewだろう。
単純な表示のみの画面は、DemoViewで表示し、ユーザが選択するものは個別のxibを指定している。


○QuartzViewController

プロパティにQuartzViewを持っている。
QuartzView *quartzView;

メソッド:-(id)initWithNibName:(NSString*)nib viewClass:(Class)vc が生成を担当。

○QuartzView.h

メソッドが1つ定義されてるだけ
// As a matter of convinience we'll do all of our drawing here in subclasses of QuartzView.
これを使って、QuartzViewのサブクラスで全ての描画を行う。
-(void)drawInContext:(CGContextRef)context;

ヘッダーには定義されていないが下記の実装がある。(UIViewのメソッドのオーバーライド)
描画のタイミングでこれが呼ばれる模様。
-(void)drawRect:(CGRect)rect
{
// Since we use the CGContextRef a lot, it is convienient for our demonstration classes to do the real work
// inside of a method that passes the context as a parameter, rather than having to query the context
// continuously, or setup that parameter for every subclass.
[self drawInContext:UIGraphicsGetCurrentContext()];
}

今回はここまで

| | コメント (0) | トラックバック (0)

2010年7月 6日 (火)

iphone 開発 サンプル「Simple Undo」を読む。

iPhone OS Reference Libraryに、Simple Undoというサンプルプロジェクトがある。
今回はこれを読みます。

サンプルCoreDataBooksと似てるけど、
あっちはCoreDataのはなしとUndoManagerのはなしが入り交じってて、
正直理解できなかった。私が馬鹿なだけ?

○Undo/Redoの実施について
情報を編集しsaveをタップしたあと、
完了する前にiphoneをシェイクするとundo/redoが起動するようです。

○ファイル構成

SimpleUndoAppDelegate.{h,m}
ブックと最初のビューコントローラを設定する。

RootViewController.{h,m}
ブックについての情報を表示するテーブルビューを管理する。

EditingViewController.{h,m}
編集用のビュー

Book.{h,m}
本をあらわす単純なオブジェクト

このサンプルはCoreDataを使ってないみたい。よかったよかった。

まず、Bookを見る。
下記3つの情報の入れ物。単純。
NSString *title;
NSString *author;
NSDate *copyright;


次に、RootViewController

宣言部分
@interface RootViewController : UITableViewController
となってる。PropertyEditingとは何か?

EditingViewController.hで定義している。
@protocol PropertyEditing
- (void)setValue:(id)newValue forEditedProperty:(NSString *)field;
@end

今までのパターン的には、RootViewControllerを表すdelegateをEditingViewControllerが持ってて、
EditingViewControllerの中で、[delegate setValue]とすることが予想される。

EditingViewController.hのこの部分がそうなんだろう。
id sourceController;

つまり、Rootの方のBookが入力完了後の本物で、
Editingの持ち物はUndoされ得る編集中の途中データのはずです。

ここから、実装について見ていく。

○RootViewController.m
テーブルビューについての話は、飛ばします。

メソッド:setEditing:animated:について
これは、UIViewControllerのメソッドで、テーブルビューを編集中にするかどうかを決める。
これを呼び出す部分は?

明示的な呼び出しはない。
 - (void)viewDidLoad {
self.navigationItem.rightBarButtonItem = self.editButtonItem;
の部分でナビゲーションバーの右側に編集ボタンをつける、という指示をすると、
上記メソッドと結びつく模様。

とりあえずsetEditing:animated:が呼び出されたとき、
これから編集を行うのであれば、undoManagerを生成し、完了したのであれば廃棄している。

NSNotificationCenterは、Undo/Redoの実施が行われた通知を受ける監視者?


メソッド:setValue:forEditedProperty:について

実装の上にコメントがある。翻訳。
##############################################################
このメソッドはブックの値を更新する。
そしてundo/redoの操作も同時に登録する。
実施にはinvocationを使う。なぜならこのメソッドには2つの引数が要るから。
このメソッドは、編集ビューでsaveがタップされたときに実施される。
##############################################################

invocationというところは、undo/redoの実装方法に、simple/invocationの選択肢があり、
1つの値を扱うときはsimple、2つ以上のときはinvocationを使うようです。

まず、undoManagerに「現在の」値を保存
[[undoManager prepareWithInvocationTarget:self] setValue:currentValueforEditedProperty forEditedProperty:field];

つぎに、「編集中の」値を保存(この時点で、bookの値は一時的に編集途中のデータになっている)
[book setValue:newValue forKey:field];

これはよくわからんが、今がundo実施中でないとき(=通常の値セットのとき)undo実施時のキャプション表示に備えて、
フィールドのなまえを登録しているのかも
if (![undoManager isUndoing]) {
[undoManager setActionName:NSLocalizedString(field, @"string provided dynamically")];
}

○SimpleUndoAppDelegate.m

下記の実装によって、shakeでundo/redoが行われる模様。セットしなくてもデフォルでもYESとのこと。
// Tell the application to support shake-to-edit.
application.applicationSupportsShakeToEdit = YES;


○EditingViewController
とくに見るところがなかった。
デリゲートのメソッドを実施しているこの辺くらい。
- (IBAction)save {
if (editingDate) {
[sourceController setValue:datePicker.date forEditedProperty:editedPropertyKey];
}
else {
[sourceController setValue:textField.text forEditedProperty:editedPropertyKey];
}

読んだ限り、シェイクしたときの画面のレイアウトや、undo/redoの実施は自動で行われている模様。
そのソースがない分、直感的には理解しづらいが、
わかってしまえばソースをかかなくてよいので簡単。

おわり。

| | コメント (0) | トラックバック (0)

UITableViewについて

・前提
Interface Builder(IB)は、使用しません。

iphoneでリスト形式の表示をするときは、UITableViewを使います。
[図1挿入]
1


その方法として、UITableViewControllerを利用する方法があります。

ただ、この方法では、画面全体にテーブルが描画されるため、
UIToolBarをテーブルの下部に入れるときなどに問題があります。

具体的には、ツールバーをサブビューとして追加すると、
テーブルをスクロールしたときに、
ツールバーも同様にスクロールして移動してしまいます。

解決案として、UITableViewControllerは使用せずに、
UIViewControllerを使用する方法を紹介します。

UITableViewControllerとツールバーを併存させる方法はあるかもしれませんが、
UITableViewを直接操作する方が柔軟性が高いでしょう。

ネットで検索すると、UITableViewControllerを直で使うケースは稀だよ、となってたりもします。

製品ドキュメントをUITableViewで検索し、重要な部分を翻訳します。
-----------------------------------------------------------------------------------
UITableViewオブジェクトは、データソースとしてふるまうオブジェクトと、
デリゲートとしてふるまうオブジェクトを持つ必要がある。

典型的には、これらは、アプリケーションのデリゲートであるか、もっとも頻繁な場合だと、
カスタムなUITableViewControllerオブジェクトである。

データソースはUITableViewDataSourceプロトコルを採用する必要がある。

また、デリゲートはUITableViewDelegateプロトコルを採用する必要がある。

UITableViewDataSourceプロトコル
required methodは以下)
– tableView:cellForRowAtIndexPath:
– tableView:numberOfRowsInSection:

UITableViewDelegateプロトコル
required methodはなし、必要そうなもの)
– tableView:didSelectRowAtIndexPath:
– tableView:editingStyleForRowAtIndexPath:
-----------------------------------------------------------------------------------
テーブルビューのプロパティ、delegate/dataSourceに、上記プロトコルを採用したオブジェクトを
持たせる必要がある、ということでしょう。

最小限の機能のサンプルソースを作りました。
(ソネットのプロバイダー契約が切れたため、下記リンクは無効になりました。
リクエストがあれば、なんとか、どこかにアップします。)

実行すると、画面の一部にテーブルビューを描画します。
[図2挿入]
2


サンプルソース

・サンプルソースの解説

サンプルソースでは、delegate/dataSourceをそれぞれを別のクラスにしました。

UITableViewを持つビューコントローラ
@interface TableViewTestViewController : UIViewController {
UITableView *tableView;
}

データソース
@interface MyDataSource : NSObject {
NSMutableArray *array;
}

デリゲート
@interface MyDelegate : NSObject {

}

データソースは、NSMutableArrayを所持しており、これは自身のinitメソッドで初期化しています。
デリゲートは、プロトコルに必須メソッドがないため、実態は空です。

ビューコントローラで、tableViewを生成し、サブビューに追加することで画面に描画します。

これだけで表示できるようですが、UITableViewControllerの製品ドキュメントに、下記の記述があります。
-----------------------------------------------------------------------------------
テーブルビューが最初に描画されるとき、テーブルビューコントローラは、データソースを再読込する。
またテーブルビューが表示されるとき、選択状態を常にクリアする。これは、viewWillAppearメソッドで行う。

テーブルビューがあらわれたとき、コントローラはスクロール指示を初期化する。
これは、viewDidAppearメソッドで行う。

ユーザがEdit/Doneをタップしたときのために、スーパークラスのsetEditing:animated:を実行します。
-----------------------------------------------------------------------------------
UITableViewControllerと同等の機能をUIViewControllerで実施するためには、
これらを明示的に行う必要があります。

これに関して、以下のURLを参考にさせていただきました。
http://d.hatena.ne.jp/KishikawaKatsumi/20090121/1232548786

以上

| | コメント (0) | トラックバック (0)

2010年7月 3日 (土)

iphone用アプリ「乱語」サポートページ

このたび、App Storeにアプリ「乱語」をリリースを申請しました。
乱語

このページでサポートを行います。

不具合/質問/感想などありましたら、書き込みをお願いします。

2010/7/12にApp Storeから購入可能になりました。

| | コメント (0) | トラックバック (0)

retain autorelease ドット演算子

iPhoneアプリ用のソースをかくときに、
retainCountの動きを理解するのが難しい。

よくわからなかったのが、

@property (retain) NSMutableArray *array
というプロパティがあったときに、
self.array = [NSMutableArray array];

array = [NSMutableArray array];
では、retainCountに違いが出ること。

selfの方は2で、selfじゃない方は1になる。

これは、たぶんドット演算子でアクセスする場合、
retainの指示があるプロパティとしてアクセスするから、
生成+プロパティアクセスで2になる。

ためしに、
@property (assign) NSMutableArray *array
としたら、self.arrayの方もretainCounterは1になった。

また、[NSMutablerArray array]で生成されるオブジェクトは、
[[NSMutableArray alloc] init] autorelease]と同義。

ただ、ドキュメントを見ても、
生成メソッドがautoreleaseして返すか否かはかいてないみたい。

initXXX以外はautorelease付きと思えばよい?
→訂正)alloc,copy,newという語以外がautorelease付きの模様

まとめると、プロパティに何かを保存するときは、
・どのように生成するか(autoreleaseか否か)
・どのように保存するか(ドット演算子か否か)
・(ドット演算子を利用する場合)属性はretain/assign/copyのいずれか
を考える必要がある。

プロパティにイベントループをまたがる情報を持つ場合
・autoreleaseを利用しない
・ドット演算子を利用しない
ことによって、生成後のretainCountが1になるようにして、
deallocでreleaseするのが良い気がする。

どうでしょう。

追記)retainCountは、あくまでデバッグ用の参考で、重要なのは、自分が増やしたカウントは
責任を持って減らすこと。(まずは増やしているかいないかを知ること)

他人の作ったソースで、retainCountがいくつ増えるかはわからない。
(1だと思ってたら3とか4とかになってかえってくることも)

下は英語だけど日本語もどこかにあるはず。
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

| | コメント (0) | トラックバック (0)

« 2010年6月 | トップページ | 2010年9月 »