2010年12月23日木曜日

three20 をプロジェクトに追加する

1.
プロジェクトフォルダ内に three20 フォルダを置く

2.
スクリプト実行

python ./src/scripts/ttmodule.py -p (プロジェクトフォルダへのパス)/(プロジェクト名).xcodeproj -c Debug -c Release Three20

python ./src/scripts/ttmodule.py -p (プロジェクトフォルダへのパス)/(プロジェクト名).xcodeproj Three20 // " -c Debug -c Release " オプションを付けると、他のライブラリを追加したときにリンクエラーが出るので使用をやめた

3.
ヘッダ検索パスに追加
(three20 フォルダへのパス)/three20/Build/Products/three20

4.
他のリンカフラグに、 -all_load を追加する(私の場合、すでに -ObjC -lxml2 が追加されているのでこれで3つ追加されていることになる)。
---------------------
git のthree20 のページを参照する。
色々説明がかいてあるので。

three20 gitページ
↑ページにヘルプを見ろと書いてる
python ./src/scripts/ttmodule.py -h

ヘルプに以下のような記述
Add the Three20 project settings to the Debug and Release configurations.
  This includes adding the header search path and linker flags.
  > ttmodule.py -p path/to/myApp.xcodeproj -c Debug -c Release
→ヘッダ検索パスとリンカフラグを追加すると書いてある

2010年12月2日木曜日

GDA のクラス関係

Google Data APIs Objective-c Client Library (GDA) の、picasa 関連のクラス関係図をxcode で一部可視化してみる。






GDataFeedPhotoBase と GDataEntryPhotoBase のスーパークラスは共に、GDataObject クラスである。

--------
xcode でクラス図を作成する。
設計 -> クラスモデル -> クイックモデル

2010年12月1日水曜日

UIImage でURL の画像を表示する。

  NSString *path = @"http://*******/************/**.jpg"; // URL
NSURL* url = [NSURL URLWithString:path];
NSData* data = [NSData dataWithContentsOfURL:url];
photo = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithData:data]];
[self.view photo];

2010年11月15日月曜日

プロパティに関するコーディングルール

Google Objective-c スタイルガイド 日本語訳 の、プロパティについて以下を参考にする。

文字列には copy を使うこと
NSStringプロパティには、必ず copy 属性を宣言するべきです。

これは論理的には、NSString のセッタには必ず、retain の代わりに copy を使わなければならない、というルールに従ったものです。

アトミック性
プロパティにはオーバーヘッドがあることを認識しておいてください。デフォルトでは、同期のセッタとゲッタはすべてアトミックになります。そのため、セットとゲットの呼び出しには同期オーバーヘッドがかなりあります。アトミック性が必要ないなら、プロパティを nonatomic と宣言しておいてください。

Delegate パターンについても以下を参考にする。
Delegate パターン
Delegate オブジェクトを retain するべきではない。

<-- 追記 -->
命名規則
プロパティに対応するインスタンス変数名は、アンダースコア(_)で終わらなければなりません。プロパティ名は、対応するインスタンス変数名から最後のアンダースコア(_)を取り除いたものにするべきです。

@interface MyClass : NSObject {
@private
NSString *name_;
}
@property(copy, nonatomic) NSString *name;
@end

@implementation MyClass
@synthesize name = name_; // インスタンス名を隠蔽できる。
@end
// ------------------------------------
NSLog("%@", self.name);

-(void) dealloc{
self.name = nil;
[super dealloc];
}

参考
Google Objective-c スタイルガイド 日本語訳

2010年11月13日土曜日

プロパティ

目的*1
■ プロパティ宣言により、アクセサメソッドの動作方法の明瞭で明示的な仕様を指定できます。
■ コンパイラは、宣言で指定された仕様に従ってアクセサメソッドを合成できます。これは、コードの記述と保守が少量で済むことを意味します。
■ プロパティは構文上は識別子として表現され、範囲付きであるため、コンパイラは宣言されていないプロパティの使用を検出できます。
■ あるクラスによって宣言されたプロパティの実行時のイントロスペクション。
-----------------------------------

イントロスペクション(リフレクション)*2
通常、プログラムのソースコードがコンパイルされると、プログラムの構造などの情報は低レベルコード(アセンブリ言語など)に変換される過程で失われてしまう。リフレクションをサポートする場合、そのような情報は生成されるコードの中にメタデータとして保存される。

宣言属性
iPhone 開発で主に使用される属性をまとめた。
プロパティ属性 説明
assign setterで単純代入を使用することを指定する
retain 代入時にオブジェクトに対してretain を呼び出す必要があることを指定する
copy 代入にオブジェクトのコピーを使用することを指定する
nonatomic 合成されるアクセサが非アトミックになるように指定する

retain*3
クライアントオブジェクト側において、プログラム上の有効範囲を超えた後も受け取ったオブジェクトを保持する必要がある場合は、受け取ったオブジェクトを保持できます。それには、retainメッセージを送信します。

nonatomic*4
nonatomic属性の影響は環境によって異なります。デフォルトでは、合成されるアクセサはすべてアトミックです。マネージドメモリ環境では、アトミックな動作を保証するにはロックを使用する必要があります。また、返されるオブジェクトは保持され自動解放されます。このようなアクセサが頻繁に呼び出されると、アトミックな動作はパフォーマンスに重大な影響をもたらすことがあります。ガベージコレクトされる環境では、ほとんどの合成メソッドはこうしたオーバーヘッドなし
でアトミックになります。
アトミックな実装の目的は堅牢なアクセサを提供することであり、コードの正確性を保証することではないことを理解することが重要です。「アトミック」とは、プロパティへのアクセスがスレッドセーフであるという意味ですが、単にクラス内のすべてのプロパティをアトミックにするだけでそのクラス(一般にはオブジェクトグラフ)が「スレッドセーフ」になるということではありません。スレッドの安全性を個々のアクセサメソッドのレベルで表現することはできません。

参照

*1
Objective-C 2.0 プログラミング言語

*2
イントロスペクション(リフレクション)

*3
Cocoa Fundamentals Guide / cocoaオブジェクトのライフサイクル

*4
objective-c 2.0 プログラミング言語 / パフォーマンスとスレッド

2010年11月12日金曜日

参照カウンタ

objective-c のメモリ管理方法。
基本原則
「alloc」または「new」で始まる名前のメソッドや、「copy」を含む名前のメソッド(たとえば、alloc、newObject、mutableCopy)を使用してオブジェクトを作成した場合、またはオブジェクトにretainメッセージを送信した場合は、そのオブジェクトの所有権を取得できます。
その場合は、releaseまたはautoreleaseを使用してオブジェクトの所有権を放棄する責任が
あります。それ以外の方法でオブジェクトを受け取った場合は、そのオブジェクトを解放してはなりません。

メッセージ 保持カウント
alloc 1にする
copy 1にする
new 1にする
retain 1つ増える
release 1つ減る
autorelease 1つ減る(ただし、任意のタイミング)


参考
Cocoaメモリ管理プログラミングガイド

2010年11月10日水曜日

戻る/進む(unDo/reDo)

戻る/進む を行うショートカット

戻る
⌘ + option + ←

進む
⌘ + option + →

2010年11月9日火曜日

Dropbox に gitレポジトリを作成する

1.
Dropbox フォルダ内に gitレポジトリ用のフォルダを作成する。
cd ~/Dropbox
mkdir repository

2.
上記フォルダ内に、[プロジェクト名].git というフォルダを作成して init する。
cd repository
mkdir ProjectName.git
cd ProjectName.git
git --bare init

--bare
Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory. *1

裸のリポジトリ(bare repository)
裸のリポジトリとは、通常 .git の拡張子を持つ ディレクトリ で、 リビジョン管理下にあるチェックアウトしたファイルをローカルに持たないディレクトリです。 通常 .git サブディレクトリ に隠れている git の管理ファイル全てが repository.git ディレクトリに直接存在し、 他のファイルは存在せず、チェックアウトもされていません。 通常、公開リポジトリを出版する人は、裸のリポジトリを作成します。*2

3.
既存のソースを追加するためには、そのプロジェクト上で以下のコマンドを実行する。
cd ~/develop/OldProject
git init
git add . // 全てのファイルを追加する
git commit -m "(コメント入力)"
git push ~/Dropbox/repository/ProjectName.git master
git remote add origin ~/Dropbox/repository/ProjectName.git

4.
ファイルを更新した後、以下のように更新したファイルの addとcommitを行い、さらに ローカルレポジトリ(既存プロジェクト)をリモートレポジトリ(Dropbox に作成したレポジトリ)へ push する。
git add ~/develop/OldProject/oldfile.h // 変更を全て含む場合、git add --a
git commit -m "(コメント入力)"
git push ~/Dropbox/repository/ProjectName.git master //git push origin master でも可

参考:
*1
git-init(1) Manual Page

*2
Chapter11. GIT用語集 裸のレポジトリ

2010年11月6日土曜日

iPhone で Picasa クライアントを作成する準備

Google Date API
http://code.google.com/intl/ja/apis/gdata/index.html

Google Date API にアクセスして、Picasa データを取得する。
iPhone で利用するためには、gdata-objectivec-client を静的ライブラリとして組み込む。

参考資料
BuildingTheLibrary の、Linking to the iPhone Static Library の内容に(ほぼ)従う。
http://code.google.com/p/gdata-objectivec-client/wiki/BuildingTheLibrary

ライブラリの準備
下記場所からダウンロードできるが、ビルドエラーが出る。
ライブラリ名:gdata-objective-client-1.10.0.zip
http://code.google.com/p/gdata-objectivec-client/downloads/list

なので、svn から直接ダウンロードして、iPhone プロジェクトのディレクトリに入れる。
http://code.google.com/p/gdata-objectivec-client/source/checkout
svn checkout http://gdata-objectivec-client.googlecode.com/svn/trunk/ gdataClient
mv  gdataClient samplePicasa


gdata-objectivec-client をビルドするための準備
1.
gdataClient -> Source -> GData.xcodeproj を開く。
2.
ビルドターゲットを GDataTouchStaticLib に変更する。
 今回作成する静的ライブラリはコレなので、他はターゲットから外す。
3-1.
ターゲット -> GDataTouchStaticLib -> 情報を見る -> ビルドタブ 構成:release を表示する。
設定 -> ベースSDK を、iPhoneデバイス4.0 に変更する。
設定 -> アーキテクチャ を、Standard に変更する。

3-2.
その他のCフラグに以下を追加する。
-DGDATA_INCLUDE_PHOTOS_SERVICE=1
 picasa を利用する場合の設定はこのようになる。それ以外のgdataを利用する場合は、参考 *2 を参照する。



4.
Simulator, Device それぞれReleaseビルドを実行する。
5.
出力されたライブラリを lipo コマンドを使用して統合する。
cd samplePicasa/gdataClient/Source/build
lipo -create Release-iphoneos/libGDataTouchStaticLib.a Release-iphonesimulator/libGDataTouchStaticLib.a -output libGDataTouchStaticLib.a


iPhoneプロジェクトにライブラリを追加する
6.
libGDataTouchStaticLib.a を iPhoneプロジェクトに、追加 > 既存のファイル で追加する。
 今回のiphoneプロジェクト名は samplePicasa とする。




7-1.
samplePicasa -> 情報を見る -> ビルド 全ての構成 を表示する。
他のリンカフラグに、-ObjC -lxml2 を追加する。




7-2.
ヘッダ検索パスに、次の2つのパスを追加する。
/usr/include/libxml2 → 再帰的にチェックする。
$(SRCROOT)/gdataClient/Source/build/Release-$(PLATFORM_NAME)/Headers



8.
適当なファイルに #import "GData.h" を追加してビルドが通れば完了。


参考
*1
iPhone開発ガイド 第3章アプリケーションの実行 ビルド環境の設定
*2
Removing Unneeded Code

2010年10月5日火曜日

TCP ACCEPT関数

accept - ソケットへの接続を受ける


struct sockadd_in の実装は以下の通り。
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};

/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};

ここで、inet_ntoa()関数を使用して sockaddr_in 構造体から IP アドレスを表示させる実装は、以下のようになる。
printf("IP = %s\n", inet_ntoa(client.sin_addr));

ただし、次のヘッダーファイルをインクルードしなければ、表示されないので注意!
<arpa/inet.h>
数値としてIPアドレスを操作する機能の定義

参考:
ACCEPT

ソケット(BSD)

TCP

TCPとは、以下のことを言う。
Transmission Control Protocol(トランスミッション コントロール プロトコル、TCP)
セッションという形で1対1の通信を実現し、パケットシーケンスチェックによる欠損パケット再送などのエラー訂正機能などを持ち、データ転送などの信頼性の必要な場面でよく使用される。一方他のトランスポート層プロトコルに比べ、プロトコル上のオーバヘッドが大きい為、比較的低速となる。

サーバー側の実装は以下の手順で行う。
1.
socket(2) でソケットを作成する。
2.
bind(2) を使ってソケットにローカルアドレスを割り当てて、 他のソケットがこのソケットに connect(2) できるようにする。
3.
listen() を使って、接続要求を受け付ける意志と接続要求を入れるキュー長を指定する。
4.
accept(2) を使って接続を受け付ける。

参考:
Transmission Control Protocol(TCP)

LISTEN

2010年10月4日月曜日

ctrl-c で強制終了

無限ループ等で処理が終わらなくなった場合、次のやり方で処理を止める。
ctrl-c
これをすれば、処理は強制終了する。

memset関数で出たwarning

char型配列 buf[] を「0」で埋めるため、以下の処理を書いたら warning が発生した。
memset(buf, 0, sizeof(buf));
warning: incompatible implicit declaration of built-in function ‘memset’
対処方法
→ #include<string.h> しましょう!!!
man memset を見れば、SYNOPSIS(概要)に書いてある。

2010年9月22日水曜日

Day6 Reaction Time(3)信号機画像を追加する

信号機ケーブル画像を追加する
画像をプログラミングで、表示したい位置に追加するために以下のように実装した。

UIImage* stopLightCableImage = [UIImage imageNamed:@"stopLightCable.png"];
UIImageView* stopLightCableView = [[UIImageView alloc] initWithImage:stopLightCableImage];
stopLightCableView.frame = CGRectMake(150, 0, stopLightCableImage.size.width, stopLightCableImage.size.height);
stopLightCableView.image = stopLightCableImage;
[self.view addSubview:stopLightCableView];

2行目
initWithImage メソッドで指定した画像で初期化する。

3行目
UIImageView の frame メソッドで表示位置を指定する。
→ bounds メソッドでは、stopLightCableView の、ローカルな座標の指定になるので間違い。frame メソッドで、親Viewから見た座標で位置を指定する。


4行目
image プロパティに画像を指定する。

信号機画像3種類をランダムに表示させるメソッドを実装する

以下の内容を訂正する。これではやりたかったことが実現できないので。。
訂正ここから-----------------------------------
以下のように実装した。
-(NSArray *)makeAnimationImagesArray{

NSMutableArray* trafficLightArray = [NSMutableArray arrayWithObjects:
[UIImage imageNamed:@"redLightSmall.png"],
[UIImage imageNamed:@"yellowLightSmall.png"],
[UIImage imageNamed:@"greenLightSmall.png"], nil];

int count = [trafficLightArray count];
srand([[NSDate date] timeIntervalSinceReferenceDate]);

int i = 0;
for(i=0; i<10; i++){
NSUInteger pos1 = rand() % count;
NSUInteger pos2 = rand() % count;

[trafficLightArray exchangeObjectAtIndex:pos1 withObjectAtIndex:pos2];
}

return(trafficLightArray);
}
9行目擬似乱数の発生系列を変更する種を、現在時刻から生成する。
10行目〜0〜2をランダムに生成する。訂正ここまで-----------------------------------


改めて、信号機画像3種類を表示させる
次のメソッドを使用して、現行ループにタイマーとスケジュールを作成する。
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode.
このメソッドで、3種類の画像(黄→赤→緑)を3秒おきに表示させる。
アクセルペダルをタップした時の動作を実装する事前にフラグを持たせておいて、緑色画像が表示されたらフラグをONにする。また、その時の時刻を取得しておく。
次のメソッドを使用して、緑色画像が表示された時刻と、現在時刻の差を取得する。
timeIntervalSinceNow
Returns the interval between the receiver and the current date and time.

結果をUIAlertView で画面に表示する。

frame と boundsとcenter の関係

ビューオブジェクトである、frame,bounds,center は以下のように定義されている。
frame(フレーム)プロパティは、親ビューの座標系に基づいて、そのビューの
位置とサイズを定義した矩形(フレーム矩形)を表します。
フレーム矩形は、boundsとcenterを使用して計算される便利な値です。これは、ビューの変換が、恒等変換に設定されている場合にのみ有効です。
bounds(境界)プロパティは、ビュー固有のローカルな座標系に基づいて、ビューの位置とサイズを定義した矩形(境界矩形)を表します。境界矩形の原点は、通常(0, 0)に設定されますが、必ずしもその必要はありません。
境界矩形は、そのビューのローカルな座標系を表すので、描画やイベント処理のコードの中で、何らかの変化がビュー内のどこで発生したかを知る必要があるときに、最もよく使います。
center(中心)プロパティは、フレーム矩形の中心点を表します。
中心点は、ビューの既知の中心点を表すので、ビューの位置を操作するのに最適な方法です。
----------------------------------------
frame と bounds の関係













参考PDF:
iPhone アプリケーションプログラミングガイド
Stanford University / CS 193P iPhone Apprication Development / Title: Lecture #5 - Views, Drawing, Animation

2010年9月17日金曜日

Day6 Reaction Time(2)view に背景画像を追加する

背景画像として、road.png をview に追加する。
road.png をResources フォルダに追加して、以下のように実装した。

- (void)viewDidLoad {
[super viewDidLoad];

UIImage* backgroundImage = [UIImage imageNamed:@"road.png"];
UIImageView* backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
[backgroundView setFrame:[[UIScreen mainScreen] bounds]];
[self.view addSubview:backgroundView];

[backgroundView release];
}

参考にしたのは以下の箇所。
How do I create a view for images?
To create a view for images, use the UIImageView class, as shown in Listing 14.

Listing 14: Creating a view for images
UIImage *image = [UIImage imageNamed:@"image.png"];
UIImageView *theImageView = [[UIImageView alloc] initWithImage:image];
さらに、UIImageView を setFrame メソッドで画面全体に表示させて、view に追加している。

参考サイト:

User Experiense Coding How-To's

Day6 Reaction Time(1)画面構成

Reaction Time アプリの内容はこんな感じか。

アプリの内容
画面中央に表示されている信号が青に変わったら、右下にあるアクセルペダル画像をタップすると、何ミリ秒で反応したかが表示される。
---------------------------------------
画像の配置
使用されている画像は次の4つ。
1.
道路(road.png)
背景画像として、view に追加する。

2-1.
信号機(redLightSmall.png、yellowLightSmall.png、greenLightSmall.png)
3つの画像をランダムに表示させている。

2-2.
信号機を上部から支えている線(stopLightCable.png)
細部も抜かりありませんね。

3.
アクセルペダル(gasPedalSmall.png)
コレをタップすると、信号が青に変わってからタップされるまでの経過時間が画面に表示される。
---------------------------------------
Alert メッセージの表示
次の状態の時、Alert メッセージを表示する。
1.
アプリを起動させた直後
操作方法を説明する。

2.
信号が青に変わってからペダルをタップした時
信号が青に変わってからタップされるまでの経過時間を表示する。

3.
信号が赤、または黄色でペダルをタップした時
「青信号でタップしろ」と警告する。

2010年9月16日木曜日

Day5 Count Me In

アプリの内容:
「+」ボタンと「ー」ボタン、数字が表示されたラベルが用意されている。
1回押すごとにラベルに表示された数字がインクリメント/デクリメントする。

実装:
View にUIButton とUILabel を追加する。
UILabel に表示する数字をインクリメント/デクリメントするメソッドを実装する。

ボタンアクション:
addTarget:action:forControlEvents: で、アクションとターゲットを追加する。
addTarget:action:forControlEvents:
Adds a target and action for a particular event (or events) to an internal dispatch table.

Control Events には、様々なイベントが用意されている。
今回の「ボタンを押す」アクションは、「UIControlEventTouchUpInside」を選択する。
UIControlEventTouchUpInside
A touch-up event in the control where the finger is inside the bounds of the control.

最初、特に理由もなく「UIControlEventAllEvents」を選択していたら、なぜかアクションに指定したメソッドが2回呼ばれてしまった。レファレンスを読むと、
UIControlEventAllEvents
All events, including system events.

と、あるのでシステムイベントでも呼ばれていたのだと思う。適当に選択するのはやめろ。

UILabel に表示する数字をインクリメント/デクリメントするメソッド:

以下のように実装した。

-(void)addUnit{
NSString* numValue = [[NSString alloc] initWithFormat:@"%d", ++count];

contLabel.text = numValue;
[numValue release];
}

-(void)subtractUnit{
if(count <= 0) return;
NSString* numValue = [[NSString alloc] initWithFormat:@"%d", --count];

contLabel.text = numValue;
[numValue release];
}
チュートリアルでは、インクリメント/デクリメント演算子が後置されていたが、それではボタンを1回押しただけでは目的が達成されないので前値に直した。
インクリメントC言語、C++、Java、JavaScriptなどでは、インクリメント演算子「++」が用意されている。厳密には、前置インクリメントと後置インクリメントの2種類の演算子があり、演算子記号は同じ「++」だがオペランドの前に置くか(例: ++x)後に置くか(例: x++)で区別される。前置インクリメントは式の評価の最初にオペランドがインクリメントされ、後置インクリメントは最後にインクリメントされる。
参考サイト:UIControl Class Refarence 、インクリメント

2010年9月14日火曜日

Xcodeデバッグする時に便利なショートカット

デバッグ開始
⌘ + option + return

デバッグ終了
⌘ + Shift + return

デバッグからプロジェクトへの切り替え
⌘ + 0

//挿入
⌘ + /

ブレークポイント挿入/削除

⌘ \

2010年9月10日金曜日

Day 3 OpenURL openMaps: カスタマイズ(2)キーボードを表示/非表示にする

以下のことを実装したい。
-------------------------
1.
テキストフィールドをタップしたときにキーボードを表示し、ユーザーがキーボードの「Done」ボタンをタップしたときにキーボードを非表示にする。
2.
テキストフィールドの下にある「openMaps」ボタンをタップすると、googleMapsが開き、テキストフィールドに入力した場所の地図が表示される。
-------------------------

UITextFieldDelegateプロトコルには、ユーザが「Return」ボタンをタップしたときにテキストフィールドを呼び出す(ボタンに表示されるテキストがどのようなものであっても)、textFieldShouldReturn:メソッドが含まれています。

textFieldShouldReturn:
Asks the delegate if the text field should process the pressing of the return button.

View Controllerはテキストフィールドのデリゲートとして設定したため、このメソッドを実装して、resignFirstResponderメッセージを送信する(キーボードを閉じる効果を持つ)ことによって、テキストフィールドからファーストレスポンダステータスを強制的になくすことができます。

resignFirstResponder
Notifies the receiver that it has been asked to relinquish its status as first responder in its window.

これを元に実装したソースは以下の通り。
-(BOOL)textFieldShouldReturn:(UITextField *)tf{

[tf resignFirstResponder];

return YES;
}

テキストフィールドに入力された文字を openMaps: メソッドに渡し、openURL でgoogleMapsを実行する処理は以下のように実装した。

-(void)openMaps:(id)sender {

self.addString = txtField.text;
NSString* addressText =self.addString;

addressText = [addressText stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString* urlText = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@",addressText];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlText]];

[addString release];

}
シミュレーターには日本語キーボードがないようなので、日本語入力を試すことが出来なかった。

参考PDF:
iPhone アプリケーション チュートリアル

Day 3 OpenURL openMaps: カスタマイズ(1)住所を直接入力する

ボタンをタップして決めうちの住所をgoogleMapsで表示するのではなく、テキストフィールドを実装してキーボードから住所を入力できるようにカスタマイズする。

UIKitフレームワークには、テキストコンテンツを表示するために、次の3つの主要なクラスがあります。
■ UILabel:静的なテキスト文字列を表示する
■ UITextField:編集可能な単一行のテキストを表示する
■ UITextView:編集可能な複数行のテキストを表示する

住所は1行程度の入力になると想定して、UITextFieldクラスを利用する。

編集可能なText Viewで作業をする場合は、常に、編集セッションを管理するデリゲートオブジェクトを提供する必要があります。
View Controllerは、それ自体がこのテキストフィールドのデリゲートになるため、UITextFieldDelegateプロトコルを採用しなければなりません。クラスがプロトコルを採用することを指定するには、インターフェイスで、そのクラスの継承元のクラスの名前の後に、角括弧(<>)で囲んでプロトコル名を追加します。

TextField を実装するソースコードは以下のようにした。

インターフェイスで、デリゲートを設定する。
RootViewController.h
#import <UIKit/UIKit.h>

@interface RootViewController : UIViewController <UITextFieldDelegate> {
UIView *view;
UITextField *txtField;
}
@end

TextField の実装は、loadView メソッドで行う。
デフォルトの画面 UIView に、テキストフィールドのビュー UITxetField を追加する。

RootViewController.m

-(void)loadView{

view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
view.backgroundColor = [UIColor grayColor];
self.view = view;

txtField = [[UITextField alloc] initWithFrame:CGRectMake(63, 56, 200, 30)];
txtField.borderStyle = 3;
txtField.placeholder = @"Enter Adress";
[self.view addSubview: txtField];
txtField.delegate = self;
txtField.returnKeyType = UIReturnKeyDone;

[txtField release];
[view release];

}

参考PDF:iPhone アプリケーションプログラミングガイド
iPhone アプリケーション チュートリアル

2010年9月7日火曜日

Day 3 OpenURL openMaps: メソッド詳細(2)

元の実装では、決め打ちで住所を指定していた(米国appleの住所、アルファベットと数字)。コレを日本語で指定したい(日本apple本社の住所)場合はstringWithCString:encoding: メソッドを使用する。
stringWithCString:encoding:
Returns a string containing the bytes in a given C array, interpreted according to a given encoding.

C言語の文字列からNSStringを返すメソッド。文字列は「" "」で囲む。エンコーディングの引数は「NSUTF8StringEncoding」を指定する。
日本語住所を指定する場合のソースは以下のようになる。
-(void)openMaps:(id)sender{
NSString* addressText =[NSString stringWithCString:"東京都新宿区西新宿3-20-2" encoding:NSUTF8StringEncoding];

addressText = [addressText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

NSString* urlText = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@",addressText];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlText]];
}

Day 3 OpenURL openMaps: メソッド詳細(1)

openMaps ボダンをクリックすると実行される処理の内容を説明する。

ソースコードは以下の通り。

-(void)openMaps:(id)sender{
NSString* addressText = @"1 Infinite Loop, Cupertino, CA 95014";

addressText = [addressText stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

NSString* urlText = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@",addressText];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlText]];
}

4行目
決め打ちの住所(米国appleの住所)をstringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding メソッドでエンコードする。
6行目
stringWithFormat メソッドで、文字列(googleMap検索のURL)を作る。
7行目
URLWithString メソッドで、NSURLを作成する。そのNSURL を利用してgoogleMapでその住所を表示する。

6行目補足
作成する文字列は、googleMapで検索した結果を表示するURL

7行目補足
sharedApplicationメソッドでインスタンスを取得して、openURL メソッドでアプリケーション(googleMaps)にアクセスする。

2010年9月6日月曜日

Day 3 OpenURL ローカル環境で実行してみる

アプリを実行してみると以下の5つのボタンが並んでいる。
 Open phone URL
 Open SMS URL
 Open E-mail URL
 Open Map URL
 Open Browser URL

ソースコードを見ると、ボタンをタップして実行されるコードを書いているのは「Open Map URL」だけなのでそこだけ実装してみる。
ここで使われているのはカスタムURLと言われるもの。iPhoneAppProgramingGuide.pdf より引用。
アプリケーションが既知の型のURLを扱う場合は、そのURLスキームを使用してそのアプリケーションとやり取りができます。
使うメソッドは、openURL:メソッド。
詳細は以降。

参考PDF:iPhone アプリケーションプログラミングガイド 他のアプリケーションとのやり取り

2010年9月3日金曜日

定義へジャンプ

定義へジャンプするショートカット

⌘ + ダブルクリック

*.hファイルと*.mファイルの切り替え

*.hファイルと*.mファイルの切り替えを行うショートカット

⌘ + option +↑

2010年9月1日水曜日

Day 2 画像をアニメーション表示させる

UIImageViewクラスを利用して画像をアニメーション表示させる。
viewDidLoadに以下で実装した処理を追加する。
実装手順は以下の通り。

1.
animationImagesプロパティに、アニメーションで使われる画像を格納した配列を指定する。最後に nil を入れるのを忘れない。

2.
アニメーションの設定を下記のプロパティで指定する。
animationDurationプロパティで、アニメーションの時間を指定する。
animationRepeatCountプロパティで、アニメーションを繰り返す回数を指定する。

3.
startAnimatingメソッドで、アニメーションを開始する。

4.
現在のviewにUIImageViewを追加する。

Navigation Controller

Navigation Controllerは、階層的に構成されたデータを表示するために使用するコンテナView Controllerです。
このクラスのメソッドは、カスタムView Controllerのコレクションをスタックベースで管理する機能をサポートしています。このスタックは、階層的なデータの中をユーザが通った経路を反映します。スタックの一番下は出発点を表し、スタックの一番上はデータ内のユーザの現在位置を表します。
Navigation ControllerはNavigation Barを1つ管理します。Navigation Barには、データ階層内のユーザの現在位置についての情報、前の画面に戻るためのボタン、および現在のView Controllerで必要なカスタムコントロールが表示されます。また、現在の画面に関連するコマンドを表示するために使うオプションツールバーの管理も行います。

Day 2 modalビューを表示させる

modalビューを表示させるために、mainviewの右下隅にあるinfoボタンを実装する。
参考にしたのは参照PDFの「モーダルモードでのNavigationControllerの表示」。

-(void)showInfo:(id)sender{

RootViewController *rootView = [[RootViewController alloc] init];
FlipsideViewController *modalview = [[FlipsideViewController alloc] init];
modalview.delegate = self;
modalview.view.backgroundColor = [UIColor greenColor];

UINavigationController *naviModalController = [[UINavigationController alloc] initWithRootViewController:rootView];

modalview.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[naviModalController pushViewController:modalview animated:NO];
[self presentModalViewController:naviModalController animated:YES];

[rootView release];
[modalview release];
[naviModalController release];

}

5行目
delegateプロパティを設定して、modalを閉じるメソッドをデリゲーションするときに使用する。

参考PDF:iPhone OS View Controller プログラミングガイド

イメージファイルの追加

xcodeのプロジェクトにイメージファイルを追加する方法。

1.
(プロジェクト名)フォルダ - Resourcesフォルダ下にimagesフォルダを追加する。
2.
imagesフォルダに既存ファイルとして、イメージファイルを追加する。

2010年8月31日火曜日

モーダルView Controllerを閉じる

そのモーダルView Controllerを表示したのと同じView Controllerが閉じるべきなのです。親のView Controllerに、モーダルモードで表示した子を閉じるように通知するにはいくつかの方法がありますが、よく使われる方法はデリゲーションです。

参考PDF:iPhone OS View Controller プログラミングガイド

Day 2 View-basedAppricationテンプレートを使用する

appsamuck.com の実装は古い環境で構築されているため、iphoneSDK3.xで実装してみる。
その準備は以下の通り。

使用テンプレート:
View-BasedApprication
くるっと回転するビュー(modalView)の実装はWindow-basedAppricationテンプレートなるものが用意されているが、IBを使用しないで実装していくのでこちらを元にしていく。
IBを使用しないための準備は、ラベル:noIBを参照する。

デリゲーション:
さらにmodalViewは、デリゲーションを使用するようにと書かれているので 、Window-basedAppricationテンプレートを参考にデリゲートを実装する。

参考PDF:iPhone OS View Controller プログラミングガイド

viewDidLoad メソッド

viewDidLoad

Managing the View
-viewDidLoad
Called after the controller’s view is loaded into memory.
------------------
ビューはメモリー上に展開されただけで、表示はされていない。なので、ここでmodalviewは動かない。

参考URL:iOS Reference Library UIViewController Class Reference

IBを使わない場合のviewとviewControllerの作成

View-basedApricationテンプレートを使用する場合、viewとviewControllerを自作する必要がある。

1.
(プロジェクト名)AppDelegate.mファイルの、application:didFinishLaunchingWithOptionsメソッドで、UIWindowとUIViewController(を継承したRootViewController)を作成してUIWindowに追加する。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

RootViewController *viewController = [[RootViewController alloc] init];
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[viewController release];

[window addSubview:navigationController.view];
[window makeKeyAndVisible];

return YES;
}

2.
UIViewCOntrollerクラスのloadViewをオーバーライドして、self.viewにviewを追加する。

-(void)loadView{
CGRect bounds = [[UIScreen mainScreen] applicationFrame]; 
UIView *contentView = [[UIView alloc] initWithFrame:bounds];

contentView.backgroundColor = [UIColor redColor];
self.view = contentView;

[contentView release];
}

参考PDF:iPhone OS View Controller プログラミングガイド プログラムによるビューの作成

ViewControllerのロードサイクル(2)

ViewControllerのロードサイクル(1)の続き

loadView

viewDidLoad

viewWillAppear

viewDidAppear

------------------------------












2010年8月26日木曜日

モーダルビューコントローラ

モーダルView Controllerは、新規に1画面分のコンテンツを表示するときに自由に使えるツールです。
モーダルView Controllerは、UITabBarControllerやUINavigationControllerのように、
UIViewControllerの特定のサブクラスではありません。
アプリケーションでモーダルView Controllerを使用する理由としては、... 何らかの情報を収集したり表示したりするために、アプリケーションのワークフローに一時的に割り込みを行うことを意味しています。
参考PDF:
iPhone OS View Controller プログラミングガイド
iPhone アプリケーションプログラミングガイド

2010年8月25日水曜日

ViewControllerのロードサイクル(1)

loadView

viewDidLoad

viewWillAppear

viewDidAppear

------------------------------

UIWindowの作成

ウインドウの最初のフレームサイズは、必ず画面全体を覆うように設定するべきです。
プログラミングによってウインドウを作成する場合は、作成時に望みのフレーム矩形を明示的に渡渡さなければなりません。画面の矩形と異なる矩形は渡さないでください。画面の矩形は、UIScreenオブジェクトから次のようにして取得できます。
UIWindow* aWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]bounds]] autorelease];
参考PDF:
iPhone OS View Controller プログラミングガイド
iPhone アプリケーションプログラミングガイド

UIViewとUIWindowとUIViewController

UIView
画面上の矩形領域を定義します。
その矩形領域内にコンテンツを表示する責務(コンテンツ表示)と、その領域内で発生したタッチイベントに応答する責務(イベント処理)を負っています。
1つ以上のサブビューを管理できます。
UIWindow
iPhoneアプリケーションは、通常、UIWindowクラスのインスタンスで表わされるウインドウを1つだけ持っています。
1つ以上のビューをそこに追加して表示します。その後は、このウインドウオブジェクトを再び参照することはほとんどありません。
UIWindowの親クラスはUIViewなので、通常はUIWindowオブジェクトのビュー関連プロパティを直接操作することはありません。

UIViewController
1つの画面のビューは、1つのView Controllerオブジェクトによって支えられています。
(View Controllerの仕事は、)ビューに表示するデータを管理することと、更新をアプリケーションのほかの部分と調整することです。
自身が管理する一連のビューを作成したり、メモリ不足状態のときにビューをメモリから削除する責務を負っています。

参考PDF:
iPhone OS View Controller プログラミングガイド
iPhone アプリケーションプログラミングガイド

2010年8月17日火曜日

InterfaceBuilder(IB)を使わないための準備

Xcodeで、IBを使わないためには以下の処理を事前に行う。
------------------------------

Window-based Applicationテンプレートを選択する。

*.xibファイルを削除する(一緒にゴミ箱に入れる)。

main.mの14行目を以下のように変更する。
int retVal = UIApplicationMain(argc, argv, nil, nil);

最後の引数を「nil」から「AppDelegateクラス名」に変更する。
int retVal = UIApplicationMain(argc, argv, nil, @"bonefire_3_xAppDelegate");

bonefire_3_x-Info.plistファイルのMain nib file base nameを削除する。

2010年8月5日木曜日

Day 2 Utility Applicationテンプレートのソースコード解析

新規プロジェクトを作成して、そのままビルドと実行を行うと、「i」ボタンが用意されている。押すとくるっと画面が回り「Done」ボタンと、ナビゲーションバーが表示される。「Done」ボタンを押すと元の画面に戻る。
IBを起動してみる。どうやら「i」ボタンはButtonアクションの設定を行っている訳ではないらしい。くるっとする動きは、showInfoで実装しているっぽい。
@protocolやら、objective-cを理解していないとむずかしい。。。

以下、理解したことのメモ。
------------------------------------
MainViewController.m
MainViewController.m→@protocol FlipsideViewControllerDelegateの実装
23-26
flipsideViewControllerDidFinishメソッド
25
dismissModalViewControllerAnimatedメソッドを実行する。
そのView Controllerと、その上にあるすべてのView Controllerが閉じられる。通常これは、その下にあるNavigation Controllerに戻るために行う。

29-38
showInfoメソッド
MainView.xibで、Viewウィンドウの右下にある「i」ボタンに紐づいている処理。
32
controller.delegate = self;
「i」ボタンが押されると、delegate(移譲先)先としてself指定する。

34
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
コントローラのアニメーションの種類を、水平方向にパネルが回転するようなアニメーションで指定する。

35
[self presentModalViewController:controller animated:YES];
指定したcontrollerを一番上に表示する。
------------------------------------
FlipsideViewController.h

23-25
@protocol FlipsideViewControllerDelegate;
protocolの宣言
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;の定義を行う。実装は、MainViewController.m23-26

2010年8月4日水曜日

Day 2 Campfire ローカル環境で実行してみる

Day 1 のように解説がある訳ではないらしい。とりあえず、ソースをダンロードして解析してみる。
いつものように、シミュレータのバージョンを変更する。warningが1つ出るが、実行は出来た。
キャンプファイヤーがめらめらしている画像が表示される。右下の「i」ボタンを押すと、くるっと画面が回転する。今度は右上の「Done」ボタンを押すとキャンプファイヤーの画面に戻る。

こういった動作をするアプリをUtilityアプリケーションと言うらしく、テンプレートも用意されている。と、言う訳でDay 2 はUtilityアプリケーションの作成らしい。
しかし、xcode3.xになって、Utility Applicationテンプレートの内容が変わった。
なので、新しいテンプレートに合わせてDay2を作り直してみる。
次回から、新しいUtility Applicationテンプレートのソースコードを解析してみる。

2010年8月3日火曜日

Day 1 解説を読んでつまずいた箇所まとめ

つまずいたその1
InterFace Builderの使い方が解らなくて説明についていけなかったのは、以下の3箇所。内容の概略を以下にメモする。

We need to reference the label in our code so we can update the label
Interfacce Builder(IB)を使って、labelを追加する所で、Xcodeのバージョンアップにより説明のようには"Class Outlets"がなかった。

xcode3では、ライブラリウィンドウに移っていた。




















--------------------------
Now we need to update our class file
Outletsを追加したので、クラスファイルを更新する。既にファイルを更新していた場合、上書きされてしまうのでFileMergeでMergeする。
--------------------------
Now we need to wire up the Label in Interface Builder to the UILabel in the class file
Interface Builderを使って、countdownLabelとFile's Ownerを接続する。

つまづいたその2
File'sOwnerとは、そもそもなんなのか?調べた。














Name→File's Owner
Type→MinutesToMidnightViewController
自動生成されたMinutesToMidnightViewControllerクラス。
MinutesToMidnightViewController.xibの持ち主がMinutesToMidnightViewControllerクラスである。

Name→View
Type→UIView
MinutesToMidnightViewControllerクラスのインスタンス変数View


つまづいたその3
各処理について簡単にしか説明がなかったので、より詳しく、どんな処理をしているのか調べた。

MinutesToMidnightAppDelegate.m
インスタンスメソッド
applicationDidFinishLaunching
処理内容は3つ
NSTimerクラスのscheduledTimerWithTimeIntervalで、1秒ごとに処理を行う。今回は、現在時刻の表示。
UIWindowクラスのaddSubviewで、ウィンドウの中にviewを渡す。
UIWindowクラスのmakeKeyAndVisibleで、キーウィンドウを作成する。

インスタンスメソッド
applicationWillTerminate
アプリが終了するときに呼ばれる



2010年7月27日火曜日

Day 1 エラーの原因と正しい処理の内容

updateLabelクラスを、beforeからafterに書き換えたところ、エラーがなくなりシミュレータが動いた。

before
-(void)updateLabel {
NSDate* now = [NSDate date];
int hour = 23 - [[now dateWithCalendarFormat:nil timeZone:nil] hourOfDay];
int min = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] minuteOfHour];
int sec = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] secondOfMinute];
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min,sec];
}

updateLabelクラスでは、ラベルに表示する時刻を生成している。
beforeでエラーが出るのは次の3行。
1: int hour = 23 - [[now dateWithCalendarFormat:nil timeZone:nil] hourOfDay];
2: int min = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] minuteOfHour];
3: int sec = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] secondOfMinute];

after
-(void)updateLabel {
NSDate* now = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
NSInteger hour = [dateComponents hour];
NSInteger minute = [dateComponents minute];
NSInteger second = [dateComponents second];
[gregorian release];
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second];
}

正しく動作する、updateLabelクラスの主な処理は以下の通り。
---------------------------
NSDate* now = [NSDate date];
NSDateクラスブジェクトを生成する
dateメソッドで、現在の日時(経過時間)を取得する

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSCalendarクラスオブジェクトの生成と初期化を行う
initWithCalendarIdentifierメソッドは、NSString型の引数をとる。
引数は、NSLocaleクラスのNSLocale Calendar Keysで定義されている。
NSGregorianCalendarは、グレゴリオ暦を指定している。

NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
NSDateComponentsクラスオブジェクトの生成を行う。
NSDateComponentsクラスは、カレンダーの年、月、日、時、分、秒、曜日を保持する。
オブジェクトの取得は、NSCalendarクラスのcomponentsメソッドを使用する。
componentsメソッドの引数は2つ。ひとつめは、Calendar Units。取得したいデータ(今回だと、時、分、秒)のこと。
ふたつめは、現在の日時(取得済み)。

形成されたNSDateComponentsから時、分、秒を取り出す。

Calendar Unitsで取得したいデータが複数ある場合は、論理和で指定する。

2010年7月24日土曜日

Xcodeで矩形選択

テキスト中で矩形選択をしたい時のショートカット。

option - (マウスで)ドラッグ

appsamuck.com / Day 1 Minutes to Midnight ローカル環境で実行してみる

appsamuck.com というサイトで、iPhoneアプリのチュートリアルが紹介されていた(Zipソース付き)。
実際にひとつずつ挑戦してみたいと思う。まずは、Day 1から。これは、時計アプリのようだ。説明にそってローカル環境で手を動かして作ってみた。
…………。



『アウトレット』のあたりでつまずいた。
iPhoneやCocoaに関する知識はもちろん、Objective-Cも初心者。なので、、、
ZipをダウンロードしてXcodeで『ビルドと実行』してみた。
…………。
エラー。。。。
以下の解決法2つを実行後、無事にローカル環境のエミュレータ上でアプリが動いた。

Zipソースを解凍して、Xcodeで開く。『ビルドと実行』をクリック。
→ 結果:There is no SDK with the name or path 'iphoneos2.0' とエラーが出る。


解決方法1:


プロジェクト>プロジェクト設定を編集>「一般」タブで、「全ての構成のベース SDK:」のプルダウンメニューから
『iPhone デバイス 4.0』を選択する。その後、Xcodeを再起動する。

もう一度、『ビルドと実行』をクリック。
→ 結果:MinutesToMidnightViewController.mの44.45.46行目に、 error: invalid operands to binary - (have 'int' and 'id') とエラーが出る。

解決方法2:
Objective-Cが分からないので、googleで検索して英語サイトに載っていた解決方法をそのままコピペ。

以上の方法で、Day 1 は動いた。次回、エラーが出た部分のソース解析をしたいと思う。