iOS7用のホームアイコンの設定

もうすぐiOS7がリリースされるそうなので、 Webアプリでホーム用アイコンを設定しているプロジェクトは アイコンのデザイン調整の対応を検討しないとなーと思って調べたメモです。

iOS6/5用と iOS7用の切替方法

iOS6/5用のアイコンは 114px四方なのに対してiOS7用のアイコンは120px四方なので、 link要素(rel=apple-touch-icon)のsizes属性で各OS用に切り替えることができそうです。

参考)http://blog.fonland.net/2011/02/iphone-ipad-apple-touch-iconpng.html

調整が必要なデザインは?

iOS7のアイコンは角丸の具合をふくめたアウトラインと内部のグリッドが変わったので、 角丸に合わせたスタイリングがしてあるものとか四隅に要素が隣接しているものは調整したほうが良い気がしました。

いったんiOS7の端末にインストールしてみてから検討すればいいと思いますが、 個人的にはフラットかどうかよりも四隅の余白のバランスに違和感を感じるものが多かったです。

納品後のプロジェクトとかでも、こういうのは対応してあげたいんですけどねー。

iOSアプリのモックアップをWebView(ローカル)で作ってみた。

今回はネットワークに繋がない端末でモックアップをインストールしたかったので、UIWebViewからローカルにバンドルしたHTMLを呼び出すだけのiOSアプリを作成しました。 もとのHTMLはテストサイトとかで作っていた画面遷移を確認する程度のWebアプリです。

  1. Single View Applicationのテンプレートで新規プロジェクト作成する
  2. UIWebViewを配置しViewControllerをdelegateに設定する
  3. ViewControllerにwebviewプロパティを定義しOutletとして接続しておく
  4. ViewControllerにUIWebviewDelegateプロトコルを実装する
  5. ViewContorllerのviewDidAppearあたりでアプリのリソースバンドル内のHTMLファイルを指定する。

NSURLを生成するときにネットワーク上のアドレスではなく、ファイルパスを渡すためURLWithStringの代わりにfileURLWithPathを呼び出す。

あと、HTMLと画像やJSなどのリソースがすべてバンドル内のルート(?)に展開されるそうなので、 ブラウザ用に作っていたものを転用する場合、HTMLやCSSのパスをバンドルされた状態にあわせて調整する必要がある。(./js/index.js -> ./index.js) (名前がかぶった時とかどうなるんだろう…検証してませんが。)

参考)http://waka.hatenablog.com/entry/2012/11/21/091037

また、JSがコンパイルするリソースとして設定されてしまうので、適宜、コピーするリソースのほうに移動してあげる必要があるそうです。コンパイルされるほうはリソースはあるけどバンドルリソースとしてアクセスできないので。

参考)http://d.hatena.ne.jp/KishikawaKatsumi/20091229/1262052856

見せられませんが、JSでサクッとつくったものがわりと綺麗にアプリっぽくなったので全部出来る人がつくるぶんにはいい方法だな、と思いました。

iOS SafariでドラッグするとrequestAnimationFrameが止まる件

JSでrequestAnimationFrameを使ってアニメーションさせるとき、 ネイティブのスクロールが発生するとスクロール中はrequestAnimationFrameが一時的に止まるらしい。厳密にはする時としないときがあった。

スワイプジェスチャーを実装してドラッグに応じてアニメーションさせようとしていたのですが、 要はブラウザがスクロールするとザ・ワールド!してしまうという感じらしい。 困った。

今回はフルスクリーン想定なのでtouchmoveでe.preventDefault()すればよかったが、 ネイティブのスクロールと併用すると困るパターンが出てきそう。

@GekoTangの言うとおりtouchstartでpreventDefaultでも良いのだけど、それだと子要素のclickイベントも死んでしまうため、touchmoveでスクロール抑止しました。

CGRectをオフセットする方法

描画用のCGRectを用意する際に、イラレ的な感覚で矩形をオフセット(ピクセル単位でパスを拡張)したかったのですが、
CGRectOffsetかと思いきや、CGRectInsetを使うと簡単にできました。

GCRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)

もとのCGRectと、内側に拡張したい数値を入れると新しいCGRectが生成されます。
dx, dyをマイナスにすると外側に広がった矩形が生成できます。

CGRect rect = CGRectMake(0, 0, 100, 100);
CGRect insetRect = CGRectInset(rect, 10, 10);

NSLog(@"CGRectInset %@",NSStringFromCGRect(insetRect));
//CGRectInset {{10, 10}, {80, 80}}

drawRectメソッドの中などで枠線の幅を考慮した計算が必要になることがありますが
そういう描画用の矩形を生成するときに便利です。

CGRectOffsetはIFが似てますが、始点(rect.origin)を動かしたCGRectを作る関数みたいです。

GCRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)

CGRect rect = CGRectMake(0, 0, 100, 100);
CGRect offsetRect = CGRectOffset(rect, 10, 10);

NSLog(@"CGRectOffset %@",NSStringFromCGRect(insetRect));
//CGRectOffset {{10, 10}, {100, 100}}

Indexを管理するモデル@AS3版

Flashでモックつくっていてカルーセルとかスライドショー的なものを汎用化していったらIndex.asっていうのを作る感じになったので公開してみるです。

Index.as

package  {
	import flash.events.EventDispatcher;
	import flash.events.Event;
	
	public class Index extends EventDispatcher {
		private var _index:int = 0;
		private var _min:int = 0;
		private var _max:int = 0;
		private var _length:int = 0;
		private var _loop:Boolean = false;

		public function Index(min:int = 0, max:int = 0, loop:Boolean = false) {
			updateRange(min, max);
			_loop = loop;
		}

		public function set index(newIndex:int):void {
			newIndex = validate(newIndex);
			
			if (_index != newIndex) {
				_index = newIndex;
				
				dispatchEvent(new Event(Event.CHANGE));
			}
		}
		public function get index():int {
			return _index;
		}
		
		public function next():void {
			index++;
		}
		
		public function prev():void {
			index--;
		}
		
		public function range(min:int, max:int, uselessRefresh:Boolean = false):void {
			updateRange(min, max);
			
			if (!uselessRefresh) {
				index = index;
			}
		}
		
		public function set loop(value):void {
			_loop = value;
		}
		public function get loop():Boolean {
			return _loop;
		}
		
		public function get isMin():Boolean {
			return index === _min;
		}
		
		public function get isMax():Boolean {
			return index === _max;
		}
		
		private function updateRange(min:int, max:int):void {
			_min = min;
			_max = max;
			_length = _max - _min + 1;
		}
		
		private function validate(value:int):int {
			if (isNaN(value)) return _index;
			
			if (_loop) {
				value -= _min;
				value %= _length;
				if (value < 0) value += _length;
				return  value + _min;
			}
			else {
				return Math.min(Math.max(value, _min), _max);
			}
		}
	}
	
}

使い方

下の例では、ViewがIndexインスタンスのCHANGEイベントを受け取って更新されるようにしてます。

package  {
	import flash.events.Event;
	import flash.display.Sprite;
	
	public class SlideBase extends Sprite {
		private var _model:Index;
		public function SlideBase(size:int = 3) {
			_model = new Index(0, size - 1, true); //loop == true
			_model.addEventListener(Event.CHANGE, onChangeIndex);
			// initialize
			onChangeIndex(null);
		}
		public function next(): void {
			_model.next();
		}
		public function prev(): void {
			_model.prev();
		}
		public function index(value:int): void {
			_model.index = value;
		}
		public function loop(value:Boolean): void {
			_model.loop = value;
		}
		private function onChangeIndex(event:Event):void {
			updateDisplay(_model.index);
		}
		protected function updateDisplay(index:int):void {
                        // indexを使って見た目を更新する
		}
	}
}

あとはボタンのCLICKイベント、キーボードイベント、タイマーイベントなど何かしらのタイミングでmodelを操作すればViewが切り替わるので、かなり柔軟でしかもスッキリ書けるようになります。

import flash.events.MouseEvent;

var slide:SlideBase = new SlideBase();
addChild(slide);

setInterval(function () {
    slide.next();
}, 10000);

addEventListener(MouseEvent.CLICK, function (event:MouseEvent) {
    slide.next();
});

解説

機能要件

  • index、min、maxあたりのプロパティを持ってる
  • next(), prev(), index()的なI/Fを持っている
  • loop処理、 範囲に応じたtrim処理あたりをよしなにやってくれる
  • イベント発行する機能を持っている

あと最大値かどうか、最小値かどうかを返すメソッドを追加したあたりでまとめてみました。

実装

  • 各プロパティはカプセル化する。indexはget/setアクセサを提供
  • loopはフラグにして、値が変更された時にmin/maxでtrimするかループさせるかをうまいことやる
  • AS3なのでEventDispatcherを継承するだけ。index値に変化があればEvent.CHANGEイベントを発行

という感じ。

工夫したところ

特に難しいことはないですが、ループする場合にすこし工夫が必要でした。

普通はindex値を範囲の長さで割った余りでシンプルに表現できるんだけど、
index値がマイナスになっちゃったりするとちょっとややこしいです。

ループさせたい範囲が min 0 ~ max 2 (長さ3)の場合で考えてみます。

値をnextしていくとき(index > 0になりうる場合)

var length = 3;
var newIndex = index % length;
// 0 % 3 => 0
// 1 % 3 => 1
// 2 % 3 => 2
// 3 % 3 => 0
// 4 % 3 => 1
// ...

値をprevしていくとき(index < 0になりうる場合)

var newIndex = index % length;
//  0 % 3 => 0
// -1 % 3 => -1 // ループ的には2であってほしい
// -2 % 3 => -2 // ループ的には1であってほしい
// -3 % 3 => 0 // 3の倍数の時は問題なさげ
// ...

というふうに、0 から prev()したときに、期待する挙動としては 2に戻ってほしいのに -1を3で剰余すると−1になってしまってうまくいかない。

でもよく見ると、結果値がマイナスのとき、結果値から期待値を引くと常に-3になる。

結果値 - 期待値 = -3

これを期待する値に合わせて整理すると

期待値 = 3 + 結果値

という事になるので、もし結果値がマイナスだったら、剰余する値を1度足せばうまくループしてくれるぽい。

var length = 3;
var newIndex = index % length;
if (newIndex < 0) newIndex += length;

検算

  • 0 % 3 = 0
  • -1 % 3 + 3 = -1 + 3 = 2
  • -2 % 3 + 3 = -2 + 3 = 1
  • -3 % 3 = 0 = 0
  • -4 % 3 + 3 = -1 + 3 = 2
  • -5 % 3 + 3 = -2 + 3 = 1
  • ...

とりあえずいきなり大きいマイナス値を入れても何とかなりそうです。
FlashでもJSでも地味にややこしいところなので整理してみました。

前回Backbone.jsで似たようなものを書いていますが、次はもちょっとちゃんとした版としてJSに移植してみます。

Androidでoverflowした要素をスクロール可能にするjQueryプラグイン

Androidでoverflowするとスクロールできない問題を解決するためにつくりました。

使い方

#hoge {
overflow:auto;//Androidは強制的にhidden的な挙動になる
width:320px;
height:400px;
}
$("#hoge"). overflowScroll();

iOSでもoverflowを設定すると慣性スクロールが効かなくなるので有用かもしれませんが、
よほどでなければUAを判定してAndroidだけ適応するほうがいいと思います。

仕組み

touchStartで取得したポイントをと現在の指の位置の差分をscrollTopに設定しているだけです。
模式的に書くと次のようなかんじです。

タッチ開始

$target.on("touchStart", function(event) {
            event = event.originalEvent;
            var touch = event.touches[0];
            touchStartX = touch.pageX;
            touchStartY = touch.pageY;
});

タッチ中(ドラッグ中)

$target.on("touchMove", function(event) {
            event = event.originalEvent;
            var touch = event.touches[0];
            touchX = touch.pageX;
            touchY = touch.pageY;

            $target.scrollLeft(touchStartX - touchX);
            $target.scrollTop(touchStartY - touchY);
});

タッチ完了

$target.on("touchEnd", function(event) {
            event = event.originalEvent;
            var touch = event.touches[0];
            touchStartX = touch.pageX;
            touchStartY = touch.pageY;

            //何かあれば
});

実際にはtouchEndで直前の touchX/touchYとの差分をもとに話した時の指の速度を割り出し
それをもとに慣性スクロールっぽくするよう試みてます。

$target.on("touchEnd", function(event) {
            event = event.originalEvent;
            var touch = event.changedTouches[0];

            touchSpeedX = (touchX - touch.pageX);
            touchSpeedY = (touchY - touch.pageY);

            intervalID = setInterval(afterMoving, INTERVAL_TO_MOVE);
});
function afterMoving() {
            //速度があまりにも小さくなったら止まったことにする。
            if ((Math.abs(touchSpeedX)<LIMIT_TO_STOP && Math.abs(touchSpeedY)<LIMIT_TO_STOP)) {
                clearInterval(intervalID);
                return;
            }

            var oldX = $target.scrollLeft(),
                oldY = $target.scrollTop();

            //摩擦係数をかけて減速
            touchSpeedX *= FRICTION;
            touchSpeedY *= FRICTION;

            $target.scrollLeft(oldX + Math.round(touchSpeedX));
            $target.scrollTop(oldY + Math.round(touchSpeedY));
}

iOS開発の日本語ドキュメントを分類してみた

iOS開発の日本語のドキュメントの一覧が見づらかったので、自分的にわかりやすいまとまりにしてみた。
https://developer.apple.com/jp/devcenter/ios/library/japanese.html

全部読んでいないので、適宜直していこうと思います。

iOSプログラミングの基本的な話

文字列操作プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/Strings.pdf

キー値コーディングプログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/KeyValueCoding.pdf

キー値監視プログラミングガイド
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html

Blocksプログラミングトピックス
https://developer.apple.com/jp/devcenter/ios/library/documentation/Blocks.pdf

高度なメモリ管理プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/MemoryMgmt.pdf

並列プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/ConcurrencyProgrammingGuide.pdf

スレッドプログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/Multithreading.pdf

ファイルシステム プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/FileSystemProgrammingGuide.pdf

ファイルメタデータ検索プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/SpotlightQuery.pdf

位置情報対応プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/LocationAwarenessPG.pdf

iOSイベント処理ガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/EventHandlingiPhoneOS.pdf

iOS View プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewPG_iPhoneOS.pdf

iOS Scroll View プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/UIScrollView_pg.pdf

iOS View Controller プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerPGforiOS.pdf

iOS View Controllerカタログ
https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerCatalog.pdf

iOS Table View プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/TableView_iPhone.pdf

Collection View プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/CollectionViewPGforIOS.pdf

Cocoa Auto Layout ガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/AutolayoutPG.pdf

Core Dataプログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/CoreData.pdf

iOS ドキュメントベース アプリケーション プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/DocumentBasedAppPGiOS.pdf

iOS マルチディスプレィ プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/WindowAndScreenGuide.pdf

ネットワーキング コンセプト
https://developer.apple.com/jp/devcenter/ios/library/documentation/NetworkingConcepts.pdf

ネットワーキング オーバービュー
https://developer.apple.com/jp/devcenter/ios/library/documentation/NetworkingOverview.pdf

ネットワーキング プログラミング トピックス
https://developer.apple.com/jp/devcenter/ios/library/documentation/NetworkingTopics.pdf

各機能の実装の話

iPodライブラリアクセス プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/iPodLibraryAccess_Guide.pdf

iOSカメラプログラミングトピックス
https://developer.apple.com/jp/devcenter/ios/library/documentation/CameraAndPhotoLib_TopicsForIOS.pdf

Local および Push Notification プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/RemoteNotificationsPG.pdf

Passbookの概要
https://developer.apple.com/jp/devcenter/ios/library/documentation/Getting_Started_with_Passbook.pdf

Passbook プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/PassKit_PG.pdf

iAd プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/iAd_Guide.pdf

カレンダーとリマインダーのプログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/EventKitProgGuide.pdf

Game Center プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/GameKit_Guide.pdf

iOS Address Book プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/AddressBookProgrammingGuideforiPhone.pdf

iCloud設計ガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/iCloudDesignGuide.pdf

iTunes Connect デベロッパガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/iTunesConnect_Guide.pdf

In App Purchase プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/StoreKitGuide.pdf