私たちが信頼するコード

Flowのコードエクスポートは優れており、正当な理由があります。

まず、受賞歴のあるインタラクティブメディア、インスタレーション、最先端のインターフェイスを10年以上にわたって制作してきました。フローは、これらの長年の実践の集大成であり、アート、テクノロジー、デザインに没頭することです。私たちはあなたが考えることができるほとんどすべてのソフトウェアを使用し、Flowを作成するためにすべての経験を利用しています。私たちの背景に飛び込みたい場合は、The Roots ofFlowをチェックしてください。

第二に、私たちはアニメーションソフトウェアの作成の専門家です。私たちのルーツは、Processing(Java)、OpenFrameworks(C ++)に戻り、Objective-Cと、SwiftおよびCoreAnimationのすべてのバージョンに移行しました。次に、ブラウザでアニメーションを作成するための最も高度なネイティブアプローチであるWeb Animations APIに関して、「私たちは自分のたわごとを知っている」と自信を持ってWebにジャンプしました。その過程で多くのトリックを学びましたが、その経験はすべてFlowのコードエクスポートに表れています。コントロール、職人技、コードエクスポートに関する私たちの考えのハイレベルな記事を公開しました。これは間違いなく読む価値があります。

Flowがその主張に応え、Production-Readyソフトウェアを出荷しているという証拠を探しているためにこの記事を読んでいるのなら、あなたはご馳走になります。

序文:迅速なコンテキスト

Flowは、さまざまな種類のメディアファイルとソフトウェアを生成する可能性があるため、コードエクスポート機能の作成で行ったすべての設計とアーキテクチャの決定を完全かつ包括的に説明することは困難です。ただし、エクスポートされたコードの設計、設計、および作成に対するアプローチの背後にある意思決定と哲学は、すべての形式で一貫しています。したがって、この記事では、パターンを探索するための基礎として、Swiftコード、Core Animation、Xcode、およびiOSに重点を置いていますが、私たちのアプローチは他のすべてのエクスポートに適用されます。

アニメーションの定義

アニメーションは、スタイルとタイミングの2つの主要なカテゴリに分類できます。スタイルとは、アニメーションの一部である要素のレイアウト、色、視覚的な形を意味します。タイミングとは、時間の経過に伴う視覚スタイルの変化のペースを意味します。

スタイルとレイアウトv。時間の経過による変化

ソフトウェアの観点からは、これら2つの要素は(視覚的な)プロパティとタイミング関数として理解できます。

モーションの構文

iOSの世界では、視覚要素をレンダリング、構成、およびアニメーション化するためのテクノロジーはCore Animationと呼ばれます。ブラウザーでは、Web AnimationsAPIは非常によく似ています。Core Animationは、開発者がパラメーターを構成し、アニメーションの発生をトリガーできるようにする強力なフレームワークです。その主な長所の2つは、基本的にアニメーションの合成をグラフィックハードウェアに渡すことです。アニメーションの中間の瞬間について心配する必要はありません。また、アニメーションをソフトウェアとして表現するためのほぼ普遍的なモデルを実際に定義します。 。

レイヤー

モバイルアプリを見ると、ツイート、ミュージックビデオ、またはお気に入りのポッドキャストを再生するためのコントロールが表示される場合があります。ただし、実際に見ているのは、複雑なレイヤーのセットであり、互いにオーバーレイすると、アプリのインターフェース全体を構成します。iOSには、画像レイヤーと形状レイヤーの2種類のレイヤーがあります。これらのレイヤーには設定可能なプロパティがあり、その多くは視覚的なもの(影、パス、画像、境界線など)であり、多くの場合数値である値によって定義されます。

この1つのビューですべてのレイヤーをチェックしてください!

レイヤーにプロパティを設定する例を次に示します。

ourLayer.opacity = 0.5 //set its value to 50% opacity

アニメーション

iOSの最も単純な形式は、暗黙的なアニメーションと呼ばれます。iOSのデフォルトの動作では、値が変更されると、0.25秒の期間にわたってアニメーション化されます。

ourLayer.position = newPosition //move an object

let anim = CABasicAnimation(keyPath: "position") 
anim.fromValue = ourLayer.position 
anim.toValue = newPosition 
anim.duration = 1.0 ourLayer.add(anim, forKey: anim.keyPath)

アニメーションの連鎖

このようにアニメーションについて考えることは、もっと高度なことをしたいときにますます難しくなります。要素をフェードインおよびフェードアウトさせたいとします。簡単そうですね。そのアニメーション効果は、実際には2つの不透明度アニメーションに分解する必要があります。

let animIn = CABasicAnimation(keyPath: "strokeWidth")
animIn.fromValue = 0
animIn.toValue = 60
animIn.duration = 1.0
ourLayer.add(animIn, forKey: anim.keypath)
animIn.delegate = self
let animOut = CABasicAnimation(keyPath: "strokeWidth")
animOut.fromValue = 60
animOut.toValue = 0
animOut.duration = 1.0
animOut.isRemovedOnCompletion = false
animOut.fillMode = .forwards
ourLayer.add(animOut, forKey: anim.keypath)

物事がどのように複雑になっているのかに気づきましたか?アニメーションを削除する(または削除しない)ことを知っている必要があります。またfillMode、を設定する方法を知っている必要がありdelegate、最初のアニメーションにを設定することもできます。

ストローク幅の2つのアニメーション:0➔60と60➔0

明示的なアニメーションアプローチ(上記)は、手に負えないほど非常に高速になります。これに対する解決策は、キーフレームアニメーションを使用することです。これにより、値とタイミング関数を設定できます。

これは、キーフレームを使用した上記と同じアニメーションです。

let anim = CAKeyframeAnimation(keyPath: "opacity")
anim.values = [0, 60, 0]
anim.keyTimes = [0, 0.5, 1]
anim.duration = 2.0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
ourLayer.add(anim, forKey: "opacity")

緩和

イージングを見てみましょう。これは、フレームの下部から上部へ、そしてその逆へのレイヤーの動きをアニメーション化するアニメーションです。iOSでは、デフォルトのイージングカーブはEase In Outです。

let anim = CAKeyframeAnimation(keyPath: "position.y")
anim.values = [100, 0, 100]
anim.keyTimes = [0, 0.5, 1]
anim.duration = 2.0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
ourLayer.add(anim, forKey: "position.y")

anim.timingFunctions = [CAMediaTimingFunction(name: .easeIn), CAMediaTimingFunction(name: .easeOut)]

let cubic = CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19)
let back = CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55)
let custom = CAMediaTimingFunction(controlPoints: 1, 0, 0.66, 1)

その他のアニメーションのもの

アニメーションで考慮すべきその他の事項は次のとおりです。

  • パス
  • グラデーション
  • 丸い角
  • グループ
  • ある値から別の値へのスナップ
  • 同じレイヤー上の複数のプロパティの複数のアニメーション
  • 複雑なアニメーションを反転させる
  • カスタムイージングカーブを使用して複雑なアニメーションを反転する
  • アニメーションの状態の管理
  • コントロールの追加(例:ユーザーがアニメーションを一時停止する機能)
  • 回転と変換

柔軟で堅牢、そして簡単

アニメーションコードを書くための最良の方法は、クリーンでクリーン、モダン、そして一貫性を保つことです。しかし、それは私たちがプロダクション対応のアニメーションをエクスポートする方法の一部にすぎません。もう1つの重要なコンポーネントは、ソフトウェアとの対話と制御を容易にすることです。

ビューとタイムライン

すべてのアニメーションは、どんなに複雑であっても、ビューとタイムラインの2つのクラスのファイルに分類されます。この二分法により、アニメーションのレイアウトを簡単に識別および変更でき、アニメーションコードを、必要に応じてホットスワップできる独自のクラスに分離できます。

AnimationView

アニメーションのビューはそのサブクラスでUIViewあり、iOSのネイティブビューとまったく同じように機能します。各アニメーションには、エクスポート時に一意の名前が付けられるため、次のように表示されます。

  • DialedView
  • GradientRingView
  • GriddyView

let view = DialedView(frame: frame)

private func createViews() {
  CATransaction.suppressAnimations {
    createDial()
    createMask()
    createBars()
    //etc...
  }
}

ご覧のとおり、各レイヤーを構築する独自の方法があります。

private func createDial() {
  dial = ShapeView(frame: CGRect(x: 18.5, y: 18.5, width: 32, height: 32))
  dial.backgroundColor = UIColor.clear
  dial.layer.position = CGPoint(x: 18.5, y: 18.5)
  //etc...
}

public func scale(to size: CGSize) {
  let x = size.width / Defaults.size.width
  let y = size.height / Defaults.size.height
  transform = CGAffineTransform(scaleX: x, y: y)
}

AnimationTimeline

タイムラインは、純粋なCoreAnimationコードをカプセル化する一意のクラスです。各タイムラインには、エクスポート時に一意の名前が付けられるため、次のように表示されます。

  • DialedTimeline
  • GradientRingTimeline
  • GriddyTimeline

DialedTimeline(view: dialed, duration: 2)

DialedTimeline(view: dialed, duration: 2, repeatCount: 10, autoreverses: true)
  • timeline.play()
  • timeline.pause()
  • timeline.stop()
  • timeline.reset()
  • timeline.offset(to: time)

タイムラインには、ファイル内のアニメーションレイヤーのキーフレームアニメーションを生成する独自のメソッドがあります。

let transform_rotation_z_dial: CAKeyframeAnimation = {
  let anim = CAKeyframeAnimation()
  anim.keyPath = "transform.rotation.z"
  //etc...
  return anim
}()

Flowからエクスポートされたすべてのプロジェクトは、アニメーションを製品に統合するための先進的なアプローチを採用した、慎重に作成された構造を持っています。

共通ファイル—ライブラリではありません

上で述べたように、アニメーションコードは非常に手に負えないものになる可能性があります。何年にもわたって、Core Animationをマスターするようになったので、アニメーションを簡単にするのに役立ついくつかのトリックを作成しました。

これらの「トリック」は、FlowCommonと呼ばれる一連のファイルにカプセル化されています。これには2つのバージョンがあります。1つはiOS用、もう1つはWeb用で、どちらにも独自のCocoaPodがあります。このコレクションのほとんどのファイルは非常に最小限であり、便利な機能が1つか2つ追加されています。

たとえば、CATransaction+Extension.swiftファイルには次のメソッドのみが含まれています。

extension CATransaction {
  static public func suppressAnimations(actions: () -> Void) {
 begin()
 setAnimationDuration(0)
 actions()
 commit()
  }
}

FlowCommonはライブラリではありません。大部分のファイルには実際のコードが20行未満含まれているため、開発者はこれらを非常に簡単に確認および承認できます。

インターフェースファイルが簡単に

Xcodeプロジェクトには、ビューを含むMain.storyboardファイルがあります。基本的なiOSエクスポートの場合、そのビューのクラスはAnimationViewアニメーションに固有のものです。このアプローチの優雅さは、InterfaceBuilderを介してビューのクラスをアニメーションビューに簡単に変更できることを意味します。

iOSカスタムアクティビティインジケーターなどのカスタムエクスポートの場合、アニメーションビューをネイティブビューに動的に埋め込むことができます。

private func setup() {
  view = createView()
  addSubview(view)
}
override func createView() -> UIView {
  return dialed
}
private lazy var dialed: DialedView = {
  let view = DialedView(frame: .zero)
  view.scale(to: frame.size)
  return startView
}()

Flowからエクスポートされたものはすべて、プラットフォームのネイティブコンポーネントです。iOSの場合、アニメーションビューはでUIViewあり、アニメーションスピナーは実際にはUIActivityIndicatorViewです。いずれの場合も、アニメーションはネイティブ要素に「スリップ」されます。

とても素敵です。

また、よく見ると、Flowからエクスポートされたすべてのプロジェクトが、アプリに読み込みアニメーションを追加する方法を示していることがわかります。

生成されたコードの信頼

アニメーションは大きくても小さくてもかまいません。起動とオンボーディングのアニメーションは、多くの努力、ニュアンス、美しい追加のタッチを加えるのに最適な場所です。対照的に、マイクロアニメーションは、ユーザーがボタン、スライダー、読み込みインジケーターなど、インターフェースのありふれた要素を操作するときに、本当の喜びの瞬間を作り出すことができます。

ソフトウェアの観点からは、単純なアニメーションと複雑なアニメーションの間にほとんど違いはありません。結局のところ、これらは適切な時点でトリガーされるキーフレームアニメーションのコレクションにすぎません。

アニメーションコードは退屈です

アニメーションコードは、実際には非常にありふれたものです。レイヤーを作成し、その初期プロパティを設定し、いくつかのアニメーションを作成して、物事が始まることになっているときにそれらをレイヤーに適用します。これは、すべてのプロパティとすべてのレイヤーに対して、何度も何度も何度も繰り返します。あなたが物事を適切に行うならば、アニメーションコードを書くことは本当にバケツ一杯の泡、すすぎ、繰り返しです。

  1. プロパティを選択してください
  2. その値を設定します
  3. イージングを設定します
  4. 期間を選択してください
  5. それをトリガーする

それはありふれたことです。

それは退屈だ。

些細なことで…

それはとても良いことです。

生成されたコードは信頼できるコードです

生成する必要のあるコードの種類にほとんどばらつきがない場合は、生成されたコードを使用することが、物事を成し遂げるための最善の方法です。作成する必要のあるパターンがわかっている場合は、ジェネレーターを簡単にセットアップして、手間のかかる作業を行うことができます。そのジェネレーターは、体系的かつ正確に、毎回、定義したルールに従います。

ジェネレーターを管理するルールを信頼する場合、生成されるコードは信頼できるものになります。

50の活動指標

これまで、私たちは主にアニメーションを高レベルの視点から見てきました。そこで、Flowから生成されたコードの実際の例を見て、この記事を終了したいと思います。

最近、さまざまなアニメーション手法を表現する50個のカスタムiOSスピナーを使用したオープンソースプロジェクトを公開しました。これは、物事がどのように機能するかを示すためのいくつかのコードの抜粋を含むいくつかの例です。

ダイヤル

このアニメーションは本当に面白いです。効果は本当に複雑に見えますが、テクニックは非常に簡単です。

要素全体を呼びますdial。これは基本的に、ドーナツをマスクとして持つレイヤーです。

let dialMask = CAShapeLayer()
dialMask.path = CGPathCreateWithSVGString("M16,0c-8.837,0,-16,7.163,-16,16 0,8.837,7.163,16,16,16 8.837,0,16,-7.163,16,-16 0,-8.837,-7.163,-16,-16,-16zM16,8c4.418,0,8,3.582,8,8 0,4.418,-3.582,8,-8,8 -4.418,0,-8,-3.582,-8,-8 0,-4.418,3.582,-8,8,-8zM16,8")!
dial.layer.mask = dialMask

dial.addSubview(bars)

anim.keyPath = "position.x" 
anim.values = [-34, 67]
anim.keyPath = "position.y" 
anim.values = [-34, 67]

anim.keyPath = "transform.rotation.z" 
anim.values = [0, 6.28319] //aka 2𝜋, aka 360° 
anim.keyTimes = [0, 1]

これは、グリッド内の16個の同一の正方形で、開始時間を少しオフセットして、同じアニメーションを16回実行することで得られる興味深い視覚効果です。

これの秘訣は、正方形のサイズをアニメーション化することではなく、エッジの線幅をアニメーション化することです。

anim.keyPath = "lineWidth"
anim.values = [4, 4, 0, 0, 4, 4]
anim.keyTimes = [0, 0.205479, 0.547945, 0.719178, 0.993151, 1] 
anim.timingFunctions = [.linear, CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19), .easeInEaseOut, CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1), CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19)]

グラデーションリング

卑劣な方法でマスクを使用することにより、グラデーションカラーのリングをシミュレートできます。

まず、グラデーションを作成します。

let startPoint = CGPoint(x: 0.5, y: -0.0450368)
let endPoint = CGPoint(x: 0.5, y: 1)
let colors = [UIColor(red: 0.463, green: 0.275, blue: 0.988, alpha: 1).cgColor, UIColor(red: 0.314, green: 0.888, blue: 0.76, alpha: 1).cgColor]

gradient.layer.mask = foregroundCircle.layer

foregroundCircle.shapeLayer.fillColor = nil

anim.keyPath = "strokeStart"
anim.values = [0.75, 0.7, 0, 0]
anim.keyTimes = [0, 0.17, 0.55, 1] 
anim.timingFunctions = [.easeInEaseOut, .easeInEaseOut, .easeInEaseOut]
anim.keyPath = "strokeEnd"
anim.values = [1.001, 1.001, 0.25, 0.25]
anim.keyTimes = [0, 0.32, 0.55, 1] 
anim.timingFunctions = [.linear, .easeInEaseOut, .easeInEaseOut]

この記事のアニメーションはすべてFlowで作成されており、これらすべてを手作業で作成することは非常に可能ですが、これらを作成できる速度とソフトウェアの詳細レベルは、Flowなしでは実現できません。

Griddyの例からkeyTimesを取得します。

anim.keyTimes = [0, 0.205479, 0.547945, 0.719178, 0.993151, 1]

代わりに、私はそれらをかなり「丸い」数でしか考えることができないかもしれません。それはアニメーションを本当に素晴らしいものにするフィネスを取り除くでしょう。私自身で、私は思い付くでしょう:

anim.keyTimes = [0, 0.25, 0.5, 0.75, 1, 1]

Flowで作成されたこのアニメーションを誰かが私に持ってきたら、私はそれを私のプロジェクトにハートビートでドロップします。

"なぜ?"

Flowの背後にあるコードジェネレーターは信頼性が高く、エクスポートされるコードは数十年の経験と多大な職人技を反映していることを私は知っているからです。

この記事から取り除くことができるものがあれば、私はそれがこれであることを願っています:

Flowが出荷するコードを信頼しているので、信頼できます。

もともとは2020年11月7日にhttps://createwithflow.comで公開されました。

提案された投稿

Web デザインの進化とその UX 実践への影響

Web デザインの進化とその UX 実践への影響

インターネットの初期から現代のレスポンシブ デザインまでの Web デザインの旅をたどる Web デザインの進化は、1989 年の World Wide Web の発明以来 30 年以上にわたる興味深い旅でした。 Web サイトや Web アプリケーションの作成をガイドするユーザー エクスペリエンス (UX) プラクティスも存在します。

Stash&Store — ストレージの検索とレンタルがより簡単に

Stash&Store — ストレージの検索とレンタルがより簡単に

イントロダクション こんにちは、ミディアム リーダーです。この UI UX ケース スタディでは、モバイル アプリのデザインを紹介します。「Stash&Store」は、私の 2 番目のモバイル アプリケーションの名前です。

関連記事

スイフト—プロトコルはどのように機能しますか?

プロトコルと委任はSwiftの基本的な概念です。このクイックガイドを読んで、それらがどのように機能し、どのように使用するかを学びます。

スイフト—プロトコルはどのように機能しますか?

プロトコルは、Swiftの非常にシンプルでありながら基本的な概念です。プロトコルはコードコントラクトのようなものです。

Mac AppStoreにないMacでiOS / iPadOSアプリを実行する方法

Mac AppStoreにないMacでiOS / iPadOSアプリを実行する方法

M1チップを実行するMacを使用している場合、Mac AppStoreにリストされているiOS / iPadOSアプリのみを実行できます。しかし、Mac AppStoreにリストされていないiOS / iPadOSアプリはどうですか?公式にはできません。

よりアクセスしやすいオーディオプレーヤー

失明や視覚障害を持つ人々に共感を持って構築することを学ぶ

よりアクセスしやすいオーディオプレーヤー

著者:Francis Mariano、iOSエンジニア世界の健康と幸福を向上させる—ヘッドスペースでの大胆な使命声明。そこに住む人々の多様な体験を私たちに認識させるもの。

SwiftUIで円の中にテキストを表示する方法

SwiftUIで円の中にテキストを表示する方法

今回は、SwiftUIを使用して円の中にテキストラベルを表示するいくつかの方法を見ていきます。ZStackビューを使用して、3つの異なる方法をさらに深く掘り下げます。