あなたが iOS開発者 ある程度のUI作業を行い、それに情熱を注いでいる人は、アニメーションに関してUIKitのパワーを愛する必要があります。 UIViewのアニメーション化はケーキと同じくらい簡単です。時間の経過とともにフェード、回転、移動、または縮小/拡大する方法についてあまり考える必要はありません。ただし、アニメーションをチェーンしてそれらの間に依存関係を設定する場合は、少し複雑になります。コードは非常に冗長になり、従うのが難しくなり、多くのネストされたクロージャとインデントレベルが発生する可能性があります。
Javaでクラスをテストする方法
この記事では、RxSwiftなどのリアクティブフレームワークの機能を適用して、コードの見た目をすっきりさせ、読みやすく、わかりやすくする方法について説明します。私がクライアントのためのプロジェクトに取り組んでいたときに、そのアイデアが思い浮かびました。その特定のクライアントは非常にUIに精通していました(これは私の情熱と完全に一致していました)!彼らは、アプリのUIが非常に特殊な方法で動作し、非常に洗練されたトランジションとアニメーションがたくさんあることを望んでいました。彼らのアイデアの1つは、アプリの概要を説明するアプリの紹介をすることでした。彼らは、事前にレンダリングされたビデオを再生するのではなく、一連のアニメーションを通じてそのストーリーを伝えて、簡単に調整および調整できるようにしたいと考えていました。 RxSwiftは、このような問題に最適な選択肢であることがわかりました。記事を読み終えたら、気付くようになることを願っています。
リアクティブプログラミングは定番になりつつあり、現代のプログラミング言語のほとんどで採用されています。リアクティブプログラミングが非常に強力な概念である理由と、特定の設計原則とパターンを適用することで優れたソフトウェア設計を促進するのにどのように役立つかを詳細に説明している本やブログがたくさんあります。また、コードの乱雑さを大幅に減らすのに役立つツールキットも提供します。
私が本当に気に入っている側面の1つに触れたいと思います。それは、非同期操作を連鎖させて、宣言的で読みやすい方法で表現できることです。
Swiftに関しては、リアクティブプログラミング言語に変換するのに役立つ2つの競合するフレームワークがあります。ReactiveSwiftとRxSwiftです。例では、RxSwiftの方が優れているからではなく、RxSwiftに精通しているために使用します。読者であるあなたもそれをよく知っていると思いますので、私はそれの本質に直接触れることができます。
ビューを180°回転させたいとしましょう。 その後 フェードアウトします。 completion
を利用できますクロージャとこのようなことをします:
UIView.animate(withDuration: 0.5, animations: { self.animatableView.transform = CGAffineTransform(rotationAngle: .pi/2) }, completion: { _ in UIView.animate(withDuration: 0.5) { self.animatableView.alpha = 0 } })
少しかさばりますが、それでも大丈夫です。しかし、間にもう1つのアニメーションを挿入したい場合、たとえば、回転した後、フェードアウトする前にビューを右にシフトするとどうなりますか?同じアプローチを適用すると、次のような結果になります。
UIView.animate(withDuration: 0.5, animations: { self.animatableView.transform = CGAffineTransform(rotationAngle: .pi/2) }, completion: { _ in UIView.animate(withDuration: 0.5, animations: { self.animatableView.frame = self.animatableView.frame.offsetBy(dx: 50, dy: 0) }, completion: { _ in UIView.animate(withDuration: 0.5, animations: { self.animatableView.alpha = 0 }) }) })
それに追加するステップが多いほど、それはよりずらされて面倒になります。そして、特定のステップの順序を変更することにした場合は、重要なカットアンドペーストシーケンスを実行する必要がありますが、これはエラーが発生しやすいものです。
java restapiテストフレームワーク
そうですね、Appleは明らかにそれを考えていました。彼らは、キーフレームベースのアニメーションAPIを使用して、これを行うためのより良い方法を提供しています。そのアプローチでは、上記のコードは次のように書き直すことができます。
UIView.animateKeyframes(withDuration: 1.5, delay: 0, options: [], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.33, animations: { self.animatableView.transform = CGAffineTransform(rotationAngle: .pi/2) }) UIView.addKeyframe(withRelativeStartTime: 0.33, relativeDuration: 0.33, animations: { self.animatableView.frame = self.animatableView.frame.offsetBy(dx: 50, dy: 0) }) UIView.addKeyframe(withRelativeStartTime: 0.66, relativeDuration: 0.34, animations: { self.animatableView.alpha = 0 }) })
これは大きな改善であり、主な利点は次のとおりです。
このアプローチの欠点は、相対的な期間の観点から考える必要があり、ステップの絶対的なタイミングまたは順序を変更することが困難になる(または少なくともそれほど単純ではない)ことです。ビューを半分ではなく1秒以内にフェードさせることにした場合、実行する必要のある計算と、各アニメーションの全体的な継続時間と相対的な継続時間/開始時間にどのような変更を加える必要があるかを考えてください。次に、他のすべてを同じに保ちながら。ステップの順序を変更する場合も同様です。相対的な開始時間を再計算する必要があります。
不利な点を考えると、上記のアプローチのどれも十分に良いとは思いません。私が探している理想的なソリューションは、次の基準を満たす必要があります。
RxSwiftを使用すると、これらの両方の目標を簡単に達成できることがわかりました。 RxSwiftは、そのようなことを行うために使用できる唯一のフレームワークではありません。完了ブロックを使用せずに構文的に連鎖できるメソッドに非同期操作をラップできる、promiseベースのフレームワークであればどれでもかまいません。しかし、RxSwiftには、演算子の配列で提供できるものがたくさんあります。これについては、後で触れます。
これが私がそれを行う方法の概要です:
資本予算の決定に含まれるステップをリストします
Observable
のオブザーバブルを返す関数にラップします。flatMap
を使用してこれらのオブザーバブルをチェーンしますオペレーター。これは私の関数がどのように見えるかです:
func rotate(_ view: UIView, duration: TimeInterval) -> Observable { return Observable.create { (observer) -> Disposable in UIView.animate(withDuration: duration, animations: { view.transform = CGAffineTransform(rotationAngle: .pi/2) }, completion: { (_) in observer.onNext(()) observer.onCompleted() }) return Disposables.create() } } func shift(_ view: UIView, duration: TimeInterval) -> Observable { return Observable.create { (observer) -> Disposable in UIView.animate(withDuration: duration, animations: { view.frame = view.frame.offsetBy(dx: 50, dy: 0) }, completion: { (_) in observer.onNext(()) observer.onCompleted() }) return Disposables.create() } } func fade(_ view: UIView, duration: TimeInterval) -> Observable { return Observable.create { (observer) -> Disposable in UIView.animate(withDuration: duration, animations: { view.alpha = 0 }, completion: { (_) in observer.onNext(()) observer.onCompleted() }) return Disposables.create() } }
そして、これが私がそれをすべてまとめる方法です:
rotate(animatableView, duration: 0.5) .flatMap { [unowned self] in self.shift(self.animatableView, duration: 0.5) } .flatMap { [unowned self] in self.fade(self.animatableView, duration: 0.5) } .subscribe() .disposed(by: disposeBag)
これは確かに以前の実装よりもはるかに多くのコードであり、このような単純なアニメーションシーケンスでは少しやり過ぎに見えるかもしれませんが、非常に複雑なアニメーションシーケンスを処理するように拡張でき、非常に読みやすいためです。構文の宣言的な性質。
一度それを理解すると、映画のように複雑なアニメーションを作成し、前述のアプローチのいずれかでは非常に難しいことを達成するために適用できるさまざまな便利なRxSwiftオペレーターを自由に使用できます。
.concat
の使用方法は次のとおりです。コードをさらに簡潔にするための演算子—アニメーションが連鎖している部分:
Observable.concat([ rotate(animatableView, duration: 0.5), shift(animatableView, duration: 0.5), fade(animatableView, duration: 0.5) ]) .subscribe() .disposed(by: disposeBag)
次のように、アニメーションの間に遅延を挿入できます。
デザインの多様性の定義の原則
func delay(_ duration: TimeInterval) -> Observable { return Observable.of(()).delay(duration, scheduler: MainScheduler.instance) } Observable.concat([ rotate(animatableView, duration: 0.5), delay(0.5), shift(animatableView, duration: 0.5), delay(1), fade(animatableView, duration: 0.5) ]) .subscribe() .disposed(by: disposeBag)
ここで、ビューが移動を開始する前に、ビューを特定の回数回転させたいと仮定します。そして、回転する回数を簡単に調整したいと思います。
まず、回転アニメーションを連続的に繰り返し、各回転後に要素を放出するメソッドを作成します。オブザーバブルが処分されたらすぐにこれらの回転を停止させたいです。私はこのようなことをすることができます:
func rotateEndlessly(_ view: UIView, duration: TimeInterval) -> Observable { var disposed = false return Observable.create { (observer) -> Disposable in func animate() { UIView.animate(withDuration: duration, animations: { view.transform = view.transform.rotated(by: .pi/2) }, completion: { (_) in observer.onNext(()) if !disposed { animate() } }) } animate() return Disposables.create { disposed = true } } }
そして、私の美しい一連のアニメーションは次のようになります。
Observable.concat([ rotateEndlessly(animatableView, duration: 0.5).take(5), shift(animatableView, duration: 0.5), fade(animatableView, duration: 0.5) ]) .subscribe() .disposed(by: disposeBag)
ビューが回転する回数を制御するのがいかに簡単であるかがわかります。take
に渡される値を変更するだけです。オペレーター。
ここで、作成したアニメーション関数をUIViewの「リアクティブ」拡張機能(.rx
サフィックスからアクセス可能)にラップすることで、実装をさらに一歩進めたいと思います。これにより、リアクティブ関数は通常.rx
を介してアクセスされるRxSwiftの規則に沿ったものになります。オブザーバブルを返していることを明確にするための接尾辞。
extension Reactive where Base == UIView { func shift(duration: TimeInterval) -> Observable { return Observable.create { (observer) -> Disposable in UIView.animate(withDuration: duration, animations: { self.base.frame = self.base.frame.offsetBy(dx: 50, dy: 0) }, completion: { (_) in observer.onNext(()) observer.onCompleted() }) return Disposables.create() } } func fade(duration: TimeInterval) -> Observable { return Observable.create { (observer) -> Disposable in UIView.animate(withDuration: duration, animations: { self.base.alpha = 0 }, completion: { (_) in observer.onNext(()) observer.onCompleted() }) return Disposables.create() } } func rotateEndlessly(duration: TimeInterval) -> Observable { var disposed = false return Observable.create { (observer) -> Disposable in func animate() { UIView.animate(withDuration: duration, animations: { self.base.transform = self.base.transform.rotated(by: .pi/2) }, completion: { (_) in observer.onNext(()) if !disposed { animate() } }) } animate() return Disposables.create { disposed = true } } } }
それで、私はこれらを次のようにまとめることができます:
資本予算は
Observable.concat([ animatableView.rx.rotateEndlessly(duration: 0.5).take(5), animatableView.rx.shift(duration: 0.5), animatableView.rx.fade(duration: 0.5) ]) .subscribe() .disposed(by: disposeBag)
この記事が示すように、RxSwiftのパワーを解き放ち、プリミティブを配置したら、アニメーションを本当に楽しむことができます。コードはクリーンで読みやすく、もはや「コード」のようには見えません。アニメーションがどのようにまとめられているかを「説明」すると、アニメーションが生き生きと動き出します。ここで説明されているよりも複雑なことをしたい場合は、他のタイプのアニメーションをカプセル化する独自のプリミティブを追加することで、確実にそれを行うことができます。また、オープンソースコミュニティの熱心な人々によって開発されたツールやフレームワークの増え続ける武器をいつでも利用できます。
RxSwiftの変換者の場合は、引き続きアクセスしてください。 RxSwiftCommunityリポジトリ 定期的に:あなたはいつも何か新しいものを見つけることができます!
UIに関するその他のアドバイスをお探しの場合は、以下をお読みください Pixelに最適なiOSUIデザインを実装する方法 仲間のApeeScapeerRomanStetsenkoによる。
ソースノート(基本を理解する)
'Swiftは、iOS、macOS、watchOS、tvOS、Linux、z / OS向けにAppleInc。によって開発された汎用のマルチパラダイムコンパイル型プログラミング言語です。 Swiftは、AppleのCocoaおよびCocoa Touchフレームワークと、Apple製品用に作成された既存のObjective-Cコードの大部分と連携するように設計されています。
'UIViewクラスは、コンテンツを表示する画面上の長方形の領域を定義します。それは、その領域内のすべてのコンテンツのレンダリングと、そのコンテンツとのすべての相互作用(タッチとジェスチャー)を処理します。
リアクティブプログラミングは、非同期データストリームと変更の伝播に関係する非同期プログラミングパラダイムです。
RxSwiftは、iOS用のリアクティブプログラミングライブラリです。データの変更やユーザーイベントに応答する動的なアプリを簡単にプログラムできます。
Observablesは、アプリケーションのパブリッシャーとサブスクライバーの間でメッセージを渡すためのサポートを提供します。 Observablesは、データストリームを1つ以上のサブスクライバーに非同期で配信される一連のイベントに変換します。