.-- --

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
スポンサー広告 comment(-) trackback(-)
.26 2009

3G S じゃなくて 3GS 発売!

Tasaki です。

1週間ぶりのエントリとなりましたが、本日 iPhone 3GS が日本国内でも発売されましたね。
個人的にはバッテリ自体がそれほど強化されなかった件は少し残念に思いましたけども、2G touch 以上のパフォーマンスとなれば、絶対“買い”なのは言うまでもありません。

それから、近況報告をいたしますと、iPhone アプリ開発の他に優先的に行うべき業務があるため、SDK の研究に時間を割けない状態にあります。そういったわけで、ブログの更新頻度も週1回程度にしようと考えています。技術情報の提供も難しくなりそうではありますが、今後ともよろしくお願いします。

スポンサーサイト
.19 2009

SQLite を使ってみる(6)

Tasaki です。

いよいよ SQLite 編も最終回となりました。
最後はデータの削除とアプリケーション終了時の後処理についてです。

ただ、4回目のエントリで既に個別のデータの消去については紹介済みですので、今回は全てのデータを消去する場合を考えます。
これはすごく単純でして、全てのデータの読み込みとデータの消去の方法が分かっていれば、簡単に思いつくでしょう。
実装例を以下に示します。このメソッドは、データベースへのポインタ database とデータベース内のデータから作成した Person オブジェクトの配列 dataArray を管理するクラスで定義することを想定しています。
// 全ての位置情報をテーブルから削除
- ( void ) deleteAllData {
static char *sqlString = "DELETE FROM person";
sqlite3_stmt *statement = NULL;
if( database != NULL ) {
  if( sqlite3_prepare_v2( database, sqlString, -1, &statement, NULL ) == SQLITE_OK ) {
   while( sqlite3_step( statement ) == SQLITE_ROW );
  }
  sqlite3_finalize( statement );
 }
}

上の例ではエラー処理を怠けてしまっていますが、削除できたデータと同じものだけをメモリに展開中の配列から抹消することが、望ましいのではないでしょうか。

次に後処理に関してですが、最低でも以下の処理は必要になります。
これも前述のクラスで定義しておくべきメソッドです。
- ( void ) closeDatabase {
if( database == NULL ) {
  return;
 }
if( sqlite3_close( database ) != SQLITE_OK ) {
  NSLog( @"Failed to close database with '%s'.", sqlite3_errmsg( database ));
 }
}

これで、開始から終了までに最低限必要となる機能をご紹介できました。
慣れるまではいろいろ面倒に感じますが、ある程度仕組みが分かってしまえば、応用はそう難しくないのではないでしょうか。

まあ、3.0 で追加された Core Data を使えば、もっと楽なのは間違いないでしょうけど。
Core Data についてのエントリもありますので、こちらからどうぞ。


.18 2009

祝!3.0リリース

Tasaki です。

本日、ついに 3.0 が正式にリリースされましたね。
といっても、既に GMSeed 版をインストールしていた場合はビルドバージョンも変更されていないため、iTunes で更新確認してみても最新ですって言われちゃうだけでしたが。

新OS では、MMS が使えるようになったので、SoftBank 携帯用メールアドレスも解禁されたみたいですが、My SoftBank に登録していなかったため、MMS の設定が変更できず、現時点ではまだ MMS が使えるかどうか確認できない状態です。

せっかくアドレス取得したのに…

それと、最近ずっと GPS との相性が悪いんですが、これは改善されるんでしょうか?


.17 2009

SQLite を使ってみる(5)

Tasaki です。

このテーマもついに5回までやってきました。
前回まででデータベースの読み込み部分の実装はできあがっていますので、今回はデータの追加部分を考えます。

ここでは、メモリに展開中のデータ配列に新たなデータが追加されたとき、同時にデータベースへのコミットを行うことで対処することにします。

そこで、前回ご紹介した Person クラスに次のメソッドを追加します。
// Person オブジェクトの新規追加用メソッド
- ( id ) initWithID:( NSUInteger ) id name:( NSString *) name database:( sqlite3 *) db {
if( name == nil || db == NULL ) {
  return nil;
 }
if(( self = [ super init ]) != nil ) {
  database = db;

  static char *sqlString = "INSERT INTO person VALUES(?,?)";
  sqlite3_stmt *statement = NULL;
  if( sqlite3_prepare_v2( database, sqlString, -1, &statement, NULL ) == SQLITE_OK ) {
   int i = 1;
   sqlite3_bind_int( statement, i++, id );
   sqlite3_bind_text( statement, i++, [ name UTF8String ], -1, SQLITE_TRANSIENT );
   if( sqlite3_step( statement ) != SQLITE_ERROR ) {
    self.id = id;
    self.name = name;
    [ name retain ];
   } else {
    // データベースへの登録に失敗
    NSLog( @"Insertion Error : failed to step with '%s'.", sqlite3_errmsg( database ));
   }
   sqlite3_finalize( statement );
  } else {
   NSLog( @"Insertion Error : failed to prepare with '%s'.", sqlite3_errmsg( database ));
  }
 }
return self;
}

このメソッドの返り値が空でなければ、それをデータ配列へ追加することでメモリ上とディスク上のデータの整合性が保たれることになります。(注:上記のコードでは、データベースへの登録に失敗したケースなどでは不整合が生じますが、name が nil になっているので外部から判別可能です)
ただ、id は主キーなので、呼び出し側で一意な値を渡しておかなければなりません。

今回、SQL ステートメント内部で ? パラメータと bind 関数を使用して値を渡していますが、このときのインデックスは 1 から始まりますので注意しましょう。


さて、これで読み出しと書き込みについての解説は終わりました。次回は削除を採りあげる予定ですが、前回のエントリでほとんど解決しちゃってる気がします。



.16 2009

SQLite を使ってみる(4)

Tasaki です。

今回は、前回掲載したコード内に登場した Person クラスの実装を見ていくことにします。

まず、ヘッダファイルの記述ですが、エンティティの各属性に対応する変数とプロパティを宣言します。加えて、前回登場したイニシャライザとデータベースから削除するためのメソッドを定義します。

#import <UIKit/UIKit.h>
#import <sqlite3.h>

@interface Person : NSObject {
// エンティティの属性
NSUInteger id;
NSString *name;
// DB のポインタ
sqlite3 *database;
}
@property ( nonatomic, retain ) NSString *name;
@property ( nonatomic ) NSUInteger id;

- ( id ) initWithStatement:( sqlite3_stmt *) statement database:( sqlite3 *) db;
- ( void ) deleteFromDatabase;

@end


上記メソッドの実装部を以下に示します。

// ステートメントから初期化
- ( id ) initWithStatement:( sqlite3_stmt *) statement database:( sqlite3 *) db {
if(( self = [ super init ]) != nil ) {
  database = db;

  int i = 0;
  id = sqlite3_column_int( statement, i++ );
  name = [[[ NSString stringWithUTF8String: sqlite3_column_text( statement, i++ )] retain ];
 }
return self;
}

// データベースから該当項目を抹消
- ( void ) deleteFromDatabase {
if( database == NULL ) {
  return;
 }
// 該当項目の削除用 SQL 文を作成
NSString *deleteSQL = [ NSString stringWithFormat: @"DELETE FROM location WHERE id=%d", id ];
// SQL文のコンパイルと実行
sqlite3_stmt *statement = NULL;
if( sqlite3_prepare_v2( database, [ deleteSQL UTF8String ], -1, &statement, NULL ) != SQLITE_OK ) {
  NSLog( @"Failed to prepare statement with '%s'.", sqlite3_errmsg( database ));
 }
int wasSucceeded = sqlite3_step( statement );
sqlite3_finalize( statement );
if( wasSucceeded != SQLITE_DONE ) {
  NSLog( @"Failed to delete from database with '%s'.", sqlite3_errmsg( database ));
 }
}


とりあえず、どんなエンティティでもこの2つのメソッドは最低限必要になると思います。
アプリにデータ修正機能を付加する場合には、更新用のメソッドが別途必要になります。

前回のエントリと合わせてご覧になればお分かりだと思いますが、SQLite API を用いた処理の流れは、どのような場合でもほとんど変わりません。prepare→step→finalize とエラー対処が基本です。このため大した処理でなくとも、コード量がそれなりに大きくなってしまいます。
SQLite を使えば使うほど、Core Data の偉大さを思い知らされそうです。


.15 2009

SQLite を使ってみる(3)

Tasaki です。

今回から、データベースを実際にコード中で操作してみます。

まずは、データベースからの読み込み部分を作成します。
これはアプリケーションの初期化時に呼び出すことになりますので、初めに実装しておくべき点です。当然、初めはデータが登録されていないどころか、データベースファイルが存在しないでしょうから、これを作成するところもコード化する必要があります。

ただ、前回のエントリで作成したテンプレートをそのままデータベースとして取り込めばいいわけですから、この部分に関しては楽ができます。
これを踏まえて、手順としては次のようになります。
①ドキュメントディレクトリ内にデータベースファイルがあるか確認する
②なければ、アプリケーションバンドルからデータベースのテンプレートファイルをドキュメントディレクトリにコピーする
③テンプレートファイルが見つからなければエラー終了
④データベースファイルからデータを読み込む

上記①~③のコード例を以下に示します。引数には、データベースファイルの絶対パスを与えます。なお、ドキュメントディレクトリの取得法については、ここでは触れません。

- ( BOOL ) initDatabase:( NSString *) path {
NSFileManager *fileManager = [ NSFileManager defaultManager ];
if([ fileManager fileExistsAtPath: path ] == NO ) {
  // DB ファイルが無い場合は DB を作成
  NSString *dbTemplatePath = [[[ NSBundle mainBundle ] resourcePath ] stringByAppendingPathComponent: @"mydatabase.sqlite" ];
  if([ fileManager fileExistsAtPath: dbTemplatePath ] == NO ) {
   // DB テンプレートが見当たらない場合は、エラー終了
   NSLog( @"DB template file is not found." );
   return NO;
  }
  // テンプレートをドキュメントディレクトリへコピー
  NSError *error = nil;
  if([ fileManager copyItemAtPath: dbTemplatePath toPath: path error: &error ] == NO ) {
   // DB テンプレートをコピーできない場合は、エラー終了
   NSLog( @"DB file could not be created with following message. '%@'", [ error localizedDescription ]);
   return NO;
  }
 }
return YES;
}


さてこの後は、いよいよデータベースからデータを読み込みます。
以下のコードでは、引数 data に読み込んだデータの配列を、path には、データベースファイルの絶対パスを与えます。
返り値は、データベースへのポインタです。

- ( sqlite3 *) loadPersonData:( NSArray *) data FromDB:( NSString *) path {
sqlite3 *database = NULL;
if( sqlite3_open([ path UTF8String ], &database ) == SQLITE_OK ) {
  // DB ファイルオープン成功
  sqlite3_stmt *statement = NULL;
  const char *allSelect = "SELECT * FROM person";
  int status = sqlite3_prepare_v2( database, allSelect, -1, &statement, NULL );
  if( status == SQLITE_OK ) {
   while( sqlite3_step( statement ) == SQLITE_ROW ) {
    // データが存在する場合は、配列に格納する
    Person *person = [[ Person alloc ] initWithStatement: statement database: database ];
    [ data addObject: person ];
    [ person release ];
   }
  }
  sqlite3_finalize( statement );
 } else {
  // DB ファイルオープン失敗
  sqlite3_close( database );
  NSLog( @"Failed to open databese with following message '%s'.", sqlite3_errmsg( database ));
  database = NULL;
 }
return database;
}


上記メソッド内に Person クラスとそのメソッドが確認できると思います。
名前から推察できる通り、データベースの person エンティティに対応するクラスです。
これは、CoreData を使う場合と違い、クラスおよびメソッドを自力で実装する必要があります。この辺りが現行バージョンで厄介な点です。

次回はこのクラスの概要と実装すべきメソッドを紹介します。
なお、sqlite3 という接頭辞がついた型または関数については、以前紹介したサイトなどを参考にしてください。(ここまでに出てきたものについては、非常によく使われていますのできちんと理解しておくことが求められます。)


.12 2009

SQLite を使ってみる(2)

Tasaki です。

SQLite の使い方2回目は、データベースの作り方をご紹介します。
Mac には、SQLite バージョン3が標準でインストールされており、ターミナルから実行することが可能です。
iPhone アプリで、SQLite データベースを使用する場合、テンプレートを予め Mac 側で作成しておくとコーディングが楽になります。

それでは、実際にやってみましょう。
ターミナルを開いて sqlite3 mydatabase.sqlite を実行すると、カレントディレクトリに mydatabase.sqlite ファイルが作成されます。既に同名のファイルが存在する場合は、既存のファイルが開かれ、SQLite が起動します。
新規にファイルを作成した場合、これでデータベースの初期化も行われます。

次にテーブルを作成します。
SQLite バージョン3では、以下の5種類のデータ型が定義されています。

型名詳細
INTEGER符号付き整数
REAL倍精度浮動小数点数
TEXTUnicode文字列(UTF-8,UTF-16BE,UTF-16LE)
BLOB不定形、バイナリデータ
NULLNULL値を表す

今回は整数と文字列で構成される以下のテーブルを作成することにします。
テーブル名 person
{ id : 整数値、主キー
name : 文字列 }
この場合、SQLite に以下の操作を行います。
CREATE TABLE person ( 'id' INTEGER PRIMARY KEY, 'name' TEXT NOT NULL );
エラーメッセージが出なければ、無事テーブルが作成されているはずです。
テーブルの内容を確認する場合は、.schema コマンドを使用します。
試しに .schema person と入力するとテーブル名とデータ型が表示されると思います。
SQLite を終了する時は、.exit と入力します。

このようにして、データベースファイルのテンプレートを作成することができました。あとは、できあがったファイルをプロジェクトに追加して終了です。

次回は、データベースの読み込み部分を解説します。

.11 2009

SQLite を使ってみる(1)

Tasaki です。

突然ですが、今回から数回に渡って、SQLite 導入編を連載していこうと思います。
ちょうど OS の変わり目で書けるネタが少なくなってきたということもありますが、WWDC でのアナウンスを受けまして、iPod touch ユーザーがこぞって 3.0 にすぐさま移行するとは思えませんので、2.2.1互換の技術は使いどころがあると見込んでのことです。(iPhone ユーザーの中にも特別な理由があって、すぐさまアップデートしない人たちもいるでしょうし)

それでは、まず初めにライブラリのインポートですが、SQLite はフレームワークとして提供される訳ではありませんので、CoreGraphics を利用する場合などとは若干操作が異なってきます。

①プロジェクトウィンドウのグループとファイル一覧からコンテキストメニューを表示し、「既存のフレームワークを"<グループ名>"に追加」を選択
②<SDKのインストールディレクトリ>/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS<ベースSDKのバージョン>SDK/usr/lib/libsqlite3.0.dylibを選択し、追加
③ソースコードのヘッダ部にて、#import <sqlite3.h>と宣言

ひとまず、これで sqlite 関連の API が利用可能になりました。
なお、iPhone OS に搭載されている SQLite はバージョン3だということで、これらの API は全て sqlite3 という接頭辞を含んでいます。
また、API は C 言語で提供されており、日本語の資料なども Objective-C 関連のものに比べれば多数公開されていますので、比較的ハードルは低めといってよさそうです。

ちなみに僕の場合、以下のサイトにお世話になっています。
rktSQLite : SQLiteのSQLコマンド一覧
SQLiteバージョン3のC/C++インタフェース - Third impact

次回はデータベースの作り方をご紹介する予定です。

.10 2009

UIGraphics系関数の使い方

Tasaki です。

今回は、久しぶりにグラフィック関連のテーマを採り上げます。
個人的には、グラフィック系に興味があるので本当はこのカテゴリのテーマを増やしていきたいとは思っているんですが。(人気のあるカテゴリだと思うので、もっと詳しい方が書かれたものがいっぱいありそうですけど。)

UIKit の描画系メソッドは、現在のグラフィックコンテキストに対して実行されます。
これでは、任意のグラフィックコンテキストへの描画はできないように思えますが、次のように記述することで、それが可能となります。
- ( void ) drawContext:( CGContextRef ) context {
CGFloat width = CGBitmapContextGetWidth( context );
CGFloat height = CGBitmapContextGetHeight( context );
UIGraphicsBeginImageContext( CGSizeMake( width, height ));
UIGraphicsPushContext( context );
// この間で描画メソッドの呼び出し
UIGraphicsPopContext();
UIGraphicsEndImageContext();
}

このとき、context には任意のビットマップコンテキストを渡すことを想定しています。
また、画面サイズより大きくても問題ありませんので、大きな画像の作成に UIKit の描画メソッドを使用することができるようになります。
ということで、NSString の UIKit 拡張メソッドもいけます。
まだ、お試しでない方はぜひ一度どうぞ。


昨日も書きましたが、iPhone 3G S では OpenGL ES 2.0 に対応するということなので、表現の幅も広がりますし、こっちの方の勉強もしていきたいですね。

.09 2009

iPhone 3G S ! Snow Leopard ! Safari 4 ! ...

Tasaki です。

ついに iPhone 3G S が発表されましたね。個人的にはバッテリ性能の向上に一番興味が湧きました。あと、OpenGL ES 2.0 対応ということで本格的に DS、PSP と対決していくんだろうなあと思いました。

またソフトウェアでは、SDK および OS の 3.0 GM Seed 版、一部開発者向けではありますが次期 Mac OS の Snow Leopard や Safari 4 などのリリースが行われ、新製品目白押しの一日でした。

開発用の iPhone には既に 3.0β5 をインストールしていたので、早速アップデートしてみました。ぱっと見た感じは、特にβ5のときと変わっていないようだったので、このまま正式リリースとなりそうですね。
3.0 βにしてから、GPS の取得に難があったんですが、その辺りが改善されているかどうかをこれから検証してみたいと思っています。

それにしても、Safari 4 の初回起動時はいいですね。おおっ!て感じで、ちょっと感激しました。

.08 2009

ネットワークプログラミング入門編(6)

Tasaki です。

今日調査して分かったことがありましたので、入門編6回目としてお送りします。

3回目と4回目でレスポンスについて触れましたが、このときにはヘッダの取得とコネクションの確立失敗に関しての話だけに留まっていました。というのも、レスポンスの中身( HTTP body )の取得の仕方が分からなかったためです。これがやっと解決しました。

connection:didReceiveResponse: メソッドはレスポンスヘッダの取得時に呼び出されるメソッドでしたが、この他にレスポンスの内容を取得したときに呼ばれるデリゲートメソッドがあります。それがこちらです。
- ( void ) connection:( NSURLConnection *) connection didReceiveData:( NSData *) data;
- ( void ) connectionDidFinishLoading:( NSURLConnection *) connection;

前者はレスポンスデータの一部または全てを引数として与え、後者は全てのデータを取得した場合に呼び出されるメソッドです。

データが単一のパケットに収まりきらない場合は connection:didReceiveData: メソッドは、部分データを逐次渡してきます。そのため、このメソッドは1度のリクエストに対し、複数回呼び出される可能性があります。

これを利用して、サーバ側でリクエストが正常に受理された場合は1を、そうでない場合には0を返すようにしていた場合、クライアントの iPhone アプリでは以下のように実装することで、受理されたかどうかを知ることができます。
- ( void ) connection:( NSURLConnection *) connection didReceiveData:( NSData *) data {
unsigned char value;
 [ data getBytes: &value length: sizeof( value )];
if( value ) {
  // 受理された
 }
}

レスポンスデータの取得も、このように簡単に行うことができます。

ふたを開けてみれば、すごく単純な話だったのですが、デリゲートメソッドの名前をタイプミスしていたせいで、今日の今日まで全くやり方が分かりませんでした。
その中で、NSURLResponse に body を取得するメソッドがないか調べたりしていました。たったこれだけのために、CFNetwork フレームワークを使わなきゃいけないのか、と悩んだりもしました。

というわけで、今回の経験で得た教訓
「デリゲートメソッドの名前はコピペしとけ!」


.05 2009

カスタムスライダの作成

Tasaki です。

前回のエントリでロック解除スライダを自作してみましたが、標準のつまみは小さくて非常につまみにくいですよね。
そこで今回は、つまみや背景の画像を置き換えたカスタムスライダを作成する方法を紹介していこうと思います。

まず初めに、スライダを構成する画像は、つまみとつまみの左右の軌道の3点が必要です。またオプションとして、スライダの左右両端にそれぞれ最小値、最大値を表す画像を表示することもできます。

つまみ画像は以下のメソッドで置き換えることができます。
- ( void ) setThumbImage:( UIImage *) image forState:( UIControlState ) state;

image に画像オブジェクトを与えます。state は UIControl のサブクラスに共通の、指定した画像を適用する場合のコントロールの状態です。とりあえず、UIControlStateNormal の場合だけでも設定しておけばいいでしょう。

次に軌道画像についてですが、つまみにオリジナルの画像を設定した段階で標準の軌道は表示されなくなりますので、これも必ず用意しておく必要があります。
軌道画像は以下の2つのメソッドを用いて設定します。
- ( void ) setMinimumTrackImage:( UIImage *) image forState:( UIControlState ) state;
- ( void ) setMaximumTrackImage:( UIImage *) image forState:( UIControlState ) state;

ご覧になってお分かりのように、引数はつまみの場合と全く同じです。
なお、setMinimum... はつまみの左側に表示される部分、setMaximum... はつまみの右側に表示される部分の画像を指定します。
また、軌道画像はスケーリングされますので、角を丸くしている場合などは strechableImageWithLeftCapWidth:topCapHeight: を使って表示がおかしくならないようにしておきましょう。

これだけでスライダの見た目を変えることができるわけですが、標準のロック解除スライダのように、つまみが軌道の内側に表示されるようにする場合には、これだけだと不十分です。やってみると分かりますが、つまみの位置が軌道の中央部分を通ってくれません。

これを実現するためには、UISlider のサブクラスを定義し、その中でこれらのメソッドをオーバーライドして、返り値に適切な値を渡す必要があります。
- ( CGRect ) trackRectForBounds:( CGRect ) bounds;
- ( CGRect ) thumbRectForBounds:( CGRect ) bounds trackRect:( CGRect ) track value:( float ) value;

前者は軌道の描画領域を、後者はつまみの描画領域を返します。
引数についてですが、bounds は、スライダ自身の bounds プロパティ、track は軌道の描画領域を、value はスライダの現在の値を示します。

今回はつまみの中心が軌道の中心を横切るようにするだけなので、trackRectForBounds: は無理に実装する必要はありません。

つまみの幅を thumbWidth、つまみの高さを thumbHeight、軌道のフレーム部分の幅を frameWidth とした場合は、以下のように書くことができます。
- ( CGRect ) thumbRectForBounds:( CGRect ) bounds trackRect:( CGRect ) track value:( float ) value {
CGFloat x = frameWidth + ( CGRectGetWidth( track )- thumbWidth - frameWidth * 2.0f )* value;
return CGRectMake( x, frameWidth, thumbWidth, thumbHeight );
}


まだ試していませんが、これらのメソッドを上手く使うことで、縦のスライダや斜めのスライダなんかも作れると思います。

.04 2009

自動リセットスライダの作成

Tasaki です。

今回は、ロック画面でおなじみの、右端までつまみを移動しないと自動的に左端までつまみが戻るスライダの作り方をご紹介します。

スライダの初期化時に設定しておくのはこれらの項目です。
・最小値(左端)
・最大値(右端)
・現在の値(つまみの表示位置)
・continuous フラグ
・アクションメソッド

このうち、今回のテーマで重要となるのが continuous フラグとアクションメソッドです。
アクションメソッドは、今回スライダの値が変化したら呼び出すようにします。
そこで、現在の値が最大値と等しいかどうかを判定します。
continuous フラグは、このアクションメソッドの呼び出し条件に関与するもので、デフォルトでは YES に設定されています。
YES の場合は、つまみから手を放していなくとも値が変更されれば、その都度アクションメソッドが呼び出されることになります。
今回のケースでは、それだと困るので NO にしておきます。

以下にサンプルコードを示します。
- ( UISlider *) createAutoResetSlider:( CGRect ) frame {
UISlider *slider = [[ UISlider alloc ] initWithFrame: frame ];
if( slider == nil ) {
  return nil;
 }
 [ slider setMinimumValue: 0.0f ];
 [ slider setMaximumValue: 1.0f ];
 [ slider setValue: 0.0f ];
 [ slider setContinuous: NO ];
 [ slider addTarget: self action: @selector( sliderValueChanged: ) forControlEvents: UIControlEventValueChanged ];
return slider;
}

- ( void ) sliderValueChanged:( UISlider *) slider {
if([ slider value ] < [ slider maximumValue ]) {
  [ slider setValue:[ slider minimumValue ] animated: YES ];
 } else {
  // 最大値まで移動していた場合の処理
 }
}

ただ、みなさんご承知の通り標準のスライダは非常につまみにくいんですよね。
そこで、次回はカスタムスライダの作成法を解説しようと思います。


.02 2009

アプリケーションロック時の注意点

Tasaki です。

梅雨の前に真夏日が来るって何の冗談ですか、といった感じの一日で大変参ってしまいますが、今日もがんばっていきましょう!

前回でネットワークプログラミング入門編はおしまいということで、今回はスリープ時のアプリの動作に関する話です。

iPhone OS では、バッテリ対策としてユーザによるタッチ操作が一定時間行われなかった場合や、スリープボタンが押された場合に画面を消しロック状態に入りますが、このときアプリケーション自体は停止していません。
ただ、イベントの伝達が行われなくなるために、以下のような制御を行うことはできません。
・タッチイベント
・加速度センサーからの情報の取得
・位置情報の取得
・タイマイベント
Apple のサンプルコードでは、アプリケーションデリゲートの application:didResignActive: メソッド内でフレームレートを落とすための処理が行われていたりしますが、実際のところ、アプリケーションがロックされた状態ではタイマ自体が停止しているようなので意味の無いように思います。
タイマはロックが解除され次第、再びセットされます。
もう1点、デバッガ起動中は画面表示を消してもイベントが伝達されるため、デバッグ中ではこれを確かめることができません。(これにだまされました。画面消えててもちゃんと動いてる!って)

ということで、ロック中はアプリケーションは休止状態にある、と考えた方がよさそうです。一応自動スリープタイマを切ることは可能ですが、スリープボタンを押した場合には対処できません。
常駐タスクを実装する場合は特にご注意ください。




.01 2009

ネットワークプログラミング入門編(5)

Tasaki です。

今回でネットワークプログラミング入門編も5回目となりました。
前回までの4回で、送信と受信についてのお話は終わっていますが、今回は僕が実装中にハマった落とし穴について解説しておこうと思います。

それは、HTTP リクエスト作成時に、サーバ側へクエリとしてパラメータを渡す場合の注意点です。HTTP プロトコルを利用する場合には、RFC1808 に従った文字列を渡す必要があります。
つまり、任意の文字列をそのまま URL として渡してよい、というわけではありません。
ここで、1番問題になりそうなのが空白文字です。これを URL に含めてしまうと、リクエスト送信時にコネクションエラーが発生してしまいます。
このような場合には、エスケープ文字を使用して文字を文字コードに置き換えておく必要があります。例えば半角スペースなら、%20といった具合です。

Cocoa には、これを簡単に実現できる以下のメソッドが用意されています。

- ( NSUInteger ) replaceOccurrencesOfString:( NSString *) target withString:( NSString *) replacement options:( NSStringCompareOptions ) opts range:( NSRange ) searchRange;


target には置換後の文字列(%20)、replacement には置換対象の文字列 (半角スペース)、opts には文字列比較オプション、searchRange には、レシーバ文字列(URL文字列)内の適用範囲を与えます。なお、返り値は置換操作の実施回数が渡されます。

これを使用して、実際に URL 文字列内の半角スペースをエスケープするコードを示します。

[ urlString replaceOccurencesOfString: @"%20" withString: @" " options: NSLiteralSearch range: NSMakeRange( 0, [ urlString length ])];

ちょっと長ったらしいですが、これを使えば1行で済んじゃいますのでとても楽です。
NSString クラスのメソッドは癖のあるものも多いですが、非常に便利なので、なるべく修得しておきたいですね。


 HOME 

ブログ内検索

関連リンク

製品情報

最新記事

カテゴリ

プロフィール

neoxneo



NEXT-SYSTEM iOS Developers Blog


  • UTO:
    カナダ版iPhone4Sは、マナーモードでシャッター音がならない…


  • Ehara:
    ...


  • Hayate:
    ...


  • Tasaki:
    Developer登録完了...したのはいいけど


  • Ueda:
    ...



リンク

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。