.-- --

スポンサーサイト

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

カスタムインストールの謎

Tasaki です。

今日は、以前のエントリーで紹介した複数の SDK を共存させる際の
注意点についてです。

SDK のインストールフォルダを /Developer 以外に設定した場合、
シミュレータデバッグの際に、なぜかファームウェア 2.0 で
実行されてしまいます。
そのまま、ファームウェアを切り替えてアプリを起動すると
問題なく動作しますが、デバッグ機能が使えないので困りものです。

これが原因かは分かりませんが、
3.0 βの SDK をデフォルト以外の場所にインストールしてしまうと、
シミュレータへ 3.0 向けのアプリをインストールする際に
クラッシュしてしまいます。
(アクティブ SDK を 2.2.1 にすると動作しますが、
3.0 の API を使用できないので意味がありません)

この対処法として、
3.0 β向けを /Developer へ、
2.2.1 向けをそれ以外のディレクトリへインストールしておきます。
これにより、シミュレータ上でのテストができないという
最悪の事態だけはまぬがれることができました。

しかし、代わりに SDK 2.2.1 側で
シミュレータ上でのデバッグが事実上不可能になってしまうことには
変わりありません。


なんとか、この問題を回避できないものでしょうか?



スポンサーサイト
.30 2009

タッチイベントとビュー階層

Tasaki です。

今回は、タッチ操作によるビューの移動を
実装するにあたっての注意点を述べたいと思います。

タッチイベントは、ビューやそのコントローラの
対応するハンドラメソッド(例えば touchesBegan:withEvent: など)で
取得することが可能な訳ですが、
いくつかのビューを階層化している場合、
スーパービューの領域外にあるサブビューには、
新たなイベントが伝達されません。

つまり、touchesMoved:withEvent:の中で、
指の軌跡に沿ってサブビュー移動させ、
そのままスーパービューの枠外に出してしまうと、
指を放すまではイベントが伝達され続けますが、
次にサブビューをタッチしても、
touchesBegan:withEvent: が呼ばれることはありません。

そこで、このような状況が発生する場合には、
ビューではなく、ビューコントローラのハンドラメソッドで、
イベントの取得を試みる必要があります。
ただし、UITouch オブジェクトの view プロパティは、
触っているはずのサブビューを返してはくれませんので、
自力で、どのビューを触っているのか調べる必要があります。
(これがかなり面倒です…)

また、マルチタッチ機能のオン/オフ、
ユーザーインタラクションのオン/オフなどを制御する場合、
ビュー階層の途中で、1カ所でもオンになっていない場所があると、
そのサブビューもオフ扱いとなってしまいます。

複雑なビュー階層を構築する場合は注意しましょう。



.27 2009

applicationDidBecomeActive のタイミング

Tasaki です。

昨日のエントリーの結果はまだ不明ではありますが、
今回はレジューム機能を実装するポイントについて考えてみます。

UIApplicationDelegate プロトコルの、
- ( void ) applicationDidBecomeActive:( UIApplication ) application メソッドは、
アプリケーションがアクティブになったタイミングで
呼び出されるとされています。

これが、具体的にはいつなのか、ちょっと確かめてみました。

実験内容(実験という表現は、少しおおげさな気もしますが)は、
ルートビューコントローラの loadView と、
このメソッドのどちらが先に呼ばれるか、というものです。

結果は、ルートビューコントローラの loadView が先に呼ばれ、
その後、applicationDidBecomeActive: が呼ばれました。

レジューム作業は、アプリケーションの初期化が終わって、
ユーザーに制御を渡す前に行うのがタイミングとしては妥当なので、
applicationDidBecomeActive: の中でレジュームを
実行するのがよい、という結論に至りました。

複数のビューコントローラの loadView を初期化時に
呼んだ場合にどうなるのかは確かめていませんが、
おそらく、最後に applicationDidBecomeActive: が
呼ばれるんだと思います。


それにしても、レジューム機能の実装って
思った以上に厄介ですね。
相変わらず苦戦中であります。

.26 2009

ファイル入出力に苦戦中

Tasaki です。

今日はレジューム機能を実装すべく、
悪戦苦闘していました。

なんとか、一部分の読み込み、書き込みはできたものの、
実機テストに移行した途端、シミュレータでは起こらなかった
原因不明のエラーが発生してしまいました。

ただ、エラーを無視して、
操作を続けるようにしたところ、
何の問題もなく動いてしまいました。

一体何が起こっているんだ?
詳細が分かり次第、技術情報として公開したいと思います。
.25 2009

Quartz でグラデーション

Tasaki です。

ここのところ、Quartz の機能紹介の特集みたいになってますが、
今回もその辺りの話です。

Mac のプログラミング経験がある方は、難なく扱えるのでしょうが、
なにぶん、iPhone アプリ開発に際して初めて Mac に
触れた僕のような方は、結構いらっしゃるかと思いますので
参考になれば幸いです。

さて、Quartz にはグラデーションを描画するための機能が
備わっており、そのためのデータ型として CGGradient が用意されています。
また、そのパターンも線形と放射状の2通りがあります。
この辺の話は公式ドキュメントの Quartz 2D Programming Guide に
詳しく書かれています。

今回のコードの流れは、こんな感じです。
①グラデーション情報の定義
②パスの生成
③描画
④後処理

まず、グラデーション情報を定義する訳ですが、
これには次の2種類の関数が用意されています。
CGGradientRef CGGradientCreateWithColors( CGColorSpaceRef space, CFArrayRef colors, const CGFloat locations )
CGGradientRef CGGradientCreateWithColorComponents( CGColorSpaceRef space, const CGFloat components[], const CGFloat locations[], size_t count )

見たところ、両者の引数はかなり似ていますが、
色の指定に、Objective-C の配列を使うか C の配列を使うかによって
使い分けを行うための措置というところですかね。

僕はずっと C に親しんできたので、下側の関数を使うことにします。
space は色空間情報、
components は色を表す数値の配列、
locations は色の位置の配列を、
count は1色を表現するのに要するバイト量を表します。
ここでいう、色の位置とは0から1の間の数値を指し、
ある色が、グラデーション全体のどの付近で表れるかを決めるものです。
ここが NULL の場合は、線形的に色が変化していきます。

具体的には、例えば黒から白に線形に変わるグラデーションは、
このように表します。
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 0.0f, 0.0f, 0.0f, 1.0f,
             1.0f, 1.0f, 1.0f, 1.0f };
size_t count = sizeof( components )/ sizeof( CGFloat )* 4;
CGGradientRef gradient = CGGradientCreateWithColorComponents( space, components, NULL, count );

次は、パスの生成ですが、
これは前々回のエントリを参考にしてください。
ただし、描画関数を呼んでしまうと、
設定していたパスがクリアされてしまいますので注意してください。

続いて描画に関してですが、
このために、以下のグラデーション専用の関数が用意されています。
void CGContextDrawLinearGradient( CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options )
void CGContextDrawRadialGradient( CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGFloat startRadius, CGPoint endPoint, CGFloat endRadius, CGGradientDrawingOptions options )

前者が線形グラデーション、後者が放射状グラデーションを
描画するための関数です。

今回は線形グラデーションを試してみます。
引数ですが、
context、gradient の説明は省略しまして、
startPoint、endPoint はそれぞれグラデーションの始点と終点を、
options は始点と終点の外側の領域の描画方法を示すフラグを表します。

今回は、始点より前は始点と同じ色、終点より後は終点と同じ色として
描画する場合のコードを紹介します。
startPoint、endPoint はグラデーションをかけたい方向を考えて
与えるといいでしょう。
CGContextDrawLinearGradient( context, gradient, startPoint, endPoint, kCGGradientDrawBeforeStartLocation | kCGGradientAfterEndLocation );

最後に後始末をして、
色空間やグラデーションなどの使用済みのオブジェクトを解放します。
CGGradientRelease( gradient );
CGColorSpaceRelease( space );

今回は少し長くなってしまいましたが、
これで終わりです。お疲れさまでした。

.24 2009

Quartz で影を描く

Tasaki です。

前回、Quartz で角丸矩形のパスを生成して描画する関数を
紹介しましたので、今回はこれに影を付ける場合を考えてみましょう。

Quartz には、図形や文字などの影を描画する機能が備わっており、
影の設定を付け加えるだけで、簡単に描画できるようになっています。

そのための関数がこれです。
void CGContextSetShadow( CGContextRef context, CGSize offset, CGFloat blur )

まず、引数についてですが、
context は描画対象のグラフィックコンテキスト、
offset は、影のオフセット値、これは光源の座標と考えればいいでしょうか、
blur は影のブラーの強さを表しています。

要は、offset の値によって、影の付く方向や長さが変わり、
blur の値によって、境界付近のぼかし具合が変わるといった仕組みです。

これを影を付けたい物体の描画前に設定しておくと、
物体の描画にともない影が表示されることになります。

ただし、影ありと影なしの物体を
同時に描画する場合には注意点があり、
影の設定前にグラフィックステートの退避を、
描画終了後にグラフィックステートの復元を行う必要があります。

グラフィックステートの退避には、
void CGContextSaveGState( CGContextRef c ) を、
復元には、void CGContextRestoreGState( CGContextRef c )
それぞれ呼び出します。

グラフィックステートは、影、フォント、色、線の幅などの
コンテキストが描画に使用するための情報の集まりで、
この場合は、影を付けない状態を事前に保存しておいて、
後でその状態に戻すことになります。

これを踏まえて、コードの流れはこのようになります。
①グラフィックステートの保存
②影を設定
③描画
④グラフィックステートの復元

ちなみに、色付きの影を表示したい場合には、この関数を使います。
void CGContextSetShadowWithColor( CGContextRef context, CGSize offset, CGFloat blur, CGColorRef color )
.23 2009

Quartz でも角丸矩形

連休明けでイマイチ調子の出ない Tasaki です。

以前、角丸ボタンの作成法(というか対処法)について紹介しましたが、
今回は、Quartz を使ってコードのみで
角丸矩形を描画する方法を考えてみようと思います。
(といっても、Apple のサンプルプロジェクト UICatalog で
使われている手法の原理を考察したものでしかありませんが。)

このアプローチは、画像を用意しておく方法に比べて、融通が利きます。
そんなわけで僕は、これをテキスト表示の背景に使用しています。

標準で、これを実現する関数が用意されていないため、
パス生成関数を組み合わせて作成する訳ですが、
下の関数を使えば、コード量を抑えることができます。

void CGContextAddArcToPoint( CGContextRef c, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius )


引数についてですが、
c は現在のグラフィックコンテキスト、
x1, y1 は弧の始点を通る接線上の点の座標、
x2, y2 は弧の終点の座標、
radius は弧の半径を表します。

この関数は、パスの現在位置を A (x0, y0)、座標(x1, y1)を B、座標(x2, y2) をCとしたとき、
線分ABと線分BCに接する半径radiusの弧をパスに追加し、
パスの現在位置を弧の終点とします。
また、Aが弧の始点でない場合は、Aから弧の始点までを直線として
自動的にパスに追加してくれます。

図で描くとこんな雰囲気です。
glaph.png

で、肝心のコードの流れは以下のようになります。
①パスの始点を設定
②矩形の四隅に対して順番に弧を追加
③パスを閉じる
④描画

CGRect 型の変数に予め矩形情報を設定しておくと、
こんな感じのコードが書けます。
void CGContextFillStrokeRoundedRect( CGContextRef context, CGRect rect, CGFloat radius ) {
 CGContextMoveToPoint( context, CGRectGetMinX( rect ), CGRectGetMidY( rect ));
 CGContextAddArcToPoint( context, CGRectGetMinX( rect ), CGRectGetMinY( rect ), CGRectGetMidX( rect ), CGRectGetMinY( rect ), radius );
 CGContextAddArcToPoint( context, CGRectGetMaxX( rect ), CGRectGetMinY( rect ), CGRectGetMaxX( rect ), CGRectGetMidY( rect ), radius );
 CGContextAddArcToPoint( context, CGRectGetMaxX( rect ), CGRectGetMaxY( rect ), CGRectGetMidX( rect ), CGRectGetMaxY( rect ), radius );
 CGContextAddArcToPoint( context, CGRectGetMinX( rect ), CGRectGetMaxY( rect ), CGRectGetMinX( rect ), CGRectGetMidY( rect ), radius );
 CGContextClosePath( context );
 CGContextDrawPath( context, kCGPathFillStroke );
}

これを使えば角丸矩形を簡単に描くことができます。

.19 2009

SDK 2.2.1と3.0βの共存

おはようございます。Tasaki です。

昨日の掲載分で、3.0β向けと2.2.1向けのSDKの
共存ができないかとぼやいていましたが、
できたみたいなので紹介します。

やり方は簡単で、
SDKインストール時のカスタムインストール設定のところで、
デフォルトでは、Developer ディレクトリが指定されていますが、
ここをダブルクリックして、“その他”を選択し、任意の場所を指定します。
後は今まで通りインストール作業を進めます。

Xcode は、SDK 2.2.1 には、3.1.2 が
SDK 3.0β には、3.1.3 が含まれていますので、
このバージョンの違いで、どちらで開くべきか判断できます。

2.2.1 向けのプロジェクトの場合、
コンテキストメニューの“情報を見る”を選び、
“このアプリケーションで開く”の設定を“Xcode(3.1.2)”にしておけば、
プロジェクトファイルをクリックしたときに、
2.2.1 向けのプロビジョニングが認識される Xcode 3.1.2 が起動されます。
ChooseApp.png
ただし、同名のアプリケーションであるためか、
デフォルト設定は上手くいかないようです。

これで、3.0βの研究ができるようになりました。
いやー、めでたし、めでたし。

…とはいえ、OS の方の更新は怖くてできないですね。

.18 2009

iPhone SDK 3.0β のワナ

Tasaki です。

ついに iPhone OS および SDK 3.0 のβ版が公開されましたね。
今までできなかった、あんなことやこんなことが
実現可能になる!と夢は膨らみますが…

なんてこった!
SDK 3.0 では、2.2.1 以前のアプリのプロビジョニングが
正しく機能しなくなるではないですか!
これでは、実機テストどころか、配布用ビルドすら実行できません。
iPhone Dev Center にもその旨が記載されていました。
(Show Detail 押さないと分かりませんが)

現行バージョンでの開発と並行して、
次世代バージョンの研究ができればよかったんですが…、
結局、元に戻すハメに。

で、SDK のバージョンの戻し方ですけど、
そのまま SDK 2.2.1 を再インストールで OK でした。
本当は、3.0 をアンインストールしてから、
2.2.1 を再インストールした方がいいのかもしれませんが、
これでも今のところ問題なく動いています。

それにしても、現行版と次世代版の SDK って共存できないんですかね。
まさか、Mac 2台用意してくださいってことはないですよね?


.13 2009

AVAudioPlayer とオーディオセッション

やっと iPhone SDK での開発になれてきた折に
次期メジャーバージョン 3.0 が間もなく公式発表される
ということで、個人的には少し複雑な心境の Tasaki です。

せめて1本目を先にリリースしたい…

今回は、iPhone でサウンドを使用する場合に
知っておきたいオーディオセッションの初期化に関する話を
しようと思います。

iPhone OS 2.2 から、AVFoundation という
新たなフレームワークが追加されたわけですが、
これを使うことで、Audio Queue Services や OpenAL などの
低水準フレームワークを使わずに音声ファイルの再生を
行えるようになりました。

それまでは、機能が著しく限定された System Sound Services だけが
他のアプローチに比べて断然簡単に実装することができたわけですが、
AVFoundation の追加によって Objective-C ベースでの
サウンドファイルの制御が可能になったのはうれしいかぎりです。
(そもそも Audio Queue Services は、いまだによく分かっていません)

ただ、AVAudioFoundation フレームワークの
AVAudioPlayer クラスを利用する場合は
オーディオセッションの初期化などが不要なので
敷居が低い代わりに、このオーディオセッションへの
理解が必ずしも伴わなくなるという側面があります。
(僕がそのうちの1人だったわけですが)

そもそもオーディオセッションとは、
アプリケーションにおけるサウンド再生のふるまいを定義するものです。
これを正しく設定することで
iPod で再生中の楽曲とアプリケーション独自のサウンドを
ミックスしたり、スリープ後もサウンドの再生を続行するなどの
効果が現れます。

上記の効果を得るための手順は簡単です。
①オーディオセッションを初期化する。
②オーディオセッションカテゴリを設定する。
③AVAudioPlayer オブジェクトを初期化する。

まず、オーディオセッションの初期化についてですが、
その前に AudioToolBox フレームワークをインポートしておきます。
その後初期化関数を呼び出します。
Audio Queue Services などを使用する場合に比べて
初期化のための引数は超単純です。
以下にコード例を示します。

#import <AudioToolbox.h>

...
AudioSessionInitialize( NULL, NULL, NULL, NULL );

…まさか、これで動くとは思いませんでした。


次にオーディオセッションカテゴリを設定します。
オーディオセッションカテゴリは、幾つかあり
それらは定数で定義されています。
例えば、iPod で再生中の楽曲とアプリケーションの効果音を
ミックスしたいという場合には、
UserInterfaceSoundEffects カテゴリが適しています。
以下のコードでオーディオセッションカテゴリを変更することができます。
UInt32 category = kAudioSessionCategory_UserInterfaceSoundEffects;
AudioSessionSetProperty( kAudioSessionProperty_AudioCategory, sizeof( category ), &category );

あとは、AVAudioPlayer オブジェクトを
いつものように初期化すればいいだけです。

特にアプリケーションで BGM を使用しない場合には、
このカテゴリに設定しておくと
ユーザーにより自由なプレイスタイルを提供できます。
音楽は要らないと思えば、自分で iPod の再生を止めればいい
だけですからね。


AVAudioPlayer は簡単なのがいいですね。
…本当は、Audio Queue Services とか OpenAL とか
触ってみたいとは思うんですがねー。

.12 2009

Interface Builder を使ってみる(2)

Tasaki です。

前回、途中までだった Interface Builder の
導入手順の続きを紹介します。

前回の最後の操作では、クラスアウトレットを追加しました。
これを使ってクラス同士を結びつける必要があります。
Nib ファイルウィンドウのアウトレットを追加したクラスの
アイコン上でControl + クリック操作を行うと
下のようなポップアップが表示されます。
これはコネクションパネルと呼ばれています。

Popup1.png

Outlets の下に追加したアウトレットが表示されていますね。
window の行の右には◉が表示されていますが、
label の行の右には○が表示されています。
◉はクラスが対応済みであるのに対し、
○はクラスが未対応であることを表します。

それでは、label の右にある○を選択し、
そのまま Nib ファイルウィンドウの Label( Label ) へドラッグ&ドロップしてみます。
すると、下のように◉に表示が変わりました。
これでクラスとアウトレットが対応しました。

Popup2.png

次に Nib ファイルウィンドウ上の Label を
Window の任意の位置へドラッグ&ドロップします。

Layout.png

ひとまず、Interface Builder での作業は完了しましたので、
メニューから "File" > "Save" を選び、保存します。

次に Xcode で、Window の例にならい、
アウトレットを追加したクラスのインターフェース部に
UILabel *label オブジェクト変数を
IBOutlet UILabel *label プロパティを追加します。
コードはこのようになります。
#import <UIKit/UIKit.h>

@interface IBTestAppDelegate : NSObject < UIApplicationDelegate > {
UIWindow *window;
UILabel *label;
}

@property ( nonatomic, retain ) IBOutlet UIWindow *window;
@property ( nonatomic, retain ) IBOutlet UILabel *label;

@end

次に実装部の冒頭に
@synthesize label;
を追加し、
window のサブビューに label を追加します。
実装部のコードはこのようになります。
#import "IBTestAppDelegate.h"

@implementation IBTestAppDelegate

@synthesize window;
@synthesize label;

- ( void ) applicationDidFinishLaunching:( UIApplication *) application {
 [ window addSubview: label ];
 [ window makeKeyAndVisible ];
}

- ( void ) dealloc {
 [ label removeFromSuperview ];
 [ window release ];
 [ super dealloc ];
}

@end

これで完成です。
ビルドして実行すると、シミュレータでは、このように表示されます。

Simulator.png
Interface Builder 側だけで完結しないのが
今ひとつしっくりきませんが、
これで、UI の設計はコードをいじることなく
視覚的に行えるようになります。

最終的に Nib ファイルを使わない場合でも、
初めのうちはこのようにして、
レイアウトを考えておくのも
いいのではないでしょうか?

なにより、デモ画面の制作などには
特に活用できる気がします。


.11 2009

Interface Builder を使ってみる(1)

Tasaki です。

僕は今まで、Interface Builder を使わずに
開発を行ってきましたが、
アプリの UI の実機テストをやる場合には、
すごく便利だということを今更ながらに実感したので、
今回のテーマに取り上げました。

Interface Builder は、Nib ファイルの編集用のツールという
位置づけで視覚的な編集が可能な点が魅力です。

iPhone アプリのテンプレートから作成したプロジェクトは、
必ず MainWindow.xib という Nib ファイルを持っているので、
これをいじってみるのが、手っ取り早い方法です。

プロジェクトウィンドウから MainWindow.xib をダブルクリックすると、
Interface Builder が起動して、MainWindow.xib が開きます。

MainWIndow.png

メニューバーの Tools から、Library と Inspector を選んで、
それぞれのウィンドウを表示します。

例えば、UILabel をMainWindow.xib に追加する場合は、
Library ウィンドウから UILabel を探し出し、
MainWindow.xib に Finder と同じ要領でドラッグ&ドロップします。
すると、このように UILabel が追加されました。

AddLabel.png

次に、このオブジェクトを保持するオブジェクトに
オブジェクト変数を追加します。
保持させたいオブジェクトを選択します。
ここでは、アプリケーションデリゲートにその役目を与えます。

SelectAppDelegate.png

そのあとで、 Inspector ウィンドウの左上にあるインフォアイコンの
表示されたタブを選択すると、
Class Outlets というリストに UIWindow *window が登録されているのが
確認できます。

Inspector1.png

この下の + ボタンを押すことで、
オブジェクト変数を追加していくことができます。
左が変数名、右がクラス名となっています。
クラス名はフレームワークで提供される基本クラスだけでなく、
独自のクラスも選択できます。
(ただし、その場合は予め Xcode を使ってプロジェクトに
クラスを追加しておく必要があります。)
ここでは、先ほどの UILabel オブジェクトを使えるようにするために、
UILabel *label を追加します。

Inspector2.png

ちょっと長くなりそうなので、
今回はここまでにしたいと思います。


.10 2009

やっぱり公式ドキュメントは正しかった!

iPod touch のあまりの薄さに
驚いている Tasaki です。

前回、iPod touch の識別用文字列が分からん、
とぼやいていましたが、
今日実機を手に入れましたので、
早速ですが iPhone、iPod touch、シミュレータが
UIDevice クラスの model メソッドで返す文字列をまとめてみました。

デバイス文字列
iPhone 3GiPhone
(2G) iPod touchiPod touch
iPhone シミュレータiPhone Simulator

タイトルのまんまでした。
信じるものは救われる、ってことですね。
なお、初代 iPhone と (1G) iPod touch については、
ちょっと検証できませんでしたけれども。

ただ、前回書いたように、
機種で判別するよりも、機能の有無で判別する方が
大抵の場合、ベターだと思われます。
前者を採用する前に、後者では実現困難なのかどうか
よく検討する必要がありそうです。


……それにしても、3G回線に比べると
Wi-Fi でのインターネットアクセスは圧倒的に高速ですね!
ダイヤルアップからブロードバンドに乗り換えた頃を思い出しました。

.09 2009

iPhone と iPod touch の判別について

先週末は、2度目のリジェクトでちょっとヘコんでた
Tasaki です。

今回は、その原因となった
機種の判別の仕方について紹介します。

前回の申請で問題になった点は、
“第2世代の iPod touch でツールバーに配置していたボタンが上手く動作しない”
というものでした。

これは、コード中での実機の判別法が
まずかったために起こってしまったのだと思われます。

iPhone SDK では、以下の方法でデバイスの種類を表す
文字列を取得することができます。
NSString *theDeviceName = [[ UIDevice currentDevice ] model ];

で、iPhone を開発機材として使用していたため、
iPhone とシミュレータは、実際の値を検証することができたのですが、
iPod touch は実機がなく、公式ドキュメントやインターネット上で
情報を得るしかありませんでした。
(Ad Hoc で配布して動作確認をお願いする方法もあることはありますが…)

ところが、調査の結果
iPod
iPod touch
IPod touch
iPod Touch
と複数の候補が出てきてしまい、
結局何が正しいのか分からない状態に陥ってしまいました。
もしかすると、この中に正解がないことも考えられます。

で、結局 iPod を選択したのですが、
少なくとも第2世代は、この値ではなかったようです。
(第1世代 touch で検証されたか分からないので、
第2世代とは、別の値を返す可能性もありますね)

ならば、とりあえず全ての候補を
対象にすればいいじゃないかと突っ込まれそうですが、
この方法はまた、根本的な弱点をはらんでいます。

それは、新たなデバイスが発売される度に
この部分のコードを修正していかなくてはならない、という点です。

記事の執筆時点では、iPhone、iPod touch 以外の機種は
まだ発売されていませんが、この問題が後々大きな影響を及ぼす
可能性もあります。

そこで、考え方を少し変えてみることにしました。
今回のケースでは、カメラ機能の有無で
UI のレイアウトを変更したかっただけなので、
カメラ機能が利用可能かどうかを調べるようにしました。
要は、こんな感じです。

if([ UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera ]) {
// カメラ搭載機(iPhone, Simulator)の処理
} else {
// カメラ非搭載機(iPod touch)の処理
}

こうすれば、例え新機種が発売されてもコードを変更しなくてすみます。
ただし、何らかの理由でカメラ搭載機において、
isSourceTypeAvailable: が NO を返した場合に
不具合が発生してしまう可能性はありますけど。

Apple の開発者フォーラムにおいても
機種を判別するより、機能毎に判別した方がいいという
Apple の技術者からのアドバイスが提示されていましたし、
条件分岐にデバイスの文字列を利用する方法は、
なるべく避けた方が良さそうです。


……今度こそ、審査を通過しますように
.05 2009

角丸ボタンの作成法(暫定版)

昨日掲載分がアップロードされずに悩んでいたら、
ブログの投稿区分が下書きになっていたというオチで
時間をムダにしてしまった Tasaki です。

そんなところいじった覚えはないんだけどな……

それはさておき、今回は前回の続きということで、
実際に任意の背景をもつ角丸ボタンの作り方を紹介します。

これは特に難しいところはありませんが、回りくどい感じはします。

まず、角を丸くした画像を用意します。
例えば、以下のこのような感じで。

PanelTemplate.png

作り方は PhotoShop などの画像編集用ソフトの解説などを見ていただくとして、
要するに Custom ボタンの背景としてこの画像を貼付けるわけです。

このとき、そのまま画像を引き延ばしてしまうと、
角の部分も同じように引き延ばされてしまうので
角が丸でなくギザギザに見えてしまいます。

そこで、角はそのままにしておき、
真ん中だけを引き延ばすことになるのですが、
まさにそのために用意されたメソッドがあります。

それが、これです。
- ( UIImage *) stretchableImageWithLeftCapWidth:( NSInteger ) leftCapWidth topCapHeight:( NSInteger ) topCapHeight;


このメソッドはレシーバの左右から leftCapWidth ピクセル、
上下から topCapHeight ピクセルをスケーリングの対象から
省いた UIImage オブジェクトを返すという便利なものです。

この後で、返り値オブジェクトを拡大すると
意図した画像を得ることができます。

コードは例えばこのような感じです。
UIImage *sourceImage = [ UIImage imageNamed: @"Button.png" ];
UIImage *strechableImage = [ sourceImage stretchableImageWithLeftCapWidth: 10 topCapHeight: 10 ];
UIButton *button = [ UIButton buttonWithType: UIButtonTypeCustom ];
[ button setBackgroundImage: strechableImage forState: UIControlStateNormal ];


.04 2009

ボタンの種類と落とし穴

Tasaki です。

今回のテーマはアプリケーションでよく使われるボタンについてです。

UIKit フレームワークでは、汎用ボタンクラスとして、
UIButton クラスが提供されています。

このクラスのイニシャライザでドキュメントに掲載されているものは、
+ ( id ) createWithType:( UIButtonType ) buttonType のみであり、
引数の値によって、以下のような数種類のボタンが作成できます。

UIButtonTypeCustom … カスタマイズ可能なシンプルなボタン
UIButtonTypeRoundedRect … 角が丸くなったボタン
UIButtonTypeDetailDisclosure … 青地に白の > 記号が描かれたボタン
UIButtonTypeInfoLight … 白い i ボタン
UIButtonTypeInfoDark … 黒い i ボタン
UIButtonTypeContactAdd … 青地に白の + 記号が描かれたボタン

このうち、RoundedRect ボタンは一見使えそうに見えますが、落とし穴があります。
それは、Custom ボタンでないせいか、カスタマイズができないというものです。
ここでいうカスタマイズとは、背景色の変更や画像の表示を指しており、
結局のところ、角が丸くなっただけの白いボタンを作成することしかできません。
(色々試したところ、こういう結論に至りました)
これは何を意図して用意されたのか、ちょっとつかめません。

ちなみにこれです。
Rounded.png


ということで、角が丸く、任意の背景をもつボタンを作成する場合は、
別のアプローチが必要になります。

僕の場合は、Custom ボタンに角を丸めた画像を貼付ける方法で対処することにしました。
公式サイトにある UICatalog アプリにおいても、
Custom ボタンの実装は、そのようになっていました。


次回は、その方法についてご紹介したいと思います。

 HOME 

ブログ内検索

関連リンク

製品情報

最新記事

カテゴリ

プロフィール

neoxneo



NEXT-SYSTEM iOS Developers Blog


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


  • Ehara:
    ...


  • Hayate:
    ...


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


  • Ueda:
    ...



リンク

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