i phoneアプリ入門 第3回
DESCRIPTION
iPhoneアプリ開発勉強会第3回資料ですTRANSCRIPT
第3回 iPhone勉強会
13年3月17日日曜日
・第3回 他の画面の値を変更する方法MVCモデル、タイマーの使い方
・第4回 TableView ユーザ操作(タップなど)の検出方法・第5回 MapKitを使った地図の表示方法
現在地の取得方法
今後の予定
13年3月17日日曜日
・前回の復習・他の画面の値を設定する・MVCモデル・タイマー(一定時間ごとに処理を実行)・サンプルアプリ作成
今日のアジェンダ
13年3月17日日曜日
今日のスケジュールは以下の通りです。まず、前回の復習を軽く行った後、他のオブジェクトの値変更の仕方およびMVCモデルについて説明した後、時間がありそうだったら、タイマーとテーブルビューの説明をいたします。その後、今日の課題となるサンプルアプリを作成いたします。今日も出来るだけ手を動かすようにしたいと思います。よろしくおねがいいたします。
~以下のようなアプリを作ってみましょう~
前回の復習
押すと次の画面へ移動する 動かすとフォントサイズが変わる
13年3月17日日曜日
まず、前回の復習をしましょう。前回受講したという前提で、まずはコードを書いてみます。図のようなアプリを作ってみましょう。iPhone上でボタンを押したら次の画面が表示される何のことだかさっぱりわからないと言う方、こっそり教えてください・・・。制限時間は20分とします。
他の画面の値を設定する
view1 view2fontSize
view1でview2のフォントサイズを変えたい
view2というオブジェクトにアクセスしてフォントサイズを変える
13年3月17日日曜日
ある画面で設定した値を他の画面へ反映するようにしたいと思うのではないでしょうか。図のようにview1でview2のフォントサイズを変更するという処理をするにはどうすればいいでしょうか。ここでよく思い出してみましょう。Objective-CはJavaと同じオブジェクト指向です。Javaのときはどうしていたでしょうか。。。Hoge hoge = new Hoge();hoge.setMoge(“AAA”);などとというようにオブジェクトにアクセスして値を変更していたと思います。
他のオブジェクトにアクセスする方法
view2fontSize
//View2というオブジェクトを作る(またはどこかからもってくる)View2 *view2 = [[View2 alloc]init];
//View2というオブジェクトのfontSizeを変更するview2.fontSize = 12;
「fontSizeを変更する」というメッセージを送る
13年3月17日日曜日
Objective-Cでもおなじことです。View2というオブジェクトを新しく作成するかまたはどこかから取得して、View2というオブジェクトに対して「フォントサイズを変更してくれ」というメッセージを送るのです。
「復習」での具体例
・
ViewController DetailViewController
DetailViewControllerの値を変更したい
どちらかをやりたいViewControllerからDetailViewControllerの値を変更DetailViewControllerからViewControllerの値を取得
13年3月17日日曜日
では、復習で行った場合の具体例を見ていきましょう。実装の都合上、ViewControllerで設定した値をDetailViewControllerで表示するというようにします。この場合ですと、ViewControllerクラスからDetailViewControllerに対してアクセスするか、DetailViewControllerクラスあらViewControllerクラスにアクセスすればいいのです。どうすればいいのでしょうか既にViewController、DetailViewControllerで画面を作っているため、オブジェクト自体を新しく作り直す訳にはいきません。いくつかやり方はあるのですが1つ紹介します。
ViewControllerからDetailViewControllerの値を変更する
ViewController
- (void)prepareForSegue:(UIStoryBoardSegue*)se
gue
DetailViewController
float(nonatmic) fontSize;UILabel *label
ViewControllerからDetailViewControllerに遷移するときに値を変更
13年3月17日日曜日
先ほど復習で使ったプロジェクトを編集し、ViewControllerで設定したフォントサイズでDetailViewControllerの画面を表示するようにしましょう。まず、StoryBoard画面を開いて、ViewControllerにSliderとStepperを配置し、ヘッダファイルに反映させましょう。そして、ViewController.mに- (void)prepareForSegue:(UIStoryBoardSegue*)segueというメソッドを記載してください。
2つのViewControllerにアクセスするメソッド
- (void)prepareForSegue:(UIStoryBoardSegue*)segueボタンを押したとき(viewを移動するとき)に呼ばれるメソッド
UIStoryBoardSegue:2つのViewControllerを行き来するときに使うオブジェクトsourceViewController:(遷移元のViewController)destinationViewController:(遷移先のViewController)
13年3月17日日曜日
- (void)prepareForSegue:(UIStoryBoardSegue*)segueはボタンを押してViewを移動するときに呼ばれるメソッドで、引数のsegueにはsourceViewControllerという遷移先のViewControllerとdestinationViewControllerという遷移先のViewControllerが格納されています。ここではdestinationViewControllerへアクセスすることで遷移先のViewControllerで保持している値を変更することが出来ます。
2つのViewControllerにアクセスするメソッド
- (void)prepareForSegue:(UIStoryBoardSegue*)segueヘッダファイルに宣言していないのになぜエラーにならない?
prepareForSegue はUIViewControllerで宣言されているから
13年3月17日日曜日
ViewController.mのみに - (void)prepareForSegue:(UIStoryBoardSegue*)segueを記載したと思いますが、ヘッダファイルに宣言しない状態でビルドしてもエラーになりません。なぜかというと、親クラスであるUIViewControllerで宣言されているからです。
ViewControllerからDetailViewControllerの値を変更する
- (void)prepareForSegue:(UIStoryBoardSegue*)segue の中身
DetailViewController *detailViewController = (DetailViewController *)segue.destinationViewController; detailViewController.label.font = [UIFont fontWithName:@"Helvetica" size:self.fontSize];
DetailViewController型にキャストしている
13年3月17日日曜日
- (void)prepareForSegue:(UIStoryBoardSegue*)segueの中身を実装しましょう。segueのdestinatinationViewControllerに遷移先のViewController(ここではDetailViewController)が格納されているので、取得してみましょう。取得したDetailViewControllerのlabelの値を変更してみましょう。この状態でコンパイルして実行します。
ViewControllerからDetailViewControllerの値を変更する
動かしてみて、フォントサイズは変わりましたか?
実は、これだとフォントサイズは変わらないのです。なぜでしょう・・・。
なぜか知るために、デバッグしてみましょう。
13年3月17日日曜日
コンパイルして実行してみましょう。フォントサイズは変わりましたか?変わっていませんね。なぜでしょうか。なぜか知るためにデバッグしてみましょう。Xcodeでブレークポイントを貼ってみましょう。
デバッガを使ってみようXcodeを立ち上げて、以下のようにブレークポイントを設定します。
detailViewController.labelがnilになっている
13年3月17日日曜日
コンパイルして実行してみましょう。フォントサイズは変わりましたか?変わっていませんね。なぜでしょうか。なぜか知るためにデバッグしてみましょう。画面のようにXcodeでブレークポイントを設定して実行してみましょう。detailViewController.labelがnilになっているのがわかりますか?nilに対して設定しても値は変わらないのです。なので、値が変わらなかったのです。
ここで湧く2つの疑問
■疑問その1:nilにアクセスして大丈夫なの?(そもそもnilって何)
■疑問その2:何でdetailViewController.labelがnilなの?
13年3月17日日曜日
ここで2つの疑問が湧くと思います。まず、nilにアクセスしても大丈夫なのか(そもそも、nilって何?)ということと何でdetailViewController.labelがnilなのかということです。
nilとは?
空のポインタ、無効なオブジェクトちなみに、値は0
メッセージを送るとどうなる?→何も送られない。
nilはid型の空ポインタで値は0
13年3月17日日曜日
ここで改めて、「nil」とは何かということを説明します。nilとは無効なオブジェクトのことで、空のポインタです。ちなみに値は0です。デバッガで表示されている値を見てみましょう。0x00000000となっていると思います。javaで言えばNULLと同じようなものなのですが、nilに対してメッセージを送っても落ちるということは無く、何も処理が行われません。なので、detailViewController.labelにアクセスしようとしても、何も行われなかったのです。
参照(strongとweak)
DetailViewController.hの中身
@property (weak, nonatomic) IBOutlet UILabel *label;
弱い参照を表している
strong(デフォルト):強い参照。誰も参照しなくなったらオブジェクトが消滅するweak:弱い参照。変数スコープを超えるとオブジェクトが消滅する
13年3月17日日曜日
では、なぜDetailViewControllerの中身がnilだったのでしょうか。DetailViewController.hの中身を見てみましょう。@property (weak, nonatomic) IBOutlet UILabel *labelと書かれているのではないでしょうか。ここで注目していただきたいのは、@propertyの後のweakという文字です。これはオブジェクトが強い参照か弱い参照かを表しています。デフォルトではstrongが設定されています。Objective-Cはリファレンスカウンタ方式をとっており、そのオブジェクトが誰からも参照されなくなったらメモリ上から消えるようになっています。「強い参照」ではオブジェクトが誰からも参照されなくなったらメモリから解放されます。(消滅します)「弱い参照」ではオブジェクトは変数スコープを超えるとメモリから解放されます。
なぜ弱い参照が必要?
Object1 Object2Object2への強い参照
Object1への強い参照
オブジェクト同士が参照し合っている状態を考えてみましょう。
これだといつまでたってもオブジェクトが解放されず、メモリリークになる
13年3月17日日曜日
なぜ、弱い参照が必要なのでしょうか。強い参照だけだと困る場合があるのです。図のようにオブジェクト同士が参照し合っている状態を考えてみます。Object1はObject2を参照し、Object2はObject1を参照します。いわゆる、循環参照という状態です。この場合、参照状態が消滅することが無いので、いつまでたってもオブジェクトが残り続けることになります。オブジェクトが残り続けるとメモリリークとなりアプリが落ちてしまう原因にもなるのでさけなくてはならないのです。
なぜ弱い参照が必要?
Object1 Object2Object2への弱い参照
Object1への強い参照
Object2にweakをつけて弱い参照にすると・・・
Object2はスコープを超えると消滅し、Object1に対しての参照も消滅する
13年3月17日日曜日
ここでObject2に対してweakをつけて弱い参照にするとどうなるでしょうか。Object1はObject2に対して弱い参照を持っていることになります。Object2はスコープをこえると消滅してしまい、同時にObject1への参照も消滅します。つまり、オブジェクトがいつまでも残り続けるということがなくなると言うわけです。
例題の場合
InterfaceBuilderでlabelを貼付けることによりDetailViewControllerのviewがlabelを参照
view labellabelへの参照
13年3月17日日曜日
例題の場合、InterfaceBuilderでlabelをviewに貼付けることで、detailViewControllerのviewがlabelを参照することになります。しかし、viewが消滅したときには貼付けているlabelを見ることはありません。なので、弱い参照としてviewが消滅すると同時にlabelも消滅させてしまうのです。
では、どうすればいい?DetailViewController.hに以下を追加します。@property (nonatomic)float fontSize;
そして、DetailViewController.mに以下を追加します
・viewWillAppearself.label.font = [UIFont fontWithName:@"Helvetica" size:self.fontSize];
さらに、ViewController.mに以下を追加します・prepareForSeguedetailViewController.fontSize = self.slider.value;
Viewを表示するときに呼ばれるメソッド
13年3月17日日曜日
ではどうすればいいでしょうか。labelを直接変更せず、フォントサイズを渡してあげればよいのです。DetailViewControllerでは受け取ったフォントサイズを反映します。DetailViewControllerの親クラスであるUIViewControllerでは、viewを表示する直前にviewWilAppearというメソッドが呼ばれます。そこでlabelに渡されたフォントサイズを反映させてあげればいいのです。さらにViewController.mのprepareForSegueではDetailViewControllerで定義したfontSizeの値を変更しましょう。ちなみに、fontSizeはオブジェクトではなくプリミティブな値なので、メモリは自動解放されます。なので、strongやweakをきにする必要はありません。
ここまでのまとめ
・他のオブジェクトの値の変更・デバッグ・nilについて・強い参照と弱い参照
13年3月17日日曜日
無事に動いたでしょうか。ここまでのおさらいをします。まず、他のオブジェクトに対して値を変更する方法を勉強しました。デバッガの起動方法も触れましたね。nilが何かということも勉強しました。強い参照と弱い参照にも触れました。ここでやったことはかなり難しいです。今すぐにすべてを理解するのは大変なので、開発がある程度で切るようになったらもう一度見直していただけるとうれしいです。
MVCモデル
ここで、他の値も設定するようにプログラムを変えてみましょう。
数カ所だけならまだしも、何個も変更する値があるとめんどくさい→そこでMVCモデルを使う
13年3月17日日曜日
今までの話で、画面で設定したものを他の画面へ反映する方法を学んだので、他のパーツを追加し、変更した値を反映するようにしましょう。いちいちすべての値を渡すのは大変です。一つのオブジェクトとしてまとめた方が楽です。そのような要求に応えてくれるのがMVCモデルです。これからMVCモデルについて勉強していきます。
Javaなどでも画面で設定したものをデータオブジェクトに保持するなんてことを行っているかと思います。Objective-Cでも同じです。iPhoneアプリの開発ではMVCモデルに基づいて開発します。釈迦に説法かも知れないですが、MVCモデルについておさらいします。Modelでデータの保持およびデータの操作を行います。ViewでModelのデータを画面に表示します。Controllerでユーザの操作の受付およびモデルの操作を行います。Viewに対するControllerということでViewControllerを使っていた訳です。
MVCモデル
Model
データおよびデータの操作
Modelのデータを画面へ表示
ユーザ操作の受付モデルの操作
View Controller
13年3月17日日曜日
Objective-Cでも同じです。iPhoneアプリの開発ではMVCモデルに基づいて開発します。釈迦に説法かも知れないですが、MVCモデルについておさらいします。Modelでデータの保持およびデータの操作を行います。ViewでModelのデータを画面に表示します。Controllerでユーザの操作の受付およびモデルの操作を行います。Viewに対するControllerということでViewControllerを使っていた訳です。
Modelを作成してみよう
・会員(Member)というモデルを作成
~モデルのデータ~・会員番号 変数名:memberId 型:int型・会員名 変数名:memberName 型:NSString型
このモデルを実現するオブジェクトを作成するには?
13年3月17日日曜日
ViewとControllerについては既に作成しているので、今度はViewで入力したデータを保持するモデルを作成しましょう。例えば会員(Member)というモデルを作成する例を考えてみます。モデルの中身は、int型の会員番号とNSString型の会員名とします。この場合、どのようにオブジェクトを作成するにはどうしたらいいでしょうか。
Modelを作成してみよう
必要なもの・.hファイル(Model.h)・.mファイル (Model.m)
・.hファイルの中身@property(nonatomic,strong) NSString *memberName;@property(nonatomic)int memberId;
13年3月17日日曜日
必要なものは.hファイルと.mファイルです。メニューのFile→New→Fileを選んで新しいファイルを作成します。Objective-C Classを選択して次に進んでください。ClassにはMember、SubClassにはNSObjectを記載して保存してください。そうすると、Model.hとModel.mが作成されます。Model.hにmemberNameとmemberIdを宣言してください。@propertyをつけるとgetterとsetterが作成されるので、特にModel.mの記載はいりません。
シングルトンどこからでもアクセスできるシングルトンにします。・Model.hに以下の記載を追加+(Member *)sharedInstance;
・Model.mに以下の記載を追加static Member *member = nil;
@implementation Member
+(Member *)sharedInstance{ if (member == nil) { member = [[super alloc]init]; } return member;}
13年3月17日日曜日
このままでもいいのですが、どこからでもアクセスできるようにシングルトンにします。(データを外部ファイルに保存していないので)Model.hには以下のように記載してください。+(Member *)sharedInstance;+というのは、クラスメソッドであるということです。つまり、オブジェクトではなく、クラスに対してメッセージを送るということです。ここではMemberというクラスに対してsharedInstanceというメッセージを送るとMemberがたのオブジェクトが返ってきます。Model.mファイルにはsharedInstanceメソッドの実装を行います。まず、staticをつけることにより、ファイル内だけで有効となります。(ほかのオブジェクトからアクセスできないようになります) そして sharedInstanceが呼ばれたときは、nilであるかどうかチェックをして、nilだった場合のみ初期化します。
値の参照方法
値を参照するには以下のように記述します
[Member sharedInstance]. memberName;
これでMember型のオブジェクトを取得
取得した値を表示させてみましょう
13年3月17日日曜日
Member型のオブジェクトに設定した値を参照するには [Member sharedInstance]を使ってオブジェクトを取得します。取得したオブジェクトの値を参照してください。MVCTestというプロジェクトをに詳細のコードを記載しておりますので、見てみましょう。
一定時間ごとに処理を行う
NSTimerを使うと一定間隔で同じメソッドを呼び出すことが出来る
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(increment:) userInfo:nil repeats:YES];
[timer invalidate];
[timer fire];
タイマーを初期化して開始するメソッド
タイマーを止めるメソッド
タイマーを開始するメソッド
13年3月17日日曜日
ここで前回リクエストがあったので、「一定時間ごとに値が更新されるプログラム」について説明をします。NSTimerというクラスを使うと、一定時間間隔で同じメソッドを呼び出すことが出来ます。 お渡ししたファイルにNSTimerTestというプロジェクトがありますので、そちらをご覧ください。
一定時間ごとに処理を行う
@selectorって何??→コンパイラに「この引数はメソッドである(SEL型)」ことを示すもの
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(increment:) userInfo:nil repeats:YES];
incrementというメソッドを渡している
13年3月17日日曜日
ここで@selectorについて少し解説します。@selectorとはコンパイラに対して「この引数はメソッド(関数)である」ということを教えています。SEL型と言います。ここで使用しているNSTimerでは自分自身のメソッドを繰り返し呼び出しています。
今日のまとめ
・他のオブジェクトの値の変更・MVCモデル・タイマーの使い方
13年3月17日日曜日
では、今日の課題に入る前におさらいをしましょう最初に他のオブジェクトの値の変更する方法を勉強しました。そして、MVCモデルを使って色々な画面から値を参照できるようにしました。おまけにタイマーの使い方を勉強しました。
今日の課題
13年3月17日日曜日
では、今日の課題です。このようにボタンを押したら項目の編集を行い、編集結果を反映できるようにしてみてください
ご清聴ありがとうございました
13年3月17日日曜日