アプリ開発におけるUIデザインやアニメーションの重要性が高まっていく一方で、
戦士たちは納期と品質という2大魔王の板挟みにあいながら、多くの血を流してきた。
しかし今日この日、Marcus氏との出会いと"Squall"の顕現によって、
ついに僕たちの戦いに終止符を打つ時がきたようだ。
さて、本日ご紹介するSquallについてだが、
すでに弊社プロデューサー兼デザイナーのy_fusanosuke氏が
"AfterEffectsを使えればiOSのアニメーションが作れる「Squall」が神ツールすぎた件"で、
デザイナー視点での紹介をしてくれている。
自分はエンジニア視点で実際にどのようにプロジェクトに組み込むのかを、
実際にSquallを導入して開発したiOSアプリ"Drips"をサンプルに踏まえて説明させていただこう。
Sqaullのおさらい
After Effects to iOS
Convert AE animations to iOS code and preview them instantly.
AfterEffectsで作成したアニメーションを、iOSプロジェクトにおいてものの数行で再現できるツール。
まさにエクスカリバー。
おそらくMarcus氏が単独で作成したと思われる。Marcus△!!
SquallはAfterEffectsExtensionとして導入することができ、AfterEffectsで作成したアニメーションデータをCoreAnimationのコード, もしくは**Squall独自ファイル(.sqa)**として書き出せるようになる。今回はsqaファイルで書き出したケースで説明する。
SquallアニメーションをiOSプロジェクトで再生する
まずは手始めに以下のようなアニメーションを実装するケースから。
iOSプロジェクトにSqaullSDKをインストールし(ライセンスキーの設定をAppDelegate.hに1行追加)、
デザイナーからSqallアニメーションファイルをもらってプロジェクトにおく。
Sqaullアニメーションを再生したい場所で、
SLAnimationInformation *animationInfo = [[[SLReader alloc] init] parseFileFromBundle:@"drip_animation.sqa" error:nil];
SLCoreAnimation *animation = [[SLCoreAnimation alloc] init];
animation.buildDelegate = self;
[animation buildWithInformation:animationInfo];
animation.rootLayer.masksToBounds = NO;
animation.frame = self.frame;
[animation play];
[self.layer addSublayer:animation];
Delegateメソッドでリピート設定をして…、
-(CAAnimationGroup*_Nullable)shouldAddAnimations:(CAAnimationGroup*_Nonnull)group toLayer:(CALayer*_Nonnull)layer withName:(NSString*_Nonnull)name
{
for (CAKeyframeAnimation *animation in group.animations) {
animation.repeatCount = FLT_MAX;
}
group.duration = DBL_MAX;
return group;
}
以上、お疲れ様でした。
今まで、デザイナーからもらったアニメーションを動画で見ながら
せっせと座標計算やら物理演算やらをコードでかいて
アニメーションを実装していた皆様(自分も含む)、本当にお疲れ様でした。
このように、固定オブジェクトのみによるアニメーションの再生だったら、ホントにコレだけ。
では、以下のようにアニメーションするオブジェクトが変動する場合
(表示するデータによって、画像やテキストを変える)、
※Sqaull作者のMarcus氏がおそらくこのような使用方法は想定していなかったと思われ、
AfferEffects側の設定, iOSプロジェクト側のコードともに一筋縄ではいかず、ココが今回の山場
あらかじめAfterEffects側で可変レイヤーにキー名をつけておいてもらい、
// 可変オブジェクトのインスタンスを作っておく
// 注意点として、プライベート変数などでメモリを確保しておかないと、インスタンスが開放されてうまく表示されない
_imageView = [[UIImageView alloc] initWithFrame:self.frame];
_imageView.image = [UIImage imageNamed:@"image.png"];
_label = [[UILabel alloc] initWithFrame:self.frame];
_label.text = @"hoge";
SLAnimationInformation *animationInfo = [[[SLReader alloc] init] parseFileFromBundle:@"news_animation.sqa" error:nil];
// 可変レイヤーのキー名はそれぞれ"image", "text"
[animationInfo replaceLayerWithName:@"image" withLayer:_imageView.layer error:nil];
[animationInfo replaceLayerWithName:@"text" withLayer:_label.layer error:nil];
[animationInfo filterLayerProperties:^BOOL(SLProperty * _Nonnull property, NSString * _Nonnull layerName) {
if ([layerName isEqualToString:@"image"] ||
[layerName isEqualToString:@"text"]) {
if ([property.name isEqualToString:@"Color"]) {
return NO;
}
}
return YES;
}];
SLCoreAnimation *animation = [[SLCoreAnimation alloc] init];
animation.buildDelegate = self;
[animation buildWithInformation:animationInfo];
animation.rootLayer.masksToBounds = NO;
animation.frame = self.frame;
[animation play];
[self.layer addSublayer:animation];
という感じで、
-(void)replaceLayerWithName:(NSString*)name withLayer:(CALayer*)replacementLayer error:(NSError*)error;
メソッドで可変レイヤーに実際に表示させたいオブジェクトのレイヤーを置換させることで、
アニメーションするオブジェクトを可変にすることができた。
Sqaullで遷移アニメーションをカスタマイズ
遷移アニメーションもSquallを使うことでスーパーリッチにすることができ、
公式で以下のようなサンプルがあがっている。
サンプルのAfterEffectsファイルを公式からダウンロードできるので、
そちらを参考にSqallアニメーションファイルを作成すれば、簡単に実装が可能だ。
具体的には、UIViewControllerAnimatedTransitioningを継承したクラスを作成し、
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
メソッド内でSqallアニメーションファイルを再生するといった流れになる。
What's a Marcus!!
開発当時、Sqaullはリリースされたばかりでナレッジがほとんどなく、
手探りで実装していくこととなったが、その中で唯一の光となったのがMarcus氏の存在である。
このSquallというサービス、作者であるMarcus氏本人直々のサポートが手厚い。
公式のメニューにある"Discussion"からとべるFacebookグループ内で質問や疑問点を投稿すると、
Marcus氏が即レスしてくれる。
進化し続けるSquall
Marcus氏、開発スピードも速い。
開発当時はVer.1.3であったが、現在はver.2.02がリリースされており、
Ver.1.3ではできなかったことが、Ver2.02ではできるようになっていたりする。
Ver.2.0からAPIにもかなり変更点があり、真に申し訳ないことなのだが、
今回の記事であげているサンプルコードの中にもすでに変更されたAPIがある。
ただ、Ver.1.3のときにはできなくて諦めたアニメーションも、Ver.2.02ではできるようになっていたりと、
確実に、スピード感をもってサービス向上しているので、今後の期待も大きい。