この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんぬづは、そろそろ実家の歳の離れた弟に会いたくなってきた田中です。
さて自分ごとで恐縮ですが、まずは宣伝、今度登壇します。
東京では初登壇です!
try! Swift登壇者の@cjwirthさんや、try! Swift主催の@k_katsumiさんもスピーカーなので、ガクブルですが頑張ります!
はい、宣伝はぼちぼちにして今日はSwiftのフレームワークの紹介をしていきますよ。
C4
今日ご紹介しますのは、SwiftでCreative CodingをするオープンソースフレームワークのC4。 C4はUIKitとCoreAnimationを元に作られています。 opneFrameworksやProcessingなどの他のCreative Coding Frameworkに慣れた人にとってはとても簡単で、初心者でも扱いやすいということがコンセプトになっているようです。
Tutorialも多く用意されていて、中にはSlackのロゴアニメーションや、Skypeのローディングなどが紹介されていて、実践的です。
インストール
公式ページを見ると、インストール方法がカンフーマスターで例えられていくつか紹介されています。 中国武術をやっている身としてはとても親近感。
フレームワークをダウンロードしてきてプロジェクトに追加する基本的なやり方は、低難度で親しみやすさダントツのJC(ジャッキー・チェン。女子中学生ではない)から、最高難度がターミナルから直接git叩いてインストールしてくれって書いてあるブルース・リー。
僕は今回iOS開発者ならお馴染みの、Cocoapodsを使ってインストールしました。
use_frameworks!
pod 'C4', '~> 1.0'
上記をPodfileに記述してpod install。
サンプル
今回サンプルとしてこんなものをつくってみました。
どこかで見覚えのある九つの色、のついた円を順々にアニメーションさせて、最後に消えていくオレンジの円は他の円と違うアニメーションを加えました。
以下がコードの全文です。 なかなか長くなっていますが、繰り返し書いているためです。 パターンとしては多くありません。
import UIKit
import C4
class ViewController: CanvasController {
override func setup() {
// MARK: - Circles
let orange = createCircle()
let gray = createCircle()
let blue = createCircle()
let green = createCircle()
let red = createCircle()
let yellow = createCircle()
let pink = createCircle()
let lightblue = createCircle()
let purple = createCircle()
// MARK: - Colors
let orangeColor = Color(red: 226.0/255.0, green: 122.0/255.0, blue: 4.0/255.0, alpha: 1)
orange.fillColor = orangeColor
orange.strokeColor = orangeColor
let grayColor = Color(red: 125.0/255.0, green: 125.0/255.0, blue: 125.0/255.0, alpha: 1)
gray.fillColor = grayColor
gray.strokeColor = grayColor
let blueColor = Color(red: 2.0/255.0, green: 104.0/255.0, blue: 183.0/255.0, alpha: 1.0)
blue.fillColor = blueColor
blue.strokeColor = blueColor
let redColor = Color(red: 235.0/255.0, green: 66.0/255.0, blue: 44.0/255.0, alpha: 1.0)
red.fillColor = redColor
red.strokeColor = redColor
let greenColor = Color(red: 4.0/255.0, green: 164.0/255.0, blue: 105.0/255.0, alpha: 1.0)
green.fillColor = greenColor
green.strokeColor = greenColor
let yellowColor = Color(red: 241.0/255.0, green: 172.0/255.0, blue: 0.0/255.0, alpha: 1.0)
yellow.fillColor = yellowColor
yellow.strokeColor = yellowColor
let pinkColor = Color(red: 232.0/255.0, green: 65.0/255.0, blue: 143.0/255.0, alpha: 1.0)
pink.fillColor = pinkColor
pink.strokeColor = pinkColor
let lightblueColor = Color(red: 0.0/255.0, green: 143.0/255.0, blue: 215.0/255.0, alpha: 1.0)
lightblue.fillColor = lightblueColor
lightblue.strokeColor = lightblueColor
let purpleColor = Color(red: 113.0/255.0, green: 79.0/255.0, blue: 156.0/255.0, alpha: 1.0)
purple.fillColor = purpleColor
purple.strokeColor = purpleColor
// MARK: - Positions
orange.center = canvas.center
gray.center = Point(CGPoint(x: canvas.center.x - 100, y: canvas.center.y))
blue.center = Point(CGPoint(x: canvas.center.x + 100, y: canvas.center.y))
red.center = Point(CGPoint(x: canvas.center.x, y: canvas.center.y - 120))
green.center = Point(CGPoint(x: canvas.center.x - 100, y: canvas.center.y - 120))
yellow.center = Point(CGPoint(x: canvas.center.x + 100, y: canvas.center.y - 120))
pink.center = Point(CGPoint(x: canvas.center.x, y: canvas.center.y + 120))
lightblue.center = Point(CGPoint(x: canvas.center.x - 100, y: canvas.center.y + 120))
purple.center = Point(CGPoint(x: canvas.center.x + 100, y: canvas.center.y + 120))
// MARK: - Add circles
canvas.add(orange)
canvas.add(gray)
canvas.add(blue)
canvas.add(red)
canvas.add(green)
canvas.add(yellow)
canvas.add(pink)
canvas.add(lightblue)
canvas.add(purple)
// MARK: - Animations
let scale = Transform.makeScale(100, 100)
let fadeOutColor = Color(red: 0, green: 0, blue: 0, alpha: 0)
let orangeAnim = ViewAnimation(duration: 1.0) {
orange.transform = scale
orange.fillColor = fadeOutColor
}
let grayAnim = ViewAnimation(duration: 0.7) {
gray.transform = scale
gray.fillColor = fadeOutColor
}
let blueAnim = ViewAnimation(duration: 0.7) {
blue.transform = scale
blue.fillColor = fadeOutColor
}
let redAnim = ViewAnimation(duration: 0.7) {
red.transform = scale
red.fillColor = fadeOutColor
}
let greenAnim = ViewAnimation(duration: 0.7) {
green.transform = scale
green.fillColor = fadeOutColor
}
let yellowAnim = ViewAnimation(duration: 0.7) {
yellow.transform = scale
yellow.fillColor = fadeOutColor
}
let pinkAnim = ViewAnimation(duration: 0.7) {
pink.transform = scale
pink.fillColor = fadeOutColor
}
let lightblueAnim = ViewAnimation(duration: 0.7) {
lightblue.transform = scale
lightblue.fillColor = fadeOutColor
}
let purpleAnim = ViewAnimation(duration: 0.7) {
purple.transform = scale
purple.fillColor = fadeOutColor
}
let orangeShrink = ViewAnimation(duration: 0.5) {
orange.transform = Transform.makeScale(0.3, 0.3)
}
orangeShrink.curve = .EaseIn
let sequence = ViewAnimationSequence(animations: [
pinkAnim, lightblueAnim, purpleAnim,
redAnim, greenAnim, yellowAnim,
grayAnim, blueAnim, orangeShrink, orangeAnim
])
wait(1.0) {
sequence.animate()
}
}
private func createCircle() -> Circle {
let rect = Rect(0, 0, 50, 50)
return Circle(frame: rect)
}
}
解説
C4の導入
まずC4を使うために
import C4
と記述して、C4をインポートします。
ミソとなるのはC4を扱うためにはUIViewControllerを継承して作られたCanvasViewControllerというクラスを扱うこと。基本的としてはこのクラスを使って、定義してあるsetup()の中でアニメーションや図形を扱うとよいでしょう。
class viewController: CanvasViewController {
override func setup() {
}
}
CanvasViewControllerの定義を見に行くとsetup()が定義されており、このsetup()はUIViewControllerのviewDidLoad()の中で呼び出されていることがわかります。
public override func viewDidLoad() {
canvas.backgroundColor = C4Grey
ShapeLayer.disableActions = true
self.setup()
ShapeLayer.disableActions = false
}
図形の生成
tutorialを見るとわかるのですが、基本図形となるようなものはC4の中で定義されています。
今回はCircleを生成して、生成したインスタンスのサイズ、ポジション、色などを設定してcanvasにaddしています。 基本的なUIKitの使い方ととても似ているのでとっかかりやすいですね!
let circle = Circle(frame: Rect(0, 0, 50, 50))
// 図形の中の色
circle.fillColor = Color(red: 226.0/255.0, green: 122.0/255.0, blue: 4.0/255.0, alpha: 1)
// 図形の枠線の色
circle.strokeColor = Color(red: 226.0/255.0, green: 122.0/255.0, blue: 4.0/255.0, alpha: 1)
canvas.add(circle)
アニメーション
単一のアニメーションの生成は以下のようにします。
// アニメーションの生成。0.5秒の間にclodureの中の処理を行う
let animation = ViewAnimation(duration: 0.5) {
// circleのサイズを0.3倍にする
circle.transform = Transform.makeScale(0.3, 0.3)
// alphaを0としているので、フェードアウトする
circle.fillColor = Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
}
// アニメーション開始時を早くする
animation.curve = .EaseIn
// アニメーションを繰り返し行う
animation.repeats = true
// アニメーションが終了したら逆再生を行う
animation.autoreverses = true
// アニメーションの再生
animation.animate()
円形のサイズを広げていくようなアニメーションはリッチ(派手)なUIなんかでよく実装されているのを見ますが、自前で書こうとするとviewを二枚重ねてmaskを設定してゴニョゴニョ...となってなかなか面倒くさかったりします。CircleをmakeScaleして広げる上記はなかなか便利です。
アニメーションを連続させる
アニメーションAが終わったらアニメーションBを再生して、次にアニメーションCをつなげて...と順次実行していきたいというパターンはよくあると思います。
自前で実装しようとすると、completionHandlerで終了を判定してネストを深くしていくか、もしくは非同期処理をメソッドチェーンで書けるようなライブラリを別途導入することも考えますがここら辺は面倒だったりします。
安心してください、便利クラスが用意されてますよ。
// 初期化時のanimationsの引数の型は[ViewAnimation]。
let sequence = ViewAnimationSequence(animations: [animationA, animationB, animationC])
// シークエンスの再生
sequence.animate()
サンプルコードで使用した部分は大体このくらいです。
注意
Colorクラスの初期化時にrgbを指定するinitializerは二つ用意されています。
init(red: Double, green: Double, blue: Double, alpha: Double)
init(red: Int, green: Int, blue: Int, alpha: Double)
実装はそれぞれ以下のようになっています。
public init(red: Double, green: Double, blue: Double, alpha: Double) {
colorSpace = CGColorSpaceCreateDeviceRGB()!
internalColor = CGColorCreate(colorSpace, [CGFloat(red), CGFloat(green), CGFloat(blue), CGFloat(alpha)])!
}
public convenience init(red: Int, green: Int, blue: Int, alpha: Double) {
self.init(red: Double(red) / 255.0, green: Double(green) / 255.0, blue: Double(blue) / 255.0, alpha: alpha)
}
Doubleを引数としている方はそのままの値なのですが、Intを引数としている方は/255.0されています。
そのため、Doubleを引数とする場合は/255.0を自分で記述し、Intを引数とする場合は記述しなくても良いというところが少しハマりポイントかと思われますので、注意してみてください。
まとめ
アプリにおいてはアニメーションを少しつけてあげるだけでも、ユーザーにとって楽しいアプリになったりします。
C4を使ってより良いUXを獲得していきましょう。?ファイトだよ!?