プロモーション動画作成(TodoCard編)

TodoCardのリリースにあたり、今回もプロモーションビデオを作成しました。結果として.Schedの時とはまったく違う作り方となったので、ご紹介します。


長いのでまとめ


  • Motion5なかなかイイ
  • マクロレンズ必要
  • 絵コンテ作ったほうがよい



今回作成したTodoCardのプロモ




過去に作成した、.Schedのプロモ


 


1. コンセプトを考える


コンセプトというと大げさですが、まずは、どんな感じのムービーにするか、考えます。TodoCardは、アプリのUIが特徴的なので、「アプリの動きや操作感を知ってもらう」という点をムービーの軸におく事とし、操作画面を中心に構成することとしました。


アニメベースでいくか、撮影ベースでいくかは、この時点では未定。どんなツールで組み立てるかと合わせて考えることとし、ツールを探すこととしました。


2. ツールさがし


前回の.Schedの時は、QuarzComposerで作りましたが、Mavericks環境のQuarzComposerではMeshオブジェクトが読み込めなくなってるようで、QuarzComposerでのアニメベースは早々に諦めました。また、QuarzComposerはアルゴリズム的制御は得意ですが、振る舞いをかっちり決めて作り込むのには向いてないということが前回の経験でわかったので、他のツールがないか物色。


ツール選定のポイントは、


  • 学習コストが低いこと
  • アプリのコストが低いこと

Finalcut Pro X? Adobe Premiere? After Effects?、名前は効いた事あるけど、具体的にどんなことが出来るのかのよくわかってない上に高価なソフトなんですよね。日常的に使う道具(アプリ)となる予定もなく、高価=満載機能を使いこなすだけの学習コストをかける投資の選択は現時点ではなし、また、アニメベースか撮影ベースかも決めかねてるので、他にないか物色。


動画の切り貼りだけなら、iMovieだけで十分なんだけど、モーショングラフィックス的なこともちょっと手を出してみたいという色気もだしつつ、探した所、Motion5というものを発見(Apple製)。存在を知りませんでした(汗)。灯台下暗しすぎ。
iMovieでできるようなことはほとんどできて、モーショングラフィックスそれなりにできて、お値段は5,000円。AppStoreで買えます。


https://www.apple.com/jp/final-cut-pro/motion/


有料テンプレート販売してるサイトmotionvfxとかもあったりします。


本買って、半日学習。大まかにマスター。




キーフレームアニメーションが簡単に作れて感動。(QuarzComposerでは、こうはいかない。)


3. 組み立てを考える


「操作画面はワンカットor同じ画角/アングルだと面白くないから、いろんなアングルで撮影してみよう。」「撮影する操作パターンは撮りながら考える。」「オープニング、クロージングは、なんか付ける。」といったことを脳内で何となくイメージして撮影に突入。(無計画に、取りあえずやってみるパターン。後で苦労することに。)


4. 動画撮影


操作画面の撮影は、三脚たてて、背景を含めた場所決め、画角決め、ライティング、アプリ操作の練習、そして撮影といった段取り。
ひとまず、コンデジで撮影してみましたが、ボケがたりず(当たり前)、やっぱり一眼+ボケるレンズで撮影しないとダメということを認識。残念ながら所有しているNIKON D40は動画撮影機能がないので、一旦撮影中止。動画撮影できるモデルにグレードアップするため、キタムラへ走り、中古のD5100をゲット。


そして撮影再開。明るいボケるレンズは持っているものの、iPhoneの操作画面を撮影するにはマクロレンズがないと厳しいことに気付く(いや気付いてたけど、なんとかなるんじゃないかという甘い考えでいた。)。またレンズを買いに行くパワーも、懐の中身もなく、しょうがないので、ズームレンズでなんとかすべく、撮影環境をセット。iPhoneとカメラの距離が離れるので、色々しんどかったですが、なんとか撮影できました。


教訓:マクロレンズ必要。


5. 編集


オープニングとクロージングは、過去にiPhoneで撮影してiPhotoに蓄積されてる動画からピックアップ。モーショングラフィックスを頑張ってみようと思ったけど、オープニングでちょこっと作って満足。


撮影した操作動画をトリミングしたり、切り貼りはじめてみるも、「尺が足りない。」「欲しい操作の絵が足りない。」「なんか画面暗い。」「操作まごついてて見苦しい。」、etc.
で、再撮影〜編集を数回繰り返すはめに。
編集してみて初めて、全体の流れとか、欲しいイメージが具体化してきたりするわけですが、撮影のやり直しは結構苦痛です。後戻り工数デカい。あらかじめ、計画たててから撮影に挑んだほうが効率よいです。モデルさん頼んだり、スタジオかりて撮影するような場合はなおさら。


教訓:絵コンテ重要!


いろいろと、直したい点はいっぱいあるものの、ひとまず通しで形にして完成させてみたら、それなりに満足。Movie作成楽しいです。


次回はランディングページ作成編を記載予定。

TodoCardで使ってるライブラリ

TodoCardで使わさせて頂いたライブラリを(全部ではありませんが、)紹介します。


定番どころ


  • ASIHTTPRequest
  • MBProgressHUD
  • SDWebImage
  • NYXImagesKit
     UIImageを拡張し、いろいろと画像処理が簡単にできるライブラリ。背景画像を更新したときに、白黒からじわじわとカラーになってくエフェクト効果を入れてますが、この時に使っている白黒画像の生成に利用。

その他


  • KGNoise
     UIColorを拡張し、ノイズが入った色を作れます。TodoCardでは、質感を出すために、カードの色に、うっすらノイズいれてます。

  • EAIntroView
     アプリ初回起動時によく表示されるイントロ画面を簡単に作成するライブラリですが、TodoCardでは、ヘルプ画面用に使いました。

  • YLGIFImage
     AnimationGIFをUIImageViewで使えるライブラリ。こちらもヘルプ画面で利用。

  • ANBlurredImageView / FXBlurView
     設定オプション選択画面等で、背景ブラーさせる用。ANBlurredImageViewはPodなし。ANBlurredImageViewとFXBlurViewは、画面により使い分けてる。ブラー化するアニメーションエフェクトをどうしても入れたくてANBlurredImageViewを利用。

  • 500px-iOS-api
     500pxの画像取得用。

  • TUSafariActivity
     ActivityからSafari開く用。TodoCardのホーム画面右下の部分は(隠し?)ボタンにしていて、ここをタップすると表示される背景画像全体表示からのActivityで利用。

  • UIView+TKGeometry
     ViewのRect指定で楽する用。同様の物はいろいろありそう。

iOS開発のデバッグツールchiselの紹介

すっかりblog放置で、1年以上ぶりのエントリになります。(汗)

先日のyidev@恵比寿勉強会参加させて頂き、その際の@dealforestさんの発表でfacebook/chiselというツールを知りました。

これがなかなか便利そうなので、紹介します。

@dealforest さんのblogエントリ
yidev@恵比寿勉強会 で chisel について発表してきた (動画付き)

chiselは、LLDBのコマンドラインからpythosスクリプトでlldbの機能をラップして便利に使うコマンド群のようです。

そもそも私はLLDBをまったく使ってきませんでしたが(汗)、これでNSLog差し込みが減って楽になりそうです。

インストール

1. brew install chisel でインストール

2. ~/.lldbinit-Xcodeに、以下を記載(fblldb.pyのパスは環境に合わせて記載してください。)

command script import /usr/local/Cellar/chisel/1.1.0/libexec/fblldb.py</div>

(.lldbinit だと、Xcodeから起動したLLDBでは呼ばれないので注意。)

Xcodeのlldbプロンプトで、helpとたたくと、chiselで使えるコマンド群が表示されます。さらにhelp <command> で、コマンドの詳細説明が出力されます。


(lldb) help
The following is a list of built-in, permanent debugger commands:
〜略〜
pca -- Run Python function __FBPrintCommands_pca
pcells -- Run Python function __FBPrintCommands_pcells
pclass -- Run Python function __FBPrintCommands_pclass
pinternals -- Run Python function __FBPrintCommands_pinternals
pinvocation -- Run Python function __FBInvocationCommands_pinvocation
pivar -- Run Python function __FBPrintCommands_pivar
presponder -- Run Python function __FBPrintCommands_presponder
ptv -- Run Python function __FBPrintCommands_ptv
pvc -- Run Python function __FBPrintCommands_pvc
pviews -- Run Python function __FBPrintCommands_pviews
show -- Run Python function __FBDisplayCommands_show
taplog -- Run Python function __FBFindCommands_taplog
unborder -- Run Python function __FBDisplayCommands_unborder
unmask -- Run Python function __FBDisplayCommands_unmask
visualize -- Run Python function __FBVisualizationCommands_visualize
vs -- Run Python function __FBFlickerCommands_vs
wivar -- Run Python function __FBDebugCommands_wivarFor more information on any particular command, try 'help '.
(lldb) help pvc
Print the recursion description of <aViewController>.

Arguments:
  <aViewController>; Type: UIViewController*; The view controller to print the description of.

Syntax: pvc <aViewController>

This command is implemented as FBPrintViewControllerHierarchyCommand in /usr/local/Cellar/chisel/1.1.0/libexec/commands/FBPrintCommands.py.

(LLDB adds the next line, sorry...)
Syntax: pvc
(lldb)

便利なコマンドたち

pvc

ViewControllerの階層構造を表示してくれます。
pvc <viewController> で、指定したViewController配下の階層構造を表示します。


(lldb) pvc
 <UINavigationController: 0x109268650; view = <UILayoutContainerView; 0x109419100>; frame = (0, 0; 320, 568)>
 | <KJMasterViewController: 0x109410ec0; view = <UITableView; 0x10a82fc00>; frame = (0, 0; 320, 568)>

(lldb) pvc 0x109410ec0
 <KJMasterViewController: 0x109410ec0; view = <UITableView; 0x10a82fc00>; frame = (0, 0; 320, 568)>
 pviews
 <UIWindow: 0x109319480; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x109319d80>; layer = <UIWindowLayer: 0x109318560>>
 | <UILayoutContainerView: 0x109419100; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x10931bfa0>; layer = <CALayer: 0x109419320>>
 | | <UINavigationTransitionView: 0x10931b970; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x109259740>>
 | | | <UIViewControllerWrapperView: 0x109527b30; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x109530800>>
 | | | | <UITableView: 0x10a82fc00; frame = (0 0; 320 568); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x109266c80>; layer = <CALayer: 0x1095310c0>; contentOffset: {0, -64}>
 〜略〜

pviews

viewの階層構造を表示してくれます。引数でviewを指定すると、指定したview配下の階層構造が表示されます。


(lldb) pviews
<UIWindow: 0x109319480; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x109319d80>; layer = <UIWindowLayer: 0x109318560>>
| <UILayoutContainerView: 0x109419100; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x10931bfa0>; layer = <CALayer: 0x109419320>>
|    | <UINavigationTransitionView: 0x10931b970; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x109259740>>
|    |    | <UIViewControllerWrapperView: 0x109527b30; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x109530800>>
|    |    |    | <UITableView: 0x10a82fc00; frame = (0 0; 320 568); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x109266c80>; layer = <CALayer: 0x1095310c0>; contentOffset: {0, -64}>
〜略〜

pcells

tableViewdで表示されているセルの一覧を表示

presponder

指定したクラスのレスポンダーチェインを表示


(lldb) pcells
 <__NSArrayI 0x109422590>(
 <UITableViewCell: 0x10928e800; frame = (0 0; 320 44); text = '2014-04-27 14:13:37 +0000'; autoresize = W; layer = <CALayer: 0x10928b300>>,
 <UITableViewCell: 0x10933ada0; frame = (0 44; 320 44); text = '2014-04-27 14:13:35 +0000'; autoresize = W; layer = <CALayer: 0x10933b090>>,
 <UITableViewCell: 0x10932fd10; frame = (0 88; 320 44); text = '2014-04-27 14:12:38 +0000'; autoresize = W; layer = <CALayer: 0x109322c00>>,
 <UITableViewCell: 0x109273a10; frame = (0 132; 320 44); text = '2014-04-27 14:11:32 +0000'; autoresize = W; layer = <CALayer: 0x10925d540>>
 )

(lldb) presponder 0x10933ada0
 <UITableViewCell: 0x10933ada0; frame = (0 44; 320 44); text = '2014-04-27 14:13:35 +0000'; autoresize = W; layer = <CALayer: 0x10933b090>>
 | <UITableViewWrapperView: 0x10926ae50; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x10926b050>>
 | | <UITableView: 0x10a82fc00; frame = (0 0; 320 568); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x109266c80>; layer = <CALayer: 0x1095310c0>; contentOffset: {0, -64}>
 | | | <KJMasterViewController: 0x109410ec0>
 | | | | <UIViewControllerWrapperView: 0x109527b30; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x109530800>>
 | | | | | <UINavigationTransitionView: 0x10931b970; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x109259740>>
 | | | | | | <UILayoutContainerView: 0x109419100; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x10931bfa0>; layer = <CALayer: 0x109419320>>
 | | | | | | | <UINavigationController: 0x109268650>
 | | | | | | | | <UIWindow: 0x109319480; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x109319d80>; layer = <UIWindowLayer: 0x109318560>>
 | | | | | | | | | <UIApplication: 0x109308360>
 | | | | | | | | | | <KJAppDelegate: 0x10921a1e0>


また、対象のviewをアプリ上でインタラクティブに特定するための以下のようなコマンドも用意されています。

taplog

実行すると、フォーカスがアプリ(シミュレータor実機)に戻り、画面をtapすると、tapしたviewの情報が表示される。

vs

インタラクティブに、viewの選択/サーチができる。実行すると、指定したViewが点灯表示され、以下のキー入力(+リターン)で、対象viewが遷移する。

(w) superviewへ移動
(s) 最初のsubviewへ移動
(a) 同階層の一つ前のviewへ移動
(d) 同階層の次のviewへ移動
(p) 階層を表示
(q) 終了

pclass

指定したクラスの継承を表示

 fv

viewクラス名の正規表現引数指定すると、該当するviewを表示。

 fvc

viewControllerクラス名の正規表現引数指定すると、該当するviewControllerを表示。
コレ以外にも、指定したViewをアプリ上でマーキングしたり、指定したViewをマーキングしたり非表示にしたり、osx上にイメージ出力したりと、色々と便利な機能が用意されているので、試してみてはいかがでしょうか。

@takayamaさんyidev運営ごくろうさまでした。

P.S.

qiitaに同じ内容投稿しました。
qiitaの方が書き心地がいいので、今度からは、この手のエントリはqiitaに書く事にします。

Xcodeのログを色付きで

XcodeColorsというXcodeのログ出力をカラーにできるPlug-inを導入してみました。
新規プロジェクト作成時のセットアップ手順が極力シンプルになるよう、CocoaLumberjackは使わずに、元々使っていたプリプロセッサマクロをいじって以下のことができるようにしました。

  • DEBUGビルドの場合にのみログ出力する。(RELEASEビルドではログ出力しない)
  • インスタンスのクラス名+メソッド名と、コード中の行番号を表示。
  • カラーパターンについては、標準パターンと、背景色付き(赤、緑、青の3パターン)を用意。

準備

1. XcodeColorsの導入

https://github.com/robbiehanson/XcodeColorsをRELEASEビルドしてインストール。(Runではなく、ArchiveするとRELEASEビルドしてくれます。)

 

2. 以下のコードを .pchにコピペ(新規プロジェクト作成時)

(スニペット登録しておくと便利)


#ifdef DEBUG
//#if (TARGET_IPHONE_SIMULATOR && DEBUG)
#define TRACE(fmt, ...) NSLog((@"\033[fg0,118,135;(%d)\033[fg50,80,200;%s \033[fg0,0,0;" fmt @"\033[;"),__LINE__,__PRETTY_FUNCTION__, ##__VA_ARGS__);
#define TRACE_R(fmt, ...) NSLog((@"\033[bg255,180,180;\033[fg0,118,135;(%d)\033[fg50,80,200;%s \033[fg0,0,0;" fmt @"\033[;"),__LINE__,__PRETTY_FUNCTION__, ##__VA_ARGS__);
#define TRACE_G(fmt, ...) NSLog((@"\033[bg180,255,180;\033[fg0,118,135;(%d)\033[fg50,80,200;%s \033[fg0,0,0;" fmt @"\033[;"),__LINE__,__PRETTY_FUNCTION__, ##__VA_ARGS__);
#define TRACE_B(fmt, ...) NSLog((@"\033[bg180,180,255;\033[fg0,118,135;(%d)\033[fg50,80,200;%s \033[fg0,0,0;" fmt @"\033[;"),__LINE__,__PRETTY_FUNCTION__, ##__VA_ARGS__);
#else
#define TRACE(...)
#define TRACE_R(...)
#define TRACE_G(...)
#define TRACE_B(...)
#endif

使い方

NSLogの代わりに、TRACEとします。


NSLog(@"hoge: %@",obj);
// ↓
TRACE(@"hoge: %@",obj);


背景色付きにしたい場合は、


TRACE_R(@"hoge: %@",obj); // 背景を赤く


です。

こんな感じになります。

UICollectionViewLayoutの実装方法

iOS6で追加されたUICollectionView、色々と夢が広がる感じです。

https://twitter.com/olebegemann/status/248380531690045440

(数ヶ月後には、UICollectionViewLayoutのサブクラスがGitHubにあふれているだろう!)

#まだほとんど無いようですが。。。

色々とカスタマイズしようと思うと、まだ情報が少なく、よくわからない部分が多いです。

DataSourceまわりの扱い(UITableViewでおなじみな感じ)、UICollectionViewFlowLayoutを使った基本的な使い方については、こちらが参考になります。
Natsu's Note
 [iOS6] Collection View 基本的な使い方
今回は、カスタムなレイアウトを実現するために、UICollectionViewLayout(UICollectionViewFlowLayoutではない)をサブクラス化して使う場合について、WWDC2012のサンプルコード(CircleLayout)をもとに解説してみます。

ただし、Appleが配布しているWWDC2012のサンプルコードは、iOS6ベータ時代のもののようで、そのままではまともに動きません。期待通りに動くように修正されたコードが以下にあります。)

元ネタは以下。

http://markpospesel.wordpress.com/2012/10/25/fixing-circlelayout/

コードはこちら。

https://github.com/mpospese/CircleLayout

 

Collection View で利用するクラス

サンプルコードを見るまえに、ざっとCollectionViewについて触れておきます。

Collection View を使う際の主なクラスは、
  • UICollectionView
  • UICollectionViewController
  • UICollectionViewCell
といったあたりですが、UITableViewを扱ったことがある方なら、名前から大体使い方はイメージできるでしょう。Collection View では、そのレイアウトの自由度を高めるために専用のクラスが用意されており、ViewControllerと合わせて使うこととなります。
  • UICollectionViewFlowLayout
  • UICollectionViewLayout
UICollectionViewFlowLayoutは、UICollectionViewLayoutのサブクラスであり、UICollectionViewLayoutは抽象クラスです。UICollectionViewFlowLayoutでは表現できないようはカスタムレイアウトを行ないたい場合は、UICollectionViewLayoutをサブクラス化してレイアウト制御を実装することとなります。このレイアウトクラスが、UITableViewとの最大の差異となっている箇所です。

Collection Viewは、割り当てたLayoutオブジェクトにレイアウト情報を問い合わせ、Layoutオブジェクトがレイアウト情報を必要に応じて返答することで、レイアウトが制御されます。

そのほかに、
  • UICollectionViewLayoutAttributes
  • UICollectionViewUpdateItem
あたりのクラスも押さえておく必要があるかと思います。UICollectionViewLayoutAttributesクラスは、Collection View上の各Cellのレイアウト情報(大きさ、座標、α値などの属性情報)をCellに紐付けて取り扱いやすいようにまとめたクラスです。
UICollectionViewUpdateItemについては、後ほど説明します。

 

UICollectionViewLayout のサブクラス化

サンプルコードを見る前の前準備としてもう一つ、UICollectionViewLayoutで使われるメソッド群について触れておきます。

UICollectionViewLayoutは抽象クラスのため、カスタムなレイアウトを実装する際には、このクラスをサブクラス化し、必要なメソッドを実装していくこととなります。

Appleのドキュメントは、オーバーライドすべき(should)メソッドとして以下が記載されています。

 

collectionViewContentSize

Collection View のコンテンツのサイズを返す。

 

layoutAttributesForElementsInRect:

Rectで指定されたエリアに存在するCell群のレイアウト情報(UICollectionViewLayoutAttributesクラスのオブジェクト)をNSArrayで返す。

 

layoutAttributesForItemAtIndexPath:

NSIndexPathで指定されたCellのレイアウト情報(UICollectionViewLayoutAttributesクラスのオブジェクト)を返す

 

layoutAttributesForSupplementaryViewOfKind:atIndexPath:

supplementary view(今回は説明省略)を利用する場合にそのレイアウト情報を生成する。

 

layoutAttributesForDecorationViewOfKind:atIndexPath:

decoration view(今回は説明省略)を利用する場合にそのレイアウト情報を生成する。

 

shouldInvalidateLayoutForBoundsChange:

Collection Viewのboundsが変更された際に、レイアウトし直すかをBOOLで返す。

 

上記がオーバーライドすべき(should)メソッドとして指定されていますが、上記以外にも、よく使いそうなメソッドとして
  • prepareLayout
  • prepareForCollectionViewUpdates:
  • finalizeCollectionViewUpdates
  • initialLayoutAttributesForAppearingItemAtIndexPath:
  • finalLayoutAttributesForDisappearingItemAtIndexPath:
  • invalidateLayout
といったものがあります。

 

レイアウト情報生成に呼び出されるメソッドの流れ

上記を踏まえ、サンプルコードのCircleLayoutプロジェクトを追いかけていきます。

CircleLayoutプロジェクトは、丸いCellを円状にレイアウトし、かつ、Cellをタップした際にはアニメーションしながらCellを削除、背景をタップした際にはアニメーションしながらCellを挿入するデモコードです。

サンプルコードでは、CircleLayout.mがUICollectionViewLayoutのサブクラス実装です。今回の説明はこの部分にフォーカスして説明していきます。

このコードを見ながら、先に紹介したLayoutクラスの各メソッドが、UICollectionViewがレイアウト情報を取得する際に、主要なメソッド群がどのように呼び出されるかの流れを、呼び出される順に見て行きます。

 

 

1. prepareLayout

レイアウトの計算に必要となるような、前処理を行う。

-(void)prepareLayout
{
    [super prepareLayout];

    CGSize size = self.collectionView.frame.size;
    _cellCount = [[self collectionView] numberOfItemsInSection:0];
    _center = CGPointMake(size.width / 2.0, size.height / 2.0);
    _radius = MIN(size.width, size.height) / 2.5;
}

ここで何をすべきというお約束は特にないので、実装するレイアウト計算に必要な前処理を行なう部分となります。

 

 

2. collectionViewContentSize

コンテンツサイズを返します。

-(CGSize)collectionViewContentSize
{
    return [self collectionView].frame.size;
}

まあ、そのままですね。

 

 

3. prepareForCollectionViewUpdates

これは、Cellの削除、追加、移動処理が行なわれた際にのみ呼ばれます。
削除、追加、移動処理のための前処理を定義します。

- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
    // Keep track of insert and delete index paths
    [super prepareForCollectionViewUpdates:updateItems];

    self.deleteIndexPaths = [NSMutableArray array];
    self.insertIndexPaths = [NSMutableArray array];

    for (UICollectionViewUpdateItem *update in updateItems)
    {
        if (update.updateAction == UICollectionUpdateActionDelete)
        {
            [self.deleteIndexPaths addObject:update.indexPathBeforeUpdate];
        }
        else if (update.updateAction == UICollectionUpdateActionInsert)
        {
            [self.insertIndexPaths addObject:update.indexPathAfterUpdate];
        }
    }
}

このメソッドでは、お約束の実装パターンが必要となります。

渡されるupdateItemsのArrayは、UICollectionViewUpdateItemオブジェクトからなるArrayで、UICollectionViewUpdateItemオブジェクトを利用し、削除、挿入、移動などの更新処理がなされたCellを特定することができます。
Cellの削除、挿入、移動のアニメーション処理のために、更新処理が行なわれた対象Cellの情報(indexPath)をここで保持しておく処理を行なっています。

また、

    [super prepareForCollectionViewUpdates:updateItems];

の行は、お約束として必ず入れておいてください。この行がないと、アニメーションされません。

 

 

4. layoutAttributesForElementsInRect + layoutAttributesForItemAtIndexPath

rectで指定された領域に存在するCell群のUICollectionViewLayoutAttributesオブジェクト(以降、attributesと略記)をArrayで返す実装をここでします。

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray* attributes = [NSMutableArray array];
    for (NSInteger i=0 ; i < self.cellCount; i++) {
        NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }
    return attributes;
}

CircleLayoutプロジェクトでは、全てのCellが表示領域に存在しているため、このメソッドの中で、存在する全てのCell数分のattributesを生成しています。attributes生成そのものは、layoutAttributesForItemAtIndexPathメソッドで行なう構造となっています。

この構造だけ見ると、layoutAttributesForItemAtIndexPath メソッド必要なの?と思いますが、後に出る、initialLayoutAttributesForAppearingItemAtIndexPath,finalLayoutAttributesForDisappearingItemAtIndexPathなど、Cell単体でのattributes取得が必要な場面が出てくるので、お約束として実装しておきます。

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
    attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
    attributes.center = CGPointMake(_center.x + _radius * cosf(2 * path.item * M_PI / _cellCount),
                                    _center.y + _radius * sinf(2 * path.item * M_PI / _cellCount));
    return attributes;
}

cellのindexPath情報に紐付けてUICollectionViewLayoutAttributesオブジェクトを生成し、Cellのサイズや位置を定義しています。

 

 

5. initialLayoutAttributesForAppearingItemAtIndexPath:

Cellを挿入した際に、挿入アニメーションを実現するために存在するメソッドです。
挿入初期状態のCellのattributesを返すようにします。
ここで生成したattributesの状態から、最終的な状態(挿入完了状態)のattributes (layoutAttributesForItemAtIndexPathで生成されるもの)へとアニメーションされることとなります。

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    // Must call super
    UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    if ([self.insertIndexPaths containsObject:itemIndexPath])
    {
        // only change attributes on inserted cells
        if (!attributes)
            attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];

        // Configure attributes ...
        attributes.alpha = 0.0;
        attributes.center = CGPointMake(_center.x, _center.y);
    }

    return attributes;
}

このメソッドがくせ者で、実装すべき内容を誤解しやすいのですが、、、、
このメソッドは、挿入されたCell分だけでなく、もともと存在するCellを含め表示する全てのCell毎に呼び出されます。(もともと9個のCellがある状態から1個のCellを追加した場合には、このメソッドは10回呼ばれます。)

ついつい、挿入対象のCellの初期状態のattributesのことだけ考えてしまいそうになりますが、元々存在しているCellの挿入処理前のattributesも、それぞれのCellに対して応答する必要があるわけです。

以下の行で、全てのCellに対し、挿入前状態のattributesを取得しています。

    UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

さらに、

    if ([self.insertIndexPaths containsObject:itemIndexPath])

の部分で、指定されたindexPathが新たに挿入されたCellか、元々存在しているCellかを判定しています。
ここで判定に利用している、self.insertIndexPath は、3. prepareForCollectionViewUpdates で用意したものを利用しています。

 

 

6. finalLayoutAttributesForDisappearingItemAtIndexPath:

Cellを削除した際に、削除アニメーションを実現するために存在するメソッドです。
削除完了状態のCellのattributesを返すようにします。

- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    // So far, calling super hasn't been strictly necessary here, but leaving it in
    // for good measure
    UICollectionViewLayoutAttributes *attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];

    if ([self.deleteIndexPaths containsObject:itemIndexPath])
    {
        // only change attributes on deleted cells
       if (!attributes)
            attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];

        // Configure attributes ...
        attributes.alpha = 0.0;
        attributes.center = CGPointMake(_center.x, _center.y);
        attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);
    }

    return attributes;
}

実装の考え方は、5.の挿入と同様なので、説明は省略。

 

 

7. finalizeCollectionViewUpdates

一連のレイアウト情報生成が完了後、最後に呼ばれるメソッドです。

- (void)finalizeCollectionViewUpdates
{
    [super finalizeCollectionViewUpdates];
    // release the insert and delete index paths
    self.deleteIndexPaths = nil;
    self.insertIndexPaths = nil;
}

レイアウト情報生成に利用したデータをクリアしています。

長くなりましたが、以上です。

もし、記載内容に誤り等あれば、ご指摘頂けますと幸いです。

Appleの「日本の祝祭日」カレンダーがおかしくなってる

11/3あたりから、appleが以下のURLで提供している「日本の祝祭日」カレンダーのエントリが英語表記になり、かつ、祝祭日の日付やエントリも所々おかしい状況になっています。

http://www.apple.com/downloads/macosx/calendars/japaneseholidaycalendar.html



(天皇誕生日が12/24だったり、クリスマスが祝日だったり、、、、)

iPhoneのカレンダーで日本の祝日を表示する方法として、上記カレンダーの参照は多くのBlogで紹介されているため、影響を受けている方は非常に多いのではないでしょうか。

いずれは、修正されるでしょうが、取り急ぎの対処方法として、Googleが提供している「日本の祝日」カレンダーを利用する方法をご紹介します。

非常に簡単です。iPhoneのSafariで以下のURLを開いて、祝日カレンダーを「照会」するだけです。

webcal://www.google.com/calendar/ical/ja.japanese%23holiday%40group.v.calendar.google.com/public/basic.ics

appleの提供している「日本の祝祭日」カレンダーについては、一時的に非表示にするか、同期エントリそのものを削除すればよいでしょう。

以下、参考リンクです。

Appleサポートコミュニティ

Japanese Holidays Calendarが英語のみの表示になった?

iPhoneちゃんねる

iPhoneのカレンダーで祝日が突然英語表記に!その対処法は

酔いどれオヤジのブログwp

【iPhone,iPad】カレンダーの祝日表記が英語になる問題

crocodile notebook

iOSで日本の祝日が英語表記になってるので仮のカレンダーを公開しています

 

2012/11/8 追記

Apple提供の「日本の祝祭日」カレンダーの不具合修正され、復旧したようです。

 

iOS6から導入されたUIActivityViewControllerでShareKitいらず

iOS6から使えるようになった、UIActivity/UIActivityViewControllerをご存知でしょうか。(WWDC2012の資料で見かけた記憶が無く、最近存在に気付きました。)
iOS6の標準Appで見かけるようになった以下のようなアイコンベースのアクションシート画面を表示して、イメージやテキストなどをShareすることができるviewControllerです。



Share先は、Facebook,Twitter,Weibo, Message, Mail, Print, Pasteboard, AssignToContact, SaveToCameraRollに標準で対応しています。

渡せるデータは、各サービスによって異なりますが、おおよそ、NSString, UIImage, NSURLあたり。渡すデータ種別に応じて、対象となるShare先が自動的に選択されて表示されます。(対象データがテキストの場合は、SaveToCameraRollは表示されないなど。)

使い方は超簡単。

- (IBAction)shareButtonPressed:(id)sender {

 // 1. shareする素材を用意して、NSArrayにする。(複数種類のデータを一度に渡せる)
 NSString *shareText = @"hogehoge";
 NSURL *shareURL = [NSURL URLWithString:@"http://www.apple.com"];
 NSArray *activityItems = @[self.imageView.image, shareText, shareURL];

 // 2. Controller生成
 UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
 // 第一引数にShareするデータをNSArrayで
 // 第二引数には、標準以外のサービスを利用したい場合に指定。
 // (Share対象サービスを自作できるようで、自作したサービスを利用する場合に指定。標準サービスのみ利用の場合は、nilでOK)

 // 3. shareボタンを表示したくないサービスを NSArrayで用意して、指定
 NSArray *excludeActivities = @[UIActivityTypePostToWeibo];
 activityController.excludedActivityTypes = excludeActivities;

 // 4. activity処理完了時の動作
 void (^completionHandler)(NSString *activityType, BOOL completed) = ^(NSString *activityType, BOOL completed){
 NSLog(@"completetion handler");
 // ただし、キャンセルした場合は、completedがNOでかえってくる。(Activity処理そのものの成否は解らない?
 };
 activityController.completionHandler = completionHandler;

 // modalで表示
 [self presentViewController:activityController animated:YES completion:^{
  NSLog(@"Activity complete!!");
 }];
}
デモコードを以下におきました。
https://github.com/kenhama/UIActivityDemo

従来であれば、ShareKitを使うところですが、アプリのサポート対象がiOS6以上でよいならばこちらが簡単でよさげです。

追記:WWDC2012のPDFあらためてチェクしたら、ちゃんと紹介されてたorz
追記2: completion handlerを追記

Expedited App Review Request してみた話

Expedited App Review Request ってご存知でしょうか?

 

昨日リリースされた.Schedのver.3.20で,iOS6対応にBUGがあり予定が全く参照できないという致命的状況となってしまいましたが、本日、修正版のver.3.21がリリースされました。(利用者の方、大変ご迷惑おかけしました。m(_ _)m )

 

通常、iOSアプリのリリースには、Appleの審査に約1週間程待たされるのですが、この「Expedited App Review Request」が通ると優先的にレビューしてもらうことが出来ます。

 

やり方は、

1. iTunes Connectを開く

2. Contact us を選択

3. “App Review” -> “Request Expedited Review” を選択

4. contact us のリンクをクリックすると、申請フォームが現れるので入力 (緊急対応が必要な理由を英作文)



 

 

・フォームに入力〜送信後、1時間もせずにメール連絡(定型フォーマット自動返信っぽい)

超訳

「超忙しいんで、対応可能かどうかの返事は、営業日1日か2日待って」

 

・15時間後にメール連絡(定型フォーマットっぽい)

超訳

「緊急状況は解ったんで1回だけ例外でexpdited reviewするよ。」

「expedited reviewは保障するものでないし、限定的なものなんで、次は対応できるかわからんよ。」

・5時間後にInReview

・1時間後にProcessing for App Store

 

ということで、iTunes ConnectにアプリをSubmitしてからリリースまで通常1週間はかかるところを、24H以内でリリースできました。

 

やっちゃった時のための、緊急修正手段として覚えておいて損はないかと思います。

 

追記@9/22 13:00

上記を書いてる間に、ver.3.21でiOS5だと起動できないという、さらに致命的なBUGを埋め込んでいたことが判明(汗)。

利用者の方(iOS5の環境でver.3.21を導入された方)へ、お詫び申し上げます。修正版がリリースされるまで今しばらくお待ち願います。

現状は、一旦AppStoreから削除した上で、修正版のver.3.22をsubmit、再度Expedited review requestしています。(多用はダメと言われてるそばから、再度expedited review依頼にチャレンジ中)

 

追記@9/25 AM

二回目のExpedited review requestが無事受け入れられ、先ほど、Rady fo saleになりました。二回目のExpedited review requestを行った日が土曜日であったため、土日明けで即座に対応してくれた感じです。(ダメもとの2連続Expedited review requestでしたが、通りました。ちなみに、メール内容は1回目と完全に同一内容でした。)

 

 

.Sched3にスキンデザインの選択機能を追加しました

久々のBlog投稿(汗)

 

既に1週間ほど立ちましたが、.Sched3のVersion 3.1がリリースされました。

Ver.3.1では、スキンデザインの選択機能を追加しています。

カラーリングだけでなく、フォントや背景イメージなどを含むスキンデザインセットとなってます。

(今後も、すこしづつデザインは追加していく予定です。)