Study CoreData 15 ~未来は僕らの手の中~

はろーーっ!わーるどっ!

世界は広いぜよ!
 

ってコトで(?)実は僕もご多聞に漏れず『龍馬伝』にハマりまくっちょります!
(NHKの連ドラを全話見てるのははじめてかも。)

見る度に、ワシもこのままではいかんっ!!
志を果たさねばっ!と。

ちっくと”エンゲレス”も勉強せんとのう…とか。

今週からスタートした第三部では、龍馬はしがらみの多い幕末の世の中で世界を相手にビジネスを始めようと奮闘し始めたようです。
しかもたった数人の脱藩浪士らとともに。

まったく規模は違いますが、たったひとりで世界に向けてアプリで勝負を仕掛けようとしている僕ら個人開発者にもそんな龍馬たちの志はまっこと眩しく見えたりもするわけです。
(『ゲゲゲの女房』もたまに観ちょります。)
 
 
…っとほぼCoreDataとは関係のないオープニングでした^^;

んだば、今宵も『ストデークワデェト』はじめるぜよ!!

注意:投稿者自身もCoraDataについて勉強中のため、このシリーズには誤りが含まれている可能性があります。もし、間違いに気付かれた方はコメント欄もしくはtwitterなどでご指摘いただけると幸いです<(_ _)>
また、開発環境はXcode3.2.3 iPhone SDK 4です。実機でのテストなどは自己責任でお願いいたします。

 
 

1. 詳細ビューの表示

前回は『新規データの追加』と『RootViewの表示』をやりましたので、
今回はRootViewから遷移する『DetailViewの表示』から始めていきましょう!

ってコトでまず、
RootViewの[tableView: didSelectRowAtIndexPath:]から見ていきます。
 
以前ここを実装した時には、まだデリゲートを設定していませんでしたので追加しておきましょうね。

次にDetailViewのテーブルビューの表示を完成させます。

#pragma mark -
#pragma mark Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
	return 30.f;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
	if (section)
		return nil;
	return @" ・・・・・・・・・・・・・・・・";
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 3;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	if (!section)
		return 4;
	return 1;
}

始めのふたつのメソッドは特に意味はありませんが、全体的に表示がwindowの真ん中辺りに来るようにしてみました。

それとセクションフッタを使って、Todoを削除するための
deleteButtonの表示も加えておきます。


// for Delete Button
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
	if (section == 2)
		return 65.f;
	return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
	
	if (section != 2)
		return nil;
	
	UIView *footerView = [UIView new];
	footerView.frame = CGRectMake(0.f, 0.f, 320.f, 45.f);
	
	UIButton *deleteButton = [UIButton buttonWithType:111];
	deleteButton.frame = CGRectMake(10.f, 20.f, 300.f, 45.f);
	[deleteButton setTitle:@"Delete Task" forState:UIControlStateNormal];
	[deleteButton setValue:[UIColor redColor] forKey:@"tintColor"];
	[deleteButton addTarget:self action:nil forControlEvents:UIControlEventTouchDown];
	[footerView addSubview:deleteButton];
	
	return [footerView autorelease];
}

※アクションはとりあえずnilにしてあります。

簡単に説明すると、一番下のセクションにフッタを追加して
そこにdeleteButtonをaddしてるってだけです。
(ここでも例の”隠しAPI”を使っています。)
 
 
では、とりあえずこれで表示できそうなのでビルドしてみましょうか。

2. 原因をつきとめる。

あれ?
またダメですね〜(> ~ <;)

でも、運良く(?)エラーが吐かれてますね。

上のハイライトされたところ(ひとつめ)を見てみると、エラーの原因は
Categoryクラスに[valueToString]なんてメソッドないぞ!
ってコトみたいですね。

そりゃそうですよね。
Categoryクラス用の[valueToString]なんて実装してないですから。
 
 
で、“Call stak at first throw:”のあとに書かれているのが
エラーに至った経緯になってます。

これは、
一番下(25)からスタートして、一番上(0)がエラーを出した最後の処理
という順番になっているので、エラーの原因となったメソッドを見つけるには
上から見ていって自分がつくったクラスのメソッドを探せばいいワケです。

ってことで、ふたつめにハイライトしてある箇所がそれですね。
 
では、DetailViewControllerの[cellForRowAtIndexPath:]を確認してみましょう。
 

3. エラーの対処には色々ある。

もう、気付いている方もいるかもですね。

ここでは

NSString *valueString =  [[_editableObject valueForKey:[_valueKeys objectAtIndexPath:indexPath]] valueToString];

としてますが、関連categoryを表示するには
[valueForKey:@”category”]ではなくて[valueForKeyPath:@”category.name”]でしたね。

これを解決するには、普通にif/elseでやっても良いんですが
せっかく分岐処理を減らしたのにまたここで増やすのは気が進まないですよね。

なので、今回は別の方法をとりたいと思います。

吐かれたエラーを見た通り
Categoryクラスに[valueToString]があればエラーを取り除ける
ってコトになりますね。

なので作っちゃいます。

まず、Categoryクラスに[DTDValueCategorys]カテゴリを追加して…

下のように実装します。

つまり、
Categoryクラスの[valueToString]が呼ばれたら@”category.name”の値を返しちゃえ!
ってやってます。
ちょっと乱暴なやり方かもですが、まったく問題はないと思いますので^^;

これでデバッグできましたので、早速ビルドして確認してみましょう!
 
 

4. 人にやさしく

どうです?うまく動きましたか?

ただ、この状態では詳細を編集できない(反映されない)ので
次に、重要度セグメント終了スイッチでの変更を保存できるようにしましょうね。

まずは、その処理の流れを。

1. 各コントロールで値が変更される。
2. 変更を通知する。
3. 変更を保存/キャンセルするためのボタンを表示
4. ボタンがタップされたら保存/キャンセル処理をしてRootViewへ戻る。

こんな感じになりそうですね。

つまり、
各コントロールの[UIControlEventValueChanged]を使う事になるわけですが
これは、RootViewから遷移した直後の
各コントロールに現在の値がセットされた時
にも呼ばれてしまいます。

これだと、常に保存/キャンセルボタンが表示される事になってしまうので
画面遷移時には表示しないようにするためのフラグを先に用意しましょう。

この[_showJudgeButtons]を使って判定します。
 
 
そしたら、先にSave/Cancelボタンタップ後の処理を。

これは簡単ですね。

お次ぎはSave/Cancelボタンの表示です。


- (void)insertJudgeButtons:(BOOL)animated {
	
	if (self.navigationItem.rightBarButtonItem)
		return;		// 既にJudgeButtonsがあるならreturn
	
	
	if (animated) {	// ナビバーをFlip
		
		[UIView beginAnimations:nil context:nil];
		[UIView setAnimationDuration:0.4f];
		[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
		[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft 
							   forView:self.navigationController.navigationBar cache:NO];
	}
	
	UIBarButtonItem *cancelButton = 
	[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel 
												  target:self 
												  action:@selector(judgeButtonTapped:)];
	cancelButton.tag = TAG_CANCEL;
	self.navigationItem.leftBarButtonItem = [cancelButton autorelease];
	
	UIBarButtonItem *saveButton = 
	[[UIBarButtonItem alloc]  initWithBarButtonSystemItem:UIBarButtonSystemItemSave 
												   target:self 
												   action:@selector(judgeButtonTapped:)];
	saveButton.tag = TAG_SAVE;
	self.navigationItem.rightBarButtonItem = [saveButton autorelease];
	
	if (animated)
		[UIView commitAnimations];
}

rightBarButtonItemがあるなら既にボタンが表示されている状態なので、何もしません。

で、Save/Cancelボタンをナビバーに追加するのですが
その時にただ表示するよりもアニメーションをつけて表示した方が分かりやすいと思ったので
navigationBarを回転させて表示させてます。
 
 

こういった形で効果的にアニメーションを使う事で
ユーザーに次の操作を促す
という方法は色々なアプリで使えるナイスな手段なんじゃないでしょうか。

 
ってコトで、次の処理へ。
次は、2つのコントロールから呼ばれるアクションメソッドを実装します。

ここでは、どちらのコントロールから呼ばれたのか(sender)を取得して[_editableObject]の値を変更。
その後に[insertJudgeButtons]を呼べばOKですね。

こんな感じで。

この[controlValueChanged:]が呼ばれるのは、前述した通り
『RootViewからの遷移直後』と『ユーザーが値を変更した後』でしたね。

この場合、遷移直後に呼ばれた時には[_showJudgeButtons]が”NO”なので
JudgeButtonsは表示せずに[_showJudgeButtons]を”YES“にします。
で、値が変更されてこのメソッドが呼ばれた時には”YES”になっているので
JudgeButtonsを表示させるって寸法です。
 
 
あとはこのメソッドを各コントロールのアクションに割り当てます。

それと[dealloc]での解放処理を今まで明示していなかったので、念のため載せておきますね。

- (void)dealloc {
	
	self.delegate = nil;
	self.editableObject = nil;
	
	_priorityControl = nil;
	_compSwitch = nil;
		
	self.titles = nil;
	self.valueTitles = nil;
	self.defaultValues = nil;
	self.valueKeys = nil;
	
    [super dealloc];
}

これでOK!
さ、ビルドして確かめてみましょう!
 
 

5. 思索を制する者は 荒波を制す。


 
どうやらうまくいったようですね。

これで、RootViewからの遷移とDetailViewでの値の変更は出来るようになりました。
…と、ここで一旦作業を休めて
『ここから完成までの道すじ』について整理しておくことにしましょう。
 
 
今のところ考えられるのはこんな感じでしょうか。

・編集用ViewControllerでの編集処理。
・リレーションシップ(関連)の編集と管理。
・”Add Details”を使った画面遷移と編集。
・”Save & Show List”での保存と画面遷移。
・検索機能の追加。
・アプリのブラッシュアップ(メモリ管理や高速化など)
・その他、バグフィックス等。

 
TodoCoreでやってきた事とダブるところもありますが、
リレーションシップの部分や検索機能など今まで手を付けていなかった内容も出てきそうです。

ただ、こうして書き出してみると少しゴールが見えてきた感じですね!
 
 
と、そんなこんなで今日はここまでにしておきます。

それではまた明日。
 
 
ニッポンの夜明けは近いぜよ!

広告

Study CoreData 15 ~未来は僕らの手の中~」への1件のフィードバック

  1. ピンバック: [朝刊] 本の中身がサクサク読める。Googleブックス開始。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中