「ほっ」と。キャンペーン

cappuccinoのコンピタブログ

cappmac.exblog.jp
ブログトップ

カテゴリ:開発メモ( 7 )

[メモ] segueで悩む

Storyboardを使って、cellを押すと遷移する機能を作っているところなんですが、segueを設定するとどうしてもワーニングメッセージが出てとても気持ち悪い。

内容としては、

2014-07-10 17:27:42.998 xcodetest2[8264:60b] OK

2014-07-10 17:27:43.000 xcodetest2[8264:60b] Warning: Attempt to present <NextViewController: 0x109207e60> on <ViewController: 0x109335ef0> while a presentation is in progress!


というもの。


NextViewControllerがViewControllerから見て遷移先になる部分なんだが、他のサイトをコピペするとこのメッセージがでてしまう。一応動くのだが、何か問題を孕んでいそうで気になってしまう。


解決策は結構簡単だった。

内容は完全に以下のサイトと同じ。解決策も懇切丁寧に書かれているので、リンクだけ張っておこう。

簡単にいうとGUIでsegueを定義した上にコーディングでもsegueを定義していることがまずいもよう。


http://blog.kfield.co.jp/2013_11_01_archive.html


[PR]
by cappuccino_mac | 2014-07-10 17:31 | 開発メモ | Comments(0)

[Xcode] Exception Breakpointで例外発生箇所を特定

iOSアプリ開発で良く躓くポイントとして、実行時エラーがありますよね。

なんか分からないけどアプリが落ちる、なんかレスポンスが悪い、とかいろいろ有ると思います。
前者に対してはXcodeのLLDBが、後者に対してはInstrumentsが役立つと思います。

今回は、Xcodeのデバッグで役立った機能があったので、メモとして残そうと思います。

と、その前に、前々回の投稿で、「やりたいことを実現するコード」と「のり付けするための知識」という考えに着目して記事にしていこうみたいな宣言をした割に、前回はその部分が不明瞭だった気がします。

そのため、今後、開発メモとして残す記事は、必ず上記が見えるようにしたいと思います。

  • 今回のやりたいこと「現在地周辺のお店情報を収集したい」

このために、以下のコードを参考に(つまりコピペ)しました。

https://github.com/koogawa/VenueListGPS

このソースはここここで詳細に解説されています。

今回、この記事中の「エラー処理は省略されています」をちゃんと読んでいなかったばかりにドツボにはまりました。

実行すると、必ずアプリが落ちる、というエラーが発生してしまいました。
ただし、発生するタイミングは一定しないため、ネットワーク処理部分に問題が有るのでは、と推測しました。

エラーの内容は「'NSInvalidArgumentException', reason: 'data parameter is nil'」というものです。停止するとUIApplicationMainで止まり、スタックトレースは見れる訳ですが、発生箇所を直感的に突き止めるのはかなり難航しそうな感触です。

まずは、怪しそうな箇所を抜粋してみました(VenueListTableViewController.m)。

- (void)locationManager:(CLLocationManager *)manager

didUpdateToLocation:(CLLocation *)newLocation

fromLocation:(CLLocation *)oldLocation

{

// 緯度・経度取得

CLLocationDegrees latitude = newLocation.coordinate.latitude;

CLLocationDegrees longitude = newLocation.coordinate.longitude;

// APIからベニューリストを取得

NSString *urlString = [NSString stringWithFormat:@"https://api.foursquare.com/v2/venues/search?ll=%f,%f&limit=30&client_id=ICIWPLPZATTTPYV0YBSVB4AQCF2PVXUWKHS3ZT1BURV0PS02&client_secret=T5SEMJSHYURT5UGERXLZNCUGI1QZ1JJHWBYN2XLDWK3FQUFN&v=20140629", latitude, longitude];

//NSLog(@"urlString = %@", urlString);

NSURL *url = [NSURL URLWithString:urlString];

NSString *response = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

NSData *jsonData = [response dataUsingEncoding:NSUTF32BigEndianStringEncoding];

NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];

// エラーコードをログに出力

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %d", errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

[self.tableView reloadData];

}


JSONが絡む処理で問題が起きる模様であるとGoogle先生の結果から推測できたので、

NSDictionaryのエラー処理をちゃんとしてみました。


NSError *error;


NSDictionary *jsonDic = [NSJSONSerialization

JSONObjectWithData:jsonData

options:kNilOptions

error:&error];


さらに、NSArrayの取り出しは、nilをチェックし、nilじゃないときだけ処理を実行するよう修正しました。
つまりこんなかんじ。

if (!error) {

// エラーコードをログに出力

if ([jsonDic count] == 0) {

NSLog(@"don't access it as the index is out of bounds");

return;

}else{

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %ld", (long)errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

}

   }else{

NSLog(@"Error: %@", [error localizedDescription]);

}



結果は変わらず・・・

ふと、UIApplicationMainで止まってしまう点についてGoogleで調べていたときに効果的にデバッグするための情報を知りました。(もう return UIApplicationMain で止まっても困らない! Xcodeでのデバッグ方法:Zero4Racer PRO Developer's Blog
これを使うと例外発生時の実行コードでブレークポイントを自動で打って停止してくれるというものです。

  • ブレークポイントペインでAdd Exception Breakpointを選択する。
c0064553_11345038.png
  • 対象例外をすべて「All」とする。
    c0064553_11345505.png


試しにやってみたところ、出ました。ブレークポイントがわかりました!
NSDataの処理をした直後のコードで停止していることが分かりました。
基本的なことですが、NSData型のjsonDataを受け取った直後のエラーチェックが抜けていた訳です。

そこで、JSONのパース処理をする部分はNSDataのチェックでnilでない場合に実行するようにしました。
修正後はこのような感じになりました。

if (jsonData == nil) {

NSLog(@"ERROR!");

}else{

NSDictionary *jsonDic = [NSJSONSerialization

JSONObjectWithData:jsonData

options:kNilOptions

error:&error];

if (!error) {

// エラーコードをログに出力

if ([jsonDic count] == 0) {

NSLog(@"don't access it as the index is out of bounds");

return;

}else{

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %ld", (long)errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

}

}else{

NSLog(@"Error: %@", [error localizedDescription]);

}


}



早速、NSDataのnilチェック処理を追加して、実行してみたところ、5時間経っても動き続けています。
解決しました。
実行した結果は以下のような感じです。
c0064553_11480243.png

  • 今回ののり付けのための知識「サンプルコードはエラーチェックがないものと心して使う」「実行時エラーはツールで特定する」

[PR]
by cappuccino_mac | 2014-07-01 06:43 | 開発メモ | Comments(0)

ViewDidLoad内の処理をメソッド化する

サンプルコードは単一の機能を説明する上で、簡単化したいため、ViewDidLoad内に直接描かれていることが多いです。
そのため、まとまった機能は部品化していきたいため、メソッド化してしまいましょう。

例として以下のソースコードを変更してみます。

https://www.cocoacontrols.com/controls/lmgeocoder

LMGeocoderという、ジオコーディングを簡単化するコントロールのソースコードです。

この中の、LMGeocoderDemoプロジェクトを開いてみます。

実行すると、映像に合わせて、緯度、経度、ジオコードした結果(住所、道路名)が表示されます。

以下のような感じ。
c0064553_17421606.png
ちなみに映像に対して、文字が縦のままなのはHorizontal専用のレイアウトだからです。
レイアウトの調整もそのうち手を付けていきたいですが、とりあえず今日の本題はそのコードの内容について。

ソースコードは以下のファイルで構成されています。
c0064553_18150996.png
このうち、AppDelegate.mとLMViewController.mが、このコードの主要部分となります。

[AppDelegate.m]
特にいじりません。

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

メソッドの中で初期化処理が行われております。


[LMViewController.m]

- (void)viewDidLoad

メソッドの中でビューのライフサイクルが動作しています。語弊は有りますが、main関数みたいなものです。

この中で位置情報を取得する処理は、部品化されています。


一方、ビデオを表示する処理は、ベタに書かれています。AVFoundationを使った簡潔なコードなので、このまま部品化して呼び出せるようにまとめておこうと思います。


とりあえず、メソッド化してみます。


返却値も、引数も無いので、以下の関数(videoLayer)を作って処理をコピペしました。{}内はviewDidLoad内のコードをそのまま移植しただけです。


- (void)videoLayer

{

BOOL hasCamera = ([[AVCaptureDevice devices] count] > 0);

if (hasCamera) {

AVCaptureSession *session = [[AVCaptureSession alloc] init];

session.sessionPreset = AVCaptureSessionPresetHigh;

AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];

[captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];

[captureVideoPreviewLayer setFrame:self.backgroundImageView.bounds];

[self.backgroundImageView.layer addSublayer:captureVideoPreviewLayer];

AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

NSError *error = nil;

AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

[session addInput:input];

[session startRunning];

}

else {

self.backgroundImageView.image = [UIImage imageNamed:@"background"];

}

}


幸い、スコープはメソッド内で閉じているので、変更はいっさい不要でした(ビデオを表示するだけだしね)。

これでメソッドは作成できたので、あとはviewDidLoadで呼び出せるよう、コードを追加します。
自分の中のメソッドを呼び出すだけなので、以下を追加するだけです。ドットでもかけますが、Objective-Cらしくメッセージで。

[self videoLayer];


この部品は後々、ビューの中で表示するビデオを実現するコードとして利用したいと思います。

今回は改変しても壊れないこと、および見通しを良くすることに主眼を置いたので、自分のやりきった感は少ないですが、実行結果は映像があったり、位置情報が変化したりと見栄えがするので面白いと思います。

今後も、見た目重視のアプリをいろいろいじっていこうと思います。

[PR]
by cappuccino_mac | 2014-06-29 17:06 | 開発メモ | Comments(0)

コピペで作るiOS App

iOSアプリの開発情報は巷に溢れておりますが、大抵、以下のパターンが多いと思います。

  • 言語仕様から理解して、コツコツ作っていく(座学的イメージ)
  • 簡単なアプリを拡張して、実用的なアプリに仕立てていく(実践的イメージ)
  • Tipsのまとめ(「分かる人」向けの開発情報)

どれも必要な情報があって、便利なのですが、手早く作る上で必要な技術とは何か、というのを模索しながら作っていくと、

(やりたいことを実現できるコードのコピペ) + (コピペをのり付けするための知識)

ということが大事かと思えてきました。

コピペで参考になるサイトはいろいろ有りますが、GitHubでパブリックに公開されているコードがよいと思います。
部品化が徹底されているものが多く、殆ど手直しせずに利用できます。

コピペをのり付けする知識は、いくらでもありますが、巷のコードを利用する上で特に必要となるのは、

  • IB依存コードとIB非依存コードの違いを吸収する
  • ビューコントローラとビューの違いを吸収する
  • Storyboardを理解する

だと思います。

この辺りを意識してアプリを作っていく記録を残していきます。

お断り:実践の記録なので挫折しているところも記録していくつもりです。




[PR]
by cappuccino_mac | 2014-06-28 17:00 | 開発メモ | Comments(0)

巨大画像を分割するUIImage実装コード

参考になります。

http://d.hatena.ne.jp/KishikawaKatsumi/20090429/1241020420
[PR]
by cappuccino_mac | 2010-11-02 00:13 | 開発メモ | Comments(0)

Interface Builderでxibファイルを間違える

◯◯ViewController.xibって方を開かないとUIの作成ができないので注意。
MainWindow.xibでも似た様子の編集画面が開く。


c0064553_1928147.jpg

ここの選択に気をつける



c0064553_19283840.jpg

失敗例


c0064553_1928588.jpg

成功例
[PR]
by cappuccino_mac | 2010-10-24 19:29 | 開発メモ | Comments(0)

うーん

c0064553_1848286.jpg
ユースケース図にするとこれだけ?
[PR]
by cappuccino_mac | 2010-10-24 18:48 | 開発メモ | Comments(0)