2011年2月10日木曜日

iPhone CoreLocation から GPS情報を最適に利用し、Annotationする方法



CoreLocationManagerは、最初にGPSを取得する際、

「精度よりも、スピード」

を重視するようで、最初から精度を重視する人は(みんなそうだと思うけど)

下記のことをやったほうが良さそう

• 一つ前に取得したGPS情報のタイムスタンプが古い場合
→ newLocation.timestamp

• 精度の悪い情報の時
→ newlocation.horizotalAccuracyが指定した範囲より大きい


<手順>

•位置情報サービスを開始する
[locationManager startUpdatingLocation];

場合によってはこの前に、どのくらい移動したら、
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {

このメソッドを呼ぶかを指定する
locationManager.distanceFilter = "移動距離(m)";

取得し、上記メソッドが呼ばれた際に、
if (-[newLocation.timestamp timeIntervalSinceNow] > 5.0) return;

で前回取得した位置情報のタイムスタンプが古かったら処理をしない


指定した範囲を超えていたら処理をしない
if (newLocation.horizontalAccuracy > "指定した距離") return;




またAnnotationを使用しつつ、持続して、位置情報を取得し続けなければならないアプリの場合は、位置情報が移動したときに、その位置をもとにAnnotationする必要があるかと思います。


その際、問題なのがAnnotationを更新するタイミング。


今回は、下記のことを行った。

• 位置情報取得して、一つまえの緯度経度を覚えておき、その距離が指定した範囲外になったときに、アノテーションし直す。


if(newLocation.speed <= 2) {
moveMeters = 15.0f;
}
else if(newLocation.speed <= 5) {
}


大体、徒歩で、スピードは2前後、電車で、かっ飛ばしても20〜30くらいの数値
上の設定は、徒歩くらいのスピードで移動していたら、15m動いたら処理、

電車の場合は、1km動いたら処理といういうように設定した。

oldLocationとnewLocationの移動距離の計算は独自でやってみた。
下記ソース

- (id)initWithDistance:(CLLocationCoordinate2D)current {
currentLocation = current;
CGFloat lonCircle = CircleEarth;
oneLatMeter = lonCircle / 360;
CGFloat latCircle = cos(currentLocation.latitude / 180 * M_PI) * CircleEarth;
oneLonMeter = latCircle / 360;

return self;
}

- (void)setMoveLocation:(CLLocationCoordinate2D)move {
moveLocation = move; 
}

- (CGFloat)getLatitude:(CGFloat)latitude {

return oneLatMeter;
}

- (CGFloat)getLongitude:(CGFloat)latiude location:(CGFloat)longitude {
return oneLonMeter;
}

- (CGFloat)getLatMeter:(NSInteger)meter {
CGFloat latTime = meter / oneLatMeter;
return latTime;
}

- (CGFloat)getLonMeter:(NSInteger)meter {
CGFloat lonTime = meter / oneLonMeter;
return lonTime;
}


- (BOOL) checkMoveDistance:(CGFloat)checkMeters withDebug:(NSInteger)flag {
CGFloat latitude = (currentLocation.latitude - moveLocation.latitude);
CGFloat longitude = (currentLocation.longitude - moveLocation.longitude);
if(latitude < 0) {
latitude = latitude * (-1);
}
if(longitude < 0) {
longitude = longitude * (-1);
}
double distance = sqrt(pow(latitude * oneLatMeter,2) + pow(longitude * oneLonMeter, 2));
if(distance <= checkMeters) {
if(flag == 1) {
NSLog(@"メートル %fm, %fm => %fm ",latitude * oneLatMeter, longitude * oneLonMeter, distance);
}
return TRUE;
}
return FALSE;
}


例えば、coordinateと、startLocationの距離が、100メートル移動したかどうか調べる時

ConvertUnit *convertUnit = [[[ConvertUnit alloc]initWithDistance:coordinate]autorelease];


[convertUnit setMoveLocation:startLocation];



if([convertUnit checkMoveDistance:100.0f withDebug:0] == FALSE) {
  // 100.0fm以上移動しました
}
else {
   // 100.0fm以内の移動でした
}

この中で、もし指定した範囲以上、移動していたら、新しくAnnotationし直すなどの
処理をする

Annotationをし直す際は、一つ前にAnnotationしたデータをmapViewから削除しないと
Annotationの量がすごいことになるので、注意。



ほかにいい方法あったら誰か教えてください。





xcode iPhone 共同開発プロジェクト 実機でコンパイル時によく起きる シンボルのリンクエラーの対処法



iPhoneプロジェクトをsubversion管理などを行って共同開発をする際にsubversion管理から


外すべきファイルは次のファイルだけど、これを管理から外してもどうしても


たまーにおこってしまうエラーがあったので、その対処法をメモ

<管理下から外すべきファイル>
*.pbxuser
*.perspectivev3
build/




もう一点注意すべきところは、プロジェクトフォルダ以下の


~.xdoceproj/project.pbxproj」 ファイルが、コンフリクトを起こさないようにすること。


このファイルは、プロジェクトで使用する、画像や、ライブラリなどをプロジェクトに追加した際に、


ファイル構成やどこにどのファイルがあるかを記録するファイルで、


おそらく、このファイルをもとにxcodeプロジェクトを開始し、コンパイル時に参照していると思われる。




実機でコンパイルする際、リポジトリから落としてきたプロジェクトは、コピーしたプロジェクトは、設定にもよるけど




このproject.bpxprojファイルがコピーもとで最後にコンパイルした時に指定していた場所から




ライブラリやframeworkを参照しようとするので、その設定と違う環境でコンパイルしよーとすると




参照できなくなりシンボルのリンクエラーになってしまうらしい。


※例えば、コピー元で最後にシュミレーターでコンパイルしたらあとに、コピー先で実機でやろうとすると、シンボルリンクエラーになる。(一概にそうとも言えないカモ。。。)実機でコンパイルしたいのに、参照先がシュミレーターになっているなど


その場合のエラーは、大体、すごい量になる。プロジェクトに使用しているファイルが多ければ多いほど多い。僕の場合は、「424カ所」だった、それと同時にワーニングで、指定しているパスが参照できません的なワーニングがでたので気がついた。




まーとにかく、細かいことは気にしないで、大量のエラーが急に出てきたら下記を試してみるといいです。





下記のファイルをエディタなどで開く

プロジェクトファイル/*.git.xcodeproj/project.pbxproj


• 各frameworkやライブラリの参照先を確認して変更
vi などで開いているときは、このコマンドで一発置換
%s/Simulator//g

• 反映
xcodeに戻ろうとするとディスクから読み取るかどうか聞かれるので、「ディスクから読み取り」を選択する


もしくは、逆。シュミレーターでやろうとしているのに、実機側から参照しようとしている場合は、Simulatorを追加してあげる。


これで、コンパイルすれば、正常にできるはず!





2011年2月8日火曜日

iPhone アプリ ネットワーク接続を検知する方法



iPhone アプリ上で、ネットワーク接続可能かどうかを検知する方法です。


1、Appleのデベロッパーサイトにある「Reachability」というライブラリをダウンロード


2、ダウンロードしたら、中にある下記のファイルを自分のプロジェクトへ追加する
• Reachability.m
• Reachability.h


3、Reachabilityの中で使用しているであろう、「SystemConfiguration.framework」を既存のFrameworkから追加だけする


4、検知したい所での.hファイル編集


#import "Reachability.h"
@class Reachability

これだけ追加


5、.mファイルの編集

// 自動検知するためにNotificationに登録する

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];

// 指定したホストに接続確認する場合
Reachability *hostReach = [[Reachability reachabilityWithHostName: @"www.yahoo.co.jp"] retain];
    [hostReach startNotifier];
[self updateInterfaceWithReachability:hostReach];

// インターネットに接続できるか確認
    Reachability *internetReach = [[Reachability reachabilityForInternetConnection] retain];
    [internetReach startNotifier];
    [self updateInterfaceWithReachability: internetReach];

// Wifi接続かどうか確認
    Reachability *wifiReach = [[Reachability reachabilityForLocalWiFi] retain];
    [wifiReach startNotifier];
    [self updateInterfaceWithReachability: wifiReach];

単にネットワークに接続できるかどうかの確認であれば、

「reachabilityForInternetConnection」を使えばいいと思う。


検知した際に呼ばれるメソッドを追加
- (void) reachabilityChanged: (NSNotification* )note
{
    Reachability* curReach = [note object];
    NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
[self updateInterfaceWithReachability:curReach];

}


接続可能かどうかで処理を分ける

- (void) updateInterfaceWithReachability: (Reachability*) curReach
{
NetworkStatus netStatus = [curReach currentReachabilityStatus];
if(netStatus == NotReachable) {
if(imageView == nil || self.tabBarController.selectedIndex == 0 ) {
UIImage *image = [UIImage imageNamed:@"no_denpa_bg.jpg"];
imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0,20, image.size.width,image.size.height);
[self.window addSubview:imageView];
}
}
else {
[imageView removeFromSuperview];
[self.window addSubview:tabBarController.view];
}
}

以上




2011年2月3日木曜日

iPhone 実機でテストする際、インストール中にエラーが出た場合









Couldn't register xxxx.xxxx with the bootstrap server. Error: unknown error code.
This generally means that another instance of this process was already running or is hung in the debugger.プログラムはシグナルを受信しました:“SIGABRT”。



っていうエラーの場合の対処は、、、、、




iPhoneを再起動!!




これだけ。

iPhone アプリ AdHoc用 ビルド方法



AdHoc(100台までテスト用として、作成中のアプリをitunesからインストールできる仕組み)用にビルドする際の手順


1. IDCで、AdHoc用のprovisioningを作成する

- インストールしたいiPhoneのDevice コードをitunesもしくは、Xcodeにて確認し、デバイス登録をする


- 左メニューのProvisioningタブで、Distributionを選択し、AdHocにチェックを入れて任意のAppIDと、先ほど登録したDevice コードにチェックをしてProvisioningを作成する。


- 数秒経つと、ダウンロードできるようになるので、ダウンロードして、ダブルクリックすると、Xcodeに読み込まれる。


2. 配布するアプリの~Info.Plistの確認

- AdHoc用ではなくても、確認が必要だけど、上記で作成したProvisioningで使用したAppIDが、AppID登録した際のドメイン(*.xxx.co.jpなど)と、アプリのプロジェクト〜Info.Plistに含まれる「Bundle identifier」が一致しているかどうか確認すr

- これがずれていると、ビルドできない


3. AdHoc用ビルドターゲット設定

- Xcodeでプロジェクトを開き、アップルメニュー「プロジェクト」から「プロジェクト設定を編集」を選択。「構成」タブから”Release”を選択して、下の「複製」ボタンよりコピー。Releaseコピーができるので、それを任意の名前でもいいけど、「AdHock」とかにする

4. プロジェクトにEntitlementsの追加
- プロジェクトのグループとファイルでどこでもいいけど、右クリックして、「追加」、「新規ファイル」を選択

- iPhone OSグループから「Code Siging」を選択して、「emtitlements」を選択

- ファイル名は任意の名前で

5. AdHock用ビルドターゲット設定

- 「プロジェクト」から「プロジェクト設定を編集」の「ビルドタブ」で、”Code Signing Entitlements”(コード署名権限)に上記で任意の名前で作成したファイルを指定する

- 左上の「構成」を先ほど作成した「AdHock」に設定


6. いざビルド

- 通常はエラーは出ないと思うけど、なぜかシンボルのリンクエラーが出た。

ビルド時にFrameworkの読み先がおかしいから、各Frameworkを認識できませんとのこと。これバグ??

なのでとりあえず、無理矢理、ターミナルから「プロジェクト名.xcodeproj/project.pbxproj」ファイルをvimで開き、frameworkの参照先がシュミレーターになっているようなところを、全部実記の方から読むように変更修正。

これでなんとかビルド完了。


7. iTunesへ転送

- Xcodeの「グループとファイル」から”Product”をクリックするとビルドされたファイルが表示されるので(もしくはFinderからプロジェクト直下のBuild/からでもいい)そのファイルを右クリックからFinderで開き、iTunesへドラッグする。するとAppの一覧に登録されるので、登録されたアプリのアイコンで右クリックし、Finderで表示すると、「〜.ipa」ファイルが表示されるので、これを一番最初にデバイスコードを登録した人に渡してあげるとiTunesからテストアプリがインストールできる。


意外とドキュメントが少なくてめんどくさかったけど、なんとかできた。