iOSのバージョンで場合分け


アプリを作っているときって、Web上にあるコードの断片を参考にしてコーディングすることが結構あるのですが、iOSのバージョンアップによって「Deprecated in iOS 6.0」なんてxcodeに警告を出されることがたまにあります。

で、この場合、iOS6.0以上と6.0未満で場合分けをしないといけないわけなんですが、実は面倒でこれまで放置していました。。。
まぁ動けばいいや、なんて思ったりしてて。
ですが、そろそろ一度調べておこうなどと殊勝にも考えまして、ようやく調べてみました。

次のように場合分けすればいいらしい。
簡単ですね。


float version = [[[UIDevice currentDevice] systemVersion] floatValue];
  if (version >= 6.0) {
    // iOS6.0以上の処理
  }else{
    // iOS6.0未満
  }

私の場合、最近アプリ開発を始めたということもあって、対象は5.1以降でいいやって思ってるため多分この方法でいいのですが、もっと細かくバージョンで場合分けしたい場合には、この方法だとダメらしいです。
上記の方法ではfloatにバージョン番号を入れちゃってるので、例えば4.3.5とか5.1.1とか小数点(ドット)が2つ以上入っているバージョンが判別できません。

で、どうすればよいのかと言うと↓のサイトで解説されています。
NSStringで比較せよとのことです。
OSのバージョン番号を比較する方法 – 強火で進め
ちょっと面倒ですが、アプリ起動時に一度調べちゃえばいいだけなので、大した手間でもないか。

iPad 5.1 simulator で UIImagePickerController が正常に動かない


UIImagePickerControllerを使って写真を選択する処理を実装しようとしています。
先人の知恵を頼りにしながら以下のようなコードになりました。
しかしこのコード、iphoneシミュレータやiPad実機などでは正常に動いたのですが、iPad 5.1 simulatorでだけは動作が不安定なんです。


- (IBAction)pushButtonSelectPicture:(id)sender {
	if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]){
		UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
		ipc.delegate = self;
		
		ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
		if(imagePopController != nil){
			[imagePopController dismissPopoverAnimated:NO];
			imagePopController = nil;
		}
		if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
			//iPad
			imagePopController = [[UIPopoverController alloc] initWithContentViewController:ipc];
			[imagePopController presentPopoverFromBarButtonItem:sender
									   permittedArrowDirections:UIPopoverArrowDirectionAny
													   animated:YES];
		}else {
			//iPad以外
			[self presentViewController:ipc animated:YES
							 completion:^{}];
		}
		ipc = nil;
	}
}

-(void)imagePickerController:(UIImagePickerController*)picker
	   didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo{
	if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
		// ipad
		[imagePopController dismissPopoverAnimated:YES];
	}else{
		// iphone
		[self dismissViewControllerAnimated:YES completion:nil];
	}
	[self setImage:image];
	image = nil;
}

popoverまでは正常に動いて写真を選択しても imagePickerContoller:didFinishPickingImage:edittigInfo が呼ばれなかったり、そもそもpopoverが出なかったり。。。
breakpointを設定してステップ実行していると、以下のエラーが出たこともありました。

Named service 'com.apple.PersistentURLTranslator.Gatekeeper' not found. assetsd is down or misconfigured. Things will not work the way you expect them to.

このエラーについて調べてみると、こんなのが↓
ios – Log message referring to Gatekeeper appears occasionally when running simulator – Stack Overflow
objective c – iPad simulator does not work with UIImagePickerController in an iPhone app – Stack Overflow

ひとりは
iPad simulator v5.0
iPad simulator v5.1
でだけこの問題が発生したと言ってます。
私もiPad 5.1 simulatorでは問題があるけど、実機やiPad 6.0 simulatorでは正常に動いています。
simulatorのバグなのかなぁ。

上記リンク先で一応の回避策のようなものが書かれています。
違うデバイスでテストするときは、一旦シミュレータを終了してからリスタートするといいみたい。
確かに私の環境でもこれで一応は動きました。

viewのframeサイズはできるだけ小さく


Mac Book Airを手に入れてから約2ヶ月半、なんとかアプリ第一弾をリリースすることができました。
足し算と引き算の算数アプリなんで特に難しいことはしていないように見えますが、全てが初めての経験なので本当にあちこちにつまずきました。

interface builderを横画面にする方法が分からなかったり、UINavigationViewの使い方なんてまったく意味が分かりませんでしたし、コピペしたコードもなぜか動かなかったり。ノウハウが全然ないからコピペして動かないと何がまずいのか全然わからない。
音の出し方もアニメーションのやり方も苦労しました。
そして最後の難関がアプリの申請作業。
申請作業ってちょっと大変過ぎでしょコレ。

そんなこんなでは苦労しましたが、その苦労のおかげでなんとかアプリの作り方のイメージができるようになり、やりたいことを実現するにはどうすればいいのか、なんとなく分かるようになってきました。

そして現在アプリ第二弾を作成中です。

作成中なのですが、今日までかなりハマっていた問題がありました。
メモリ不足でアプリが落ちてしまう問題です。

続きを読む

viewForZoomingInScrollViewの中で zoomScale を参照すると危険


UIScrollViewをピンチイン、ピンチアウトでズームイン、ズームアウトできるようにしました。

ここまでは apple Developer Library(日本語) iOS Scroll View プログラミングガイドの23ページあたりに書かれている通りにやるとすんなり動きました。

その後、ズーム前後でズームの倍率がどれくらい変わったかを知る必要があったので、
viewForZoomingInScrollViewの中で現在のズーム率を取得して、scrollViewDidEndZoomingの引数のscaleと比較してズーム倍率の変化を求めようと考えました。

しかし、viewForZoomingInScrollViewの中で、

beforeScale = [scrollView zoomScale];

などと現在の倍率を調べようとすると無限ループに陥ります。。。

NSLog(@"scale: %f",[scrollView zoomScale]);

なんてのもダメ。
これやっちゃうと固まってしまってしばらく処理を受け付けなくなってしまいました。

うーん、どうやらzoomScaleの値を取得する際にviewForZoomingInScrollViewが呼び出されているようなのです。

こちらにも同じ症状でお悩みの方が。
iphone – UIScrollView bug? float foo = scrollview.zoomScale crashes the app – Stack Overflow

結局scrollViewWillBeginZoomingの中でズーム前のズーム倍率を取得することにしました。
こちらのメソッドは内部でzoomScaleを参照しても大丈夫でした。

英語のヘルプ読んでもviewForZoomingInScrollViewとscrollViewWillBeginZoomingの違いはよく分かりませんでしたが、
使ってみた感じだと前者は、実際にズームされなくても2本指でタッチしたら呼ばれる、
後者は2本指でタッチした後で指を動かして実際にviewの大きさが変わり始めたら呼ばれる
ような感じかな。
viewForZoomingInScrollViewが呼ばれてもscrollViewDidEndZoomingが呼ばれないことは多々あって少し困っていたけど、scrollViewWillBeginZoomingが呼ばれた後は今のところ必ずscrollViewDidEndZoomingが呼ばれてて少し幸せになれました。

iPad実機で画像が表示されない


第一弾のアプリもだいぶできてきたので、とうとうApple Developer登録を済ませました。
例によってgoogleでやり方を調べながら、途中で時間がかかりすぎてセッションが無効になったりしながら、なんとか完了。

それでいよいよiPadでの実機テストを行ったのですが、一部の画像が表示されない!
シミュレータでは表示されるのになぜだー。

こちらの方法や
BridgeMetaware | 実機で画像が表示されない
こちらの方法
実機テストで画像が表示されない
を試してみたけどダメ。

結構長い間うんうん悩んで、、、まぁ悩んでいてもしょうがないのでダメ元でファイル名でも変更してみようと思いまして。
ファイル名を変更して、ソースファイルの画像ファイル読み込み部分も変更しようとしたら大文字の部分が小文字になってる?
これが原因?

で正しく大文字に書き直してやると無事実機でも動くようになりました。

「シミューレータでは画像ファイル名の大文字/小文字の違いは無視するけど、実機では大文字/小文字まで正しく書かないとダメ」
ってことでしょうかね。

初歩的な間違いだけどシミュレータでは平然と動いてくれるので発見が遅れてしまいました。
どうせならコンパイルの時点でエラーにするか、シミュレータでも表示しないようにしてくれると嬉しいんだけどなぁ。

InterfaceBuilderでUILabelのtextを複数行に


コードで書くときはUILabelのnumberOfLinesを増やしてテキストに\nを書いてやれば複数行にできたのですが、InterfaceBuilderのtext項目には\nを入れても、そのまま表示されるだけで改行してくれなかった。

やり方がこちらに載っていたのでメモ。
チーズくんの備忘録 [iOS]InterfaceBuilderを使って、UILabelを複数行にする方法

Linesを0にするというのはやってみたのだけど、option+returnで改行されるとは知りませんでした。
shift+returnとかは試してみたんだけど、、、。
macでは結構optionボタンを使うのね。

そういえば \n のバックスラッシュを入力するときも option+¥ だしね。

UIbutton上の画像を右寄せ

UIButtonにイメージを貼り付け、横サイズ280に固定したかったのですが、
イメージが中寄せになるので困っていました。

いろいろ試してみるとこれで右寄せすることができました。


UIButton * button = [[UIButton alloc] init];
[button setImage:img forState:UIControlStateNormal];
button.frame = CGRectMake(10,10,280,img.size.height);
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

performselectorで実行を遅らせる

iOSアプリ開発の話です。

テスト用のアプリで基本的な動きを学びながら、分かったところで本命アプリに組み込むなんて方法で開発しているのですが、ちょっと複雑にするとすぐ問題が出てきます。
今日発生した問題は画面遷移中に落ちてしまうという問題。

A.「ボタンをタップすると0.3秒のアニメーションがあって、アニメーション後に画面を書き換える」という処理と、
B.「0.1秒毎にtimerを動かしていて、指定した時間が過ぎると時間切れで画面遷移する」という処理があったのですが、
時間切れギリギリにボタンをタップすると、画面遷移のためにviewWillDisappearで変数をnil化した後にAの画面書き換え処理が動いてしまって、nil化した変数にアクセスしてしまい例外が発生していました。

これ、どうしようかと思いましてまずは画面書き換え処理の前にフラグを立てて、処理終了後にフラグを下ろす。フラグが上がっている間は画面遷移せずに待つ。
という方法にしてみました。

一定時間実行を遅らせるために使ったのはこれ。

[NSThread sleepForTimeInterval:0.5];

これで0.5秒待つ。フラグが下りてなかったら再度待つ。5秒待ってもフラグが下りなければ画面遷移強行。ってやってみたのですが、確かにスリープはしていますが、それでもタイミングによっては落ちます。

このsleepForTimeIntervalってのは画面遷移の時間を遅らせると同時に、画面書き換えの処理も止めちゃってますよね?
これじゃ意味ない。

ってわけで次は performselector ってのを使ってみます。

[self performSelector:@selector(OpenNext) withObject:nil afterDelay:0.5];

これで OpenNext という関数を0.5秒後に実行してくれます。
スリープしているわけではないのでその間に画面書き換えの処理は動いて完了してる(っぽい)。

これでどうにか落ちることはなくなりました。

file was built for unsupported file format which is not the architecture being linked (i386)

iADというiPhone/iPad向けの広告を試しに入れてみようと試行錯誤しているのですが、表題のエラーが出ました。

iAD関連のライブラリが不適切なんだろうと思ったのですが、よくわからない。
調べてみると、同じような症状の人がいました。

objective c – Sending email in an iPhone application – Stack Overflow

どうやらiAd.frameworkの追加の仕方が悪かった模様です。
左側にある[project navigator]の中に、Finderから直接iAd.frameworkのフォルダをドラッグアンドドロップしていたのですが、これが間違いらしい。

正しいフレームワークの追加方法は、[project navigator]の一番上のプロジェクト名のところをクリック。
[TARGETS]を選択し、一番上の[Build Phase]を選択。
[Link Binary With Libraries]を選択し、そこの[+]マークからお目当てのフレームワークを追加する。

正しい方法で追加すると正常にビルドできました。
以前AVFoundation.frameworkとか追加したことがあったんだけど、偶然正しい方法で追加していたっぽいなぁ。

NSTimerでアニメーション


一週間以上アニメーションのことで悩んでいました。

UIImageViewを画面を表示している間ずっと点滅させたい、やりたかったのはただこれだけなのですが。

ちょっと調べて最初に書いたコードはこんな感じ。

[UIView animateWithDuration:1.0
  delay:0.0
  options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
  animations:^{ flashIV.alpha = 0.0; }
  completion:nil
];

flashIVの透明度を1秒かけて1→0へ。その後UIViewAnimationOptionAutoreverseの効果で透明度は再び1秒かけて0→1へ戻る。
UIViewAnimationOptionRepeatを指定しているので、これが延々と繰り返すというもの。

これはこれでちゃんと動いていたのですが、ちょっと問題が。
続きを読む