ブログトップ 記事一覧 ログイン 無料ブログ開設

もとまか日記乙 このページをアンテナに追加 RSSフィード Twitter

iOS5とiCloud向けアプリ開発のチュートリアルまとめ
今よりもっとiOS5とiCloudを使いこなすための情報まとめ
iPhone無料iPhoneアプリまとめ完全版(2011秋版)

これまで作ったiOSアプリケーション
はてブポケット DateCam S AppBank.net mobile mmCalendar mmBlog 101のヒント 下書きめも AppBank for iPad


2008年12月10日

[][] iPhoneアプリを作ってみよう:第3回 タイマーアプリ(#2.ストップウォッチ作成編)


iPhoneアプリを作ってみよう:第3回 タイマーアプリの第2回です。


まずはタイマーアプリの基本中の基本、ってことで、

ストップウォッチを作ってみようと思います。




プロジェクトの作成


今回は以下の条件で作成してみます。

プロジェクト名:stopwatch

テンプレート:View-Based Application



レイアウト作成


Xcodeで、「stopwatchViewController.h」を以下のように編集します。


次にIBを起動し、以下のようにUILabel、UIButtonをViewに貼り付けます。


そして、以下のようにアウトレットします。


IBを保存して、再びXcodeへ。この辺はもうお決まりの作業ですね。

ここまでのソースは以下となります。

#import <UIKit/UIKit.h>

@interface stopwatchViewController : UIViewController {
    IBOutlet UILabel *lbl;
}
@property(nonatomic,retain) UILabel *lbl;

- (IBAction) start_down:(id) sender;
- (IBAction) stop_down:(id) sender;
- (IBAction) clear_down:(id) sender;

@end


コーディング


ここまではストップウォッチのUI(Start、Stop、Clear)を構築しましたが、

次に、今回の核となるタイマー部分をコーディングしていきます。


「stopwatchViewController.h」を以下のように編集して、NSTimerの変数を定義します。

※画像は修正前なので、以下のソースと若干異なります。


ソースコードは以下。

#import <UIKit/UIKit.h>

@interface stopwatchViewController : UIViewController {
    IBOutlet UILabel *lbl;
    NSTimer *timer;
}
@property(nonatomic,retain) UILabel *lbl;

-(void)onTimer:(NSTimer*)timer;
- (IBAction) start_down:(id) sender;
- (IBAction) stop_down:(id) sender;
- (IBAction) clear_down:(id) sender;

@end

次に「stopwatchViewController.m」の「viewDidLoad」を以下のように編集してください。

※画像は修正前なので、以下のソースと若干異なります。


ソースコードは以下。

- (void)viewDidLoad {
    [super viewDidLoad];

    timer = [NSTimer scheduledTimerWithTimeInterval:(0.001) 
    target:self selector:@selector(onTimer:)
    userInfo:nil repeats:YES];
}

Viewがロードされた時に実行されるこの部分は、以下の意味があります。

onTimerという処理を、0.001秒間隔で繰り返し実行する」


後はonTimerの中身を作ればいいわけですが、難しく考える必要はなく、

Startボタンを押した時点から現時点の秒数をラベルに表示すればOKです。

そうすれば、onTimerは0.001秒間隔でどんどん実行しろ、と指定されているので

あたかもミリ秒単位のカウンターがもの凄い速さで動いているように見える、というわけです。

そのコーディング内容が以下です。

※画像は修正前なので、以下のソースと若干異なります。


ソースコードは以下。

#import "stopwatchViewController.h"

@implementation stopwatchViewController

@synthesize lbl;

NSDate *stdate;
BOOL timeflg=FALSE;

- (void)onTimer:(NSTimer*)timer {
    if(timeflg){
        NSDate *now = [NSDate date];
        self.lbl.text = [NSString stringWithFormat:@"%.3f",
                        [now timeIntervalSinceDate:stdate]];
    }
}

-(IBAction) start_down:(id) sender{
    timeflg = TRUE;
    stdate = [NSDate date];
    [stdate retain];
}

「timeflg」は初期値はFlaseで、Startボタンがタップされた時にTrueになります。

こうしておかないと、アプリを起動したらすぐにカウントが始まってしまうので

通常はFalseにしておくわけですね。

このロジックの考え方は以下です。

  • 1.Startボタンをタップすると「timeflg」がTrueになってカウンター始動。
  • 2.この時の日時を「stdate」に保持する。
  • 3.stdateを「retain」して、継続して利用可能に。
  • 4.onTimer内で、その時点の日付(now)を取得。
  • 5.stdateとnowの間隔をミリ秒単位でラベルに表示する。

※stringWithFormatの他のパラメータについては、NSString:stringWithFormat:が参考になります。


次に、StopとClearですが、これは単純に「timeflg」をFalseにすればOKです。


ソースコードは以下。

-(IBAction) stop_down:(id) sender{
    timeflg = FALSE;
}

-(IBAction) clear_down:(id) sender{
    timeflg = FALSE;
    self.lbl.text = @"0.000";
}

これをシミュレータで実行すると、こんな感じです。


画像なので止まってますがw

実際には1/1000秒単位のストップウォッチとして動作しています。


<追記>

間隔に0.001を指定してはいますが、実際には正確にその間隔では動作しないと思われます。

※では、どれくらいの間隔までなら大丈夫なのか、については未検証なのでわかりません。

というか、例えば0.1秒間隔にしたところで、iPhoneOSの処理が重くなってしまったら

0.1秒ごとに処理を回すことすら出来なくなってしまうこともあったりします。

なので、ストップウォッチ的には処理間隔自体はさほど重要ではなく、

「処理が行われる実際の間隔に影響しない結果を表示する」ということが

ストップウォッチ的には大事だと思われます。

もちろん本内容でそれは大丈夫なのか?という懸念はあるでしょうが、

そもそも本チュートリアルの目的はそういうのとは趣が少々違うので。

なお、本内容で「0.001」を指定しているのは、「そういう指定も出来ますよ」

という程度の意味で捉えてもらえればいいと思います。



いかがでしたでしょうか?


タイマーの基本はこんな感じです。

今回は1/1000秒単位で実行していますが、必要に応じて間隔を変えてあげれば、

例えば10分おきに何かをチェックする、ということも可能となるわけですね。


次回はタイマーアプリというとちょっと違うんですが、Tab Barアプリの

基本について解説しようと思います。

目標とする形を考えると必要だし、View-Basedばかりだと飽きてしまうのでw

たまには気分転換も必要ですね(^o^)

 

cranecrane 2008/12/11 00:07 NSTimerから呼ばれるonTimerメソッドですが、引数にNSTimer*が必要です。リファレンスを参照ください。

それから、1/1000秒を指定しても実際にはそこまでの精度では呼んでくれません。それに、そんなに短くするとバッテリー的につらいです。

moto_makamoto_maka 2008/12/11 06:21 コメントありがとうございます。

内容を一部修正しました。

mspecmspec 2010/01/19 23:24 いつも参考にさせていただいております。ありがとうございます。
お一つお力を貸していただきたいんのですが、、、

上記のようにご教授頂いたタイマーをアプリに実装しようとしております。
問題無く動作することは確認できました。

今現在、トップ画面から4つの各画面に画面を切り替えるようなアプリを作成しております。
この際に4つの画面すべてでタイマーを使いたいのですが、どうもうまくいきません。。。
どのソースにも同じようにコードを実装したのですが、コンパイルエラーになります。(command /Developer/Platforms/iPhoneSimylator.platform/Developer/usr/bin/gcc-4.2 failed with exit code 1)

1つだけの画面に実装するとうまくいくのですが、4つの画面に実装するとうまくいきません。

すみませんが、ヒントをご教授いただけないでしょうか。

以上、よろしくお願い致します。

moto_makamoto_maka 2010/01/20 02:36 プロジェクトの右下に赤いマークが出てると思いますが
そのマークをクリックするとどのようなメッセージが表示されますか?
また、エラーになってるコード自体を教えてください。

mspecmspec 2010/01/21 01:27 ご連絡ありがとうございます。

ビルド結果のメッセージは以下のようになっておりました。


duplicate symbol _timeflg in /Users/ユーザ名/Desktop/アプリ名/build/アプリ名.build/Debug-iphonesimulator/アプリ名.build/Objects-normal/i386/SubtractionViewController.o and /Users/ユーザ名/Desktop/アプリ名/build/アプリ名.build/Debug-iphonesimulator/アプリ名.build/Objects-normal/i386/AdditionViewController.o

command /Developer/Platforms/iPhoneSimylator.platform/Developer/usr/bin/gcc-4.2 failed with exit code 1

以上のようになっておりましたが、継続して調べたところ、
変数名がバッティングしているようでした。

初歩的なミスで、お手数をおかけ致しました。

これからも参考にさせていただきたいと思います。

ありがとうございます。

moto_makamoto_maka 2010/01/21 05:53 いえいえ、お互いがんばっていきましょう!(^o^)

FREEDOMFREEDOM 2010/01/29 23:01 いつもここのサイトを参考にさせていただいています。
現在、object-cを勉強中の初心者です。
一つお聞きしたいことがあります。

上記のようにソースコードをうって問題なくタイマーアプリを作ることができました。

実際に時間を計ってストップボタンを押して時間を止めます。そしてまたスタートボタンを押すと最初からになってしまいます。

これを時間を止めてまた最初から計るのではなく途中から計るようにするのには
どのようなソースコードを書けばいいかわかりません。

すいませんが、教えていただけないでしょうか。
よろしくお願いします。

moto_makamoto_maka 2010/01/30 06:22
・スタートを押した時が開始
・表示しているのは開始からの時間差

なので、このソースだと最初からになるんですね。
途中から計るようにする考え方は、

・ストップ時点の秒数を保持する
・表示される秒数に保持した秒数を加算する

例えば、ストップした時が1秒だったら、
再度スタートして計測自体は0からでも
表示上1秒足してあげれば1秒からスタートしたように見える、
というわけです。

簡単な方法としてはこんな感じじゃないかなと。
他にもやり方はあると思うし、もっと簡単な方法もあるかもしれませんので
色々試してみるのが良いかもしれませんね。

FREEDOMFREEDOM 2010/02/05 22:20 先日はどうもありがとうございました。
この数日色々試してみたのですか、なかなかうまくいきませんでした。
・時点の秒数を保持する
・表示される秒数に保持した秒数を加算する
のソースコードをどこにどのように書けばいいのかどうしてもわかりませんでした。
すいませんが、教えていただけないでしょうか。
お手数をおかけ致しますが、よろしくお願いします。

moto_makamoto_maka 2010/02/07 05:28 >・時点の秒数を保持する
>・表示される秒数に保持した秒数を加算する

このチュートリアルの例でいうと、
stop_downの時にself.lbl.textの表示内容を取得して保持、
この変数を仮にhojiとします。これが最初のポイントですね。
次にonTimerのself.lbl.textにこの変数の値を加算して表示します。
これが二つ目のポイントですね。
難しいのは、変数の型やretainへの理解だと思いますが
ここが理解出来ないと後が苦しくなるので、踏ん張りどころだと思います。
ぜひ頑張ってください!

LecterLecter 2010/08/27 14:46 いつもありがとうございます。
先生、案の定retainで詰まりました!

[stdate retain]の必要性がどうにもわからないのです。
これを入れないと参照カウンタがゼロになってしまうんですよ?どこで−1されるんでしょうか??

LecterLecter 2010/08/28 01:03 いろいろいじってるうちに、なんかわかったかも!

stdate = [NSDate date];
stdateはdateというファクトリメソッドで生成してますよね。
これってallocとinitの機能を併せ持った便利なやつだと聞いてます。
でもついでにautoreleaseまでくっついてくるんですよね。
だから、処理が一段落したらreleaseされちゃうんですよね。
この場合、start_down:メソッドの中で生成したので、そのメソッドを抜けるときにreleaseされちゃうんですかね?←ここは自信なし。
そうするとonTimerメソッドでstdateが参照できなくてエラーが起きちゃうんですよね。

試しに
stdate = [[NSDate alloc]init];
と書き換えてみたら、これだとautoreleaseがないからか、エラーが起きなかったです。
ただどこかでreleaseしてやらないとリークが起こっちゃうんでしょうけど。

この理解で合ってます?

LecterLecter 2010/08/28 01:12 訂正です。

試しに
stdate = [[NSDate alloc]init];
と書き換えてみたら、これだとautoreleaseがないからか、
[stdate retain];
をコメントアウトしてもエラーが起きなかったです。

moto_makamoto_maka 2010/08/28 06:40 >Lecterさん

はい、私もそういう認識です。
しかし素晴らしいです。
とても分かりやすい解説ですね!(^o^)

LecterLecter 2010/08/28 11:41 ありがとうございます。
ちょっとずつ謎が解けて視界が広がっていく感じ、実に楽しいです。ほんと、もとまかさんのおかげです。
もとまかさんのチュートリアルは、簡単すぎず難しすぎず、そして親切すぎず、まさに絶妙なところなんですよねー。

OhayojiOhayoji 2011/08/16 18:18 もとまかさんこんにちは。
プログラミング自体初心者ですが、このチュートリアルで少しづつ勉強させていただいてます。

アドバイスをお願いします。
上記のコーディングをしてiphonシュミレータで実行したときに、アプリが一瞬立ち上がってすぐ落ちてしまいます。
どういった原因が考えられますでしょうか?
エラーは一つもなく、問題なく完了しています。
X-codeを再起動しても解決しませんでした。

よろしくお願いいたします。

OhayojiOhayoji 2011/08/17 15:15 ソースコードをもう一回みなおしたところ、mファイルのviewDidLoadメソッドの部分に誤りがありました。
それを直したら正常に起動しました!
*scheduledTimerWithTimeIntervalメソッドのselectorラベルの引数@selector(onTimer:)のコロンが抜けていました。
お騒がせしました。