見出し画像

「図形をアニメートする」徹底解説

割引あり

Playgrounds 4.1 で新しい App教材 が追加になりました。
解説のふたつ目は「図形をアニメートする」です。
【2023年4月:一部更新・加筆しました】

この App教材は全部で 19 のソースコードを含みますが、解説のないコードが7つあります。
最初に開いた時からワーニングを表示したり用語説明リンク先の間違いや、ソースコードそのものにも不要な部分もありました。
この記事はこれらをじっくり解説しています。
初学者を混乱させる部分には「‼️」マークを付けました(検索してください)。

この記事は『Playgrounds 4.1徹底解説』マガジンで読むことができます。



・画像クリックで拡大表示できます
・画像を拡大表示中は画像の左右をクリックで画像だけを順に表示できます
・ソースコード部分は左右にスクロールできます
・リンクしているドキュメントは英文が多いですが、翻訳機能を活用してください
・この記事は Mac用 Playgrounds と iPad用 Playgrounds 共通です

画面はライトモードの iPad を使い、操作説明も「タップ」と書いています、Macでは「クリック」に読み替えてください。



🟢 図形をアニメートする

サブタイトルは「図形とアニメーションについて学ぶ」です。

チュートリアルとアプリのコードを解説の二部構成の教材です。
アプリに関係する説明はありません。

「図形をアニメートする」のダウンロード画面


概要

Playgrounds 4.1 で「Appを拡張する」に追加された App教材です。

バージョン 1.0.0 
リリース:2021年12月15日 2022年5月17日
Swift5.5 Swift5.8版
【2023年4月現在、バージョン番号は 1.0.0 のままですがリリース日と対応 Swift バージョンは変更されていました】
プロジェクトの容量は5.6MB 6.4MBです。

バージョン番号とリリース日は Playgrounds 4.0 で利用可能だった App教材と同じです。
Apple社としては最初から提供予定だったが 4.0 には間に合わなかったのかも知れません。


🟢 全体構成

ガイドの構成

このApp教材のガイドは前半の「図形を試してみる」と後半の「アニメーションの例を詳しくみる」の二つにわかれています。
前半が自分でコード入力して試すチュートリアル、後半がアプリの解説です。
Playgrounds 4.0 のApp教材はチュートリアルか解説のみかどちらかでした。

この App教材は見栄えのするアニメーションを実行でき、かつ図形とアニメーションの基本も体験できるように構成されています。
見栄えのするアニメーションはどうしても複雑で難しいコードになるため、このような新しい構成にしたようです。

ダウンロード直後に実行し、アニメーションを確認することができる App教材です。

図形を試してみる

最初に説明を読みながら実際にコードを入力するチュートリアルの6つのガイドがあります。

・図形のサイズ
・図形の修飾子
・カスタム図形を作成する
・図形を作成する
・基本アニメーション
・一致したジオメトリエフェクト

アニメーションの例を詳しくみる

アプリのコードのうち図形とアニメーションに関係した部分の解説です。

・ハートの鼓動
・回転するアニメーション
・爆発するアニメーション
・うねる虹
・ドラッグ・アンド・ドロップする
・踊るドット

プロジェクト内の全19コードのうち6コードは説明がありません。
ガイドに説明のないコードの解説は『🟢 アプリの構成と解説なしコードの説明』に書きました。


🟢 基本の予習

いくつかの基本を確認しておくと Swift や SwiftUI の理解が深まります。
(教材内の説明は日本語ですが、最小限です。ここで予習してください。)


プロトコルについて

「プロトコル」は「型」と似ていますが型ではありません。
特定のプロパティやメソッドを要件として定義します。

実際の型でプロトコルに準拠すると、プロトコルの要件を満たしていない場合はエラーとなります。
エラーなしの状態であれば、確実に要件で定義されたプロパティやメソッドを利用できます。

Swift言語は複数のプロトコルに準拠でき、プロトコルを活用した実装になっています。
Swift言語の日本語版言語ガイドの「プロトコル」を参照してください。

Shape プロトコル
この教材では Shape プロトコルなどを利用します。

Shape

インスタンスメソッド path(in:) が必須です。

role プロパティも必須ですが、デフォルトの実装が提供されています。


SwiftUI のプレビューについて

プレビューは Xcode の機能として登場した、 SwiftUI のカスタムビューの表示と動作を確認するためのしくみです。
プレビューをプレビュー表示させるには、各ビューに数行コードを追加します。

Playgrounds 4 以降では Appプロジェクトで Xcode 用のプレビューコードを使ってプレビューを表示します。
プレビューコードがない場合は、アプリとしての起動画面をプレビュー表示します。
Playgrounds 4 以降ではアプリの起動画面はプレビューコードは不要で「Appプレビュー」を表示します。


プレビュー用のコード

「ハートの鼓動」のプレビューコードの例です。

struct HeartPulseView_Previews: PreviewProvider {
    static var previews: some View {
        HeartPulseView()
    }
}

プロトコル「PreviewProvider」に準拠した struct で型名は通常ビューの型名の後ろに「_Previews」を追加します。
「HeartPulseView」のプレビューなら「HeartPulseView_Previews」です。

このコードを Xcode や Playgrounds が見つけてプレビューを表示します。
プレビュー用コードはアプリの実行には関係しません

プレビュー用コードの入力
App教材では多くのビューがプレビューの設定済みです。

自分のコードのプレビューを表示させる場合の手順です。
コードの末尾をタップして挿入点を置きます。
ライブラリ(➕ボタン)で「プレビュー」と検索し絞り込みます。

ライブラリ操作での入力手順

「プレビューのプロバイダ」をタップすると、コードに5行入力されます。

ライブラリで入力したコード

「MyPreviewProvider」を実際のビューの型名で置き換え、Textを実際のビューのコードで置き換えてください。

プレビュー関連資料
Creating Your App’s Interface with SwiftUI(英文)

Previews in Xcode(英文)


図形とアニメーション

noteで公開している私のほかの記事を参照してください。

図形について
SwiftUI UI部品カタログ 後編」の「11 Shapes(基本図形)」(後半の有料部分です)

SwiftUIのPath図形表示
図形入門などを含む記事です。

アニメーションについて
SwiftUIのアニメーション」を参照してください。
有料記事ですがおよそ半分試読できます。

これらの記事は「SwiftUI初級脱出パッケージマガジン購入でも読むことができます


※ SwiftUI のメソッド(モディファイアー)やイニシャライザーは引数を省略して記述する場合が多いです。
ドキュメントへのリンクでデフォルトや省略可能な引数を確認してください。
webブラウザーの翻訳機能も活用してください。


🟢 図形を試してみる


図形をアニメートする」をダウンロード直後に開くと iPad ではコードとAppのプレビューを表示します。

詳しい情報」ボタンをタップするとガイドを表示します。

ガイドを開始する」ボタンで最初のタスク「図形のサイズ」を開始します。

ガイドが表示されていない場合は「ガイド」ボタンをタップしてください。


タスク:図形のサイズ

ここでの「図形」は ShapeShapeプロトコルに準拠した SwiftUI ビュー)のことです。

ステップ 1/9
最初の説明です。
青文字部分は用語説明または英文ドキュメントを表示します。
できるだけすべての説明を確認してください。

「図形」のリンクが Shape プロトコルの英文ドキュメントにリンクしています。
英語のドキュメントが表示されるので驚かないでください。
「Shape(図形)」だとドキュメントが表示されることがわかりやすいと思いました。

コードは正しいですが、VStack の中身がからなのでまだプレビューには何も表示されていません。

ステップ 2/9
「Circle()」を追加します。
「Circle()」は円のビュー Circle 型のイニシャライザーです。
「追加」ボタンタップでコードの必要な位置に挿入され、プレビューにも表示されます。

表示される円の色は通常(ライトモード)なら黒、ダークモードの場合は白になります。
円はプレビューの幅と同じ直径、つまり出来るだけ大きな円として表示します。

Circle

ステップ 3/9
「.border(.green)」を「コピー」ボタンでコピーし、コード内にペーストします。
ペーストする場所が説明には書かれていませんが、2/9 で追加した「Circle()」の次の行です。

「.border(.green)」を追加するとプレビューの枠全体に緑色の枠線を表示します。(プレビューの四角が角丸なので、四隅に枠線が見えない部分があります)

border(_:width:)

func border<S>(
    _ content: S,
    width: CGFloat = 1
) -> some View where S : ShapeStyle

width: 引数を省略した場合、線幅は 1 になります。

ステップ 4/9
「Circle()」ビューの円の表示とビューの範囲についての説明です。
とても当たり前の説明ですが、実は「円形」は縦横が同じなので全体には広がらない特殊なビューです。
そのため、ビューの枠線を緑色で表示して確認しました。

ステップ 5/9
「Circle()」ビューを別の図形に変える練習です。
ここでは4つの図形から選択できると書かれています。

説明文部分は選択やコピーができないので、キーボードから入力するか、ライブラリ(➕ボタン)から入力してください。
その他の「長方形のフレーム」「カプセル」「角丸四角形」「楕円」です。
ぜひすべて確認してください。
「角丸四角形」のイニシャライザーは角丸半径の指定が必要です、80などを入力してください。

プレビュー範囲が角丸なので「長方形のフレーム」も角丸に見えてしまいます。

Rectangle

Capsule

RoundedRectangle
init(cornerRadius:style:)
引数cornerRadiusは角丸の半径を設定します。
style: 引数は省略可能でデフォルトで . circular となります。

Ellipse

ステップ 6/9
「.frame(width: 200)」をコピーしてコードに追加します。
追加(ペースト)する場所は「.border(.green)」の下です。

追加するとプレビューの表示が変わります。
幅が 200 に制限され、円も小さくなります。

「.frame(width: 200)」は幅だけを指定しています。
このモディファイアー(修飾子)は引数は三つでそれぞれ省略可能で、二つは省略されています。

frame(width:height:alignment:)

func frame(
    width: CGFloat? = nil,
    height: CGFloat? = nil,
    alignment: Alignment = .center
) -> some View

引数 width は CGFloat型インスタンスか数値リテラルで指定します(オプショナルなので nil も指定できます)。
指定しない場合はデフォルトの nil となります。
nil の場合は幅は変わりません。
引数 height も同様です、省略すると高さはかわりません。
引数 alignment は省略可能で、省略した場合はデフォルトの .center となります。

ステップ 7/9
このステップではサイズ指定を「.frame(width: 200, height: 100)」に変更します。
省略せず幅も高さも指定します。

緑色の枠が幅 200 高さ 100 になり、円は直径が100で中央に表示します。

ステップ 8/9
このステップでは最大最小を指定する、別のモディファイアー(修飾子)を使います。

.frame(maxWidth: 200, maxHeight:200) と最大幅と最大高さだけ指定していますが、最大や最小でサイズを指定する frame モディファイアー(修飾子)は引数がたくさんあります。
同じ frame ですがラベルが違います。
frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)

func frame(
    minWidth: CGFloat? = nil,
    idealWidth: CGFloat? = nil,
    maxWidth: CGFloat? = nil,
    minHeight: CGFloat? = nil,
    idealHeight: CGFloat? = nil,
    maxHeight: CGFloat? = nil,
    alignment: Alignment = .center
) -> some View

引数はすべてデフォルトが指定されていて省略可能ですが、少なくとも一つの値を指定してください。
引数 minWidth は最小幅を設定します。
引数 idealWidth は理想的な幅を設定します。
引数 maxWidth は最大幅を設定します。
引数 minHeight は最小高さを設定します。
引数 idealHeight は理想的な高さを設定します。
引数 maxHeight は最大高さを設定します。
引数 alignment は省略可能で、省略した場合はデフォルトの .center となります。
詳しくはドキュメントを参照してください。

ステップ 9/9
最小と最大を試すステップです。
いろいろためしてください。

‼️説明文に「frame修飾子の値を変更して、最小と最大の高さをためしてみましょう。」とありますが、「frame修飾子」のリンクが「frame(width:height:alignment:)」を開きます。
「最小と最大の高さをためす」のであれば利用するモディファイアーは「frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)」です。
用語クリックで表示するドキュメントが違っています。

別の図形(楕円などのShape)の追加は必ずためしてください。
別の図形との関係で最小の設定が生きることを確認しましょう。

「完了」ボタンをクリックするとチェックマークを表示し、次のタスク「図形の修飾子」に切り替わります。


タスク:図形の修飾子

ソースコードが ModifyShapes に切り替わります。

ステップ 1/11
コードは正しいですが、VStack の中身がからなのでまだプレビューには何も表示されていません。

stroke、strokeBorder、fill、foregroundColor、background、shadow はそれぞれ Shape の表示を変更するモディファイアー(修飾子)です。

ステップ 2/11
「Circle()
    .stroke(.green, lineWidth: 10)」を追加します。
「追加」ボタンでコードの正しい位置に追加され、対応するプレビューが表示されます。

stroke(_:lineWidth:)
線描する ShapeStyle と線の幅を指定します。

func stroke<S>(
    _ content: S,
    lineWidth: CGFloat = 1
) -> some View where S : ShapeStyle

最初の引数は ShapeStyle 型のインスタンスです。
二つ目の引数 lineWidth は線の幅です。
引数を省略した場合のデフォルトは1.0です。

ShapeStyle プロトコル
このドキュメントには Color 型や LinearGradient型など、ShapeStyle プロトコルに準拠している型は16あることが末尾に書かれていて、それぞれにリンクしています。

ステップ 3/11
stroke モディファイアー(修飾子)は図形輪郭を中心線とする線を描きます。
線幅の半分だけ外側と内側に描きます。
このため線が画面の幅よりもはみ出します。

strokeBorder(_:lineWidth:antialiased:)
線幅の1/2だけ内側に線を描きます。

func strokeBorder<S>(
    _ content: S,
    lineWidth: CGFloat = 1,
    antialiased: Bool = true
) -> some View where S : ShapeStyle

antialiased: 引数は省略してデフォルトで利用することがほとんどです。

ステップ 4/11
あたらしいプロトコル「InsettableShape」が登場しました。
リンクは英文ドキュメントを表示します。
「strokeBorder」は InsettableShapeプロトコルで、全部で4種類あります。

このタスクで利用する図形( Shape )は InsettableShape 準拠なので「strokeBorder」を利用できます。

ステップ 5/11
点線の例です。

Ellipse()
.strokeBorder(.pink, style: StrokeStyle(lineWidth: 10,
miterLimit: 10, dash: [20, 5], dashPhase: 5))

こちらは style 指定ありの「strokeBorder」です。

strokeBorder(_:style:antialiased:)

func strokeBorder<S>(
    _ content: S,
    style: StrokeStyle,
    antialiased: Bool = true
) -> some View where S : ShapeStyle

引数 style には StrokeStyle型インスタンスを渡します。

StrokeStyle のイニシャライザーは引数(パラメーター)が多いですが、ドキュメントをじっくり見てください。
init(lineWidth:lineCap:lineJoin:miterLimit:dash:dashPhase:)
点線は dash: と dashPhase: 引数を使って指定します。

点線の指定について詳しくは「SwiftUI UI部品カタログ 後編」の「11-8 点線」を参照してください。

点線をどこから描き始めるかは「SwiftUIのPath図形表示」の「3-4 dashPhase」を参照してください。

ステップ 6/11
StrokeStyle型の英文ドキュメントにリンクしています。

lineWidth
miterLimit
dash
dashPhase

が載っていますが、ほかに
lineCap
lineJoin
があります。

ステップ 7/11
輪郭線のかわりに塗りつぶします。

Circle()
   .foregroundColor(Color(red: 1.0, green: 0, blue: 1.0))

foregroundColor(_:)
Color
Color にはいろいろなイニシャライザーがあります。
init(_:red:green:blue:opacity:) はそのひとつです。

ステップ 8/11

Circle()
    .fill(.indigo)

「.indigo」は Color.indigo を省略した書き方です。

fill(_:style:)

func fill<S>(
    _ content: S,
    style: FillStyle = FillStyle()
) -> some View where S : ShapeStyle


ステップ 9/11
fill(塗りつぶし)と foregroundColor はどちらも図形の色を指定します。
‼️「どちらも追加する図形の背景色を指定する図形ビューの修飾子です」とありますが、「背景色」の訳は次のbackgroundと紛らわしいです。
「ぬりつぶしを指定する Shape 用の修飾子です」。

こんどは黒い円と背景が .indigo です。

background(_:ignoresSafeAreaEdges:)

func background<S>(
    _ style: S,
    ignoresSafeAreaEdges edges: Edge.Set = .all
) -> some View where S : ShapeStyle


Circle()
    .background(.indigo)

このコードは fill も foregroundColor も指定していないので、ライトモードなら「黒」・ダークモードなら「白」で円を表示します。
ビューの背景を .indigo で塗りつぶして表示します。

ステップ 10/11

Capsule()
    .strokeBorder(.indigo, lineWidth: 10)
    .background(Capsule().fill(.pink))

は strokeBorder と background を設定しています。

よく見ると .background(Capsule().fill(.pink)) と単純な色ではなく、図形を渡して表示しています

ステップ 11/11
10/11 で追加した部分の説明です。

Shape の枠線表示と塗りつぶしは同時に設定できません。
このためこのようなテクニックが必要になります。


タスク:カスタム図形を作成する

「パス」を使ってカスタム図形を作成します。
ソースコードは「CreateAShapeView」に切り替わります。

SwiftUIのPath図形表示」で座標や関係する型を確認してください。

ステップ 1/7
「パス」は「Path」の英文ドキュメントにリンクしています。
コードは空の VStack なのでプレビューには何も表示されていません。
「CreateAShapeView」は通常のビューです。

ステップ 2/7
カスタム図形のため新しい struct を追加します。

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.minX, y: rect.minY))
        
        return path
    }
}

この型を「CreateAShapeView」の上に追加する指示です。
追加してもプレビューの表示はかわりません。

この「Triangle」型は Shape プロトコルに準拠しています。
func path(in rect: CGRect) -> Path」は Shape プロトコルのメソッドです。
このメソッドが図形のパスを返します。

var path = Path() で変数 path を初期化し
path.move(to: CGPoint(x: rect.minX, y: rect.minY)) で始点に移動しています。
ここまででは表示できる図形ではありません。

.minX と .minY は CGRect 型のプロパティです。

ステップ 3/7
説明文から「CGRect」の英文ドキュメントにリンクしています。
minX などのプロパティ部分の翻訳を引用します:

計算された幾何学的特性
var height: CGFloat 長方形の高さを返します。
var width: CGFloat 長方形の幅を返します。
var minX: CGFloat 長方形のx座標の最小値を返します。
var midX: CGFloat 長方形の中心を確立するx座標を返します。
var maxX: CGFloat 長方形のx座標の最大値を返します。
var minY: CGFloat 長方形のy座標の最小値を返します。
var midY: CGFloat 長方形の中心を確立するy座標を返します。
var maxY: CGFloat 長方形のy座標の最大値を返します。

XとYが付いたプロパティはそれぞれの座標を返します。

説明文から「コピー」ボタンでコードをコピーし「path.move」の下にペーストすると path は三角形になります。

path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))

ご自身で紙に描いて確認してください。
rectはプレビュー全体を占める四角形です(図形はできるだけ大きく表示することを思い出してください)。
始点は CGPoint(x: rect.minX, y: rect.minY)
次は  CGPoint(x: rect.minX, y: rect.maxY)
最後は CGPoint(x: rect.maxX, y: rect.maxY)

Xは横方向、Yは縦方向(上が原点で最大なら下の座標になります)です。
どんな形か予想してください。

x座標とy座標で一つの点です。
三つの点それぞれの位置を予想することはカスタム図形のコード作成に欠かせません。

ステップ 4/7
まだプレビューで表示する CreateAShapeView の body は空です。

Triangle()
    .foregroundColor(.pink)

をコピーして VStack の中にペーストする指示があります。

このように表示されます。
予想どおりでしたか?

Mac のプレビュー画面は四隅が角丸なので三角形の頂点が表示されない状態です。

ステップ 5/7
次は曲線を追加です。

path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.maxY),
                  control: CGPoint(x: rect.midX, y: rect.midY))

addQuadCurve(to:control:) は制御点が一つのカーブを描きます。

mutating func addQuadCurve(
    to p: CGPoint,
    control cp: CGPoint
)

直前の addLine と追加したカーブに注目してください。

path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.maxY),
                  control: CGPoint(x: rect.midX, y: rect.midY))

カーブの始点は直前の addLine の CGPoint(x: rect.minX, y: rect.maxY) です。
カーブの終点は to: パラメーターの CGPoint(x: rect.maxX, y: rect.maxY) です。

control: パラメーターの CGPoint(x: rect.midX, y: rect.midY) はx座標もy座標も中央で、プレビュー領域の中央(対角線の交点)です。
この制御点に引っ張られるようなカーブを描きます。

ステップ 6/7
説明に従ってもうひとつのカーブを追加しましょう。

path.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.minY),
                  control: CGPoint(x: rect.maxX, y: rect.midY))

カーブの終点は to: パラメーターの CGPoint(x: rect.minX, y: rect.minY)) です。
この点は path の始点ですね。

制御点の座標は CGPoint(x: rect.maxX, y: rect.midY) です。
この座標はどこかわかりますか?

カーブは2番目の path.addLine あとに追加しました。
path.addLineの終点と一つ目のカーブの終点を見比べてください。

このカーブの追加すると、二つ目の path.addLine は不要になります
二つ目の path.addLine をコメントにしてもプレビューの表示は変わりません。

ステップ 7/7
最後に回転とサイズの指定を追加です。

.rotationEffect(Angle(degrees: 270))
.frame(width: 300, height: 200)

rotationEffect(_:anchor:)

func rotationEffect(
    _ angle: Angle,
    anchor: UnitPoint = .center
) -> some View

Angle型については「SwiftUIのPath図形表示」の「5-1 Angle型」を参照してください。

.rotationEffect(Angle(degrees: -90)) でも同じです。


タスク:図形を作成する

「作成済みビュー」は英語環境では「composed views」と表示しています。

ステップ 1/11
このステップでは最初からプレビューに円を表示しています。
コードを確認してください。

ZStack 内で ForEach で arcCount の数だけ Circle を表示しています。

ステップ 2/11
虹を表示するための方針が書かれています。

ステップ 3/11
「InsettableShape」プロトコルの英文ドキュメントにリンクしています。

「.inset(by: CGFloat(arc) * 25 )」をコピーボタンでコピーして「Circle()」の下にペーストしてください。

‼️「inset(by:)識別子」と書かれていますが「inset(by:)修飾子」の間違いと思われます。
‼️「この修飾子をCircle()(円)の下に追加します()`:」とありますが最後の「()`」は不要です。無視してください。

InsettableShape

inset(by:)

コピーペーストしたコード
.inset(by: CGFloat(arc) * 25 )
は ForEach で arc が 0 から 5 まで変化しながら繰り返します。
最も小さいものは 125 ポイント inset されます。

ただし色の指定がなくすべて同じに塗られているため、プレビューの表示は変わりません。

ステップ 4/11
「.trim(from: 0.5, to: 1.0)」をコードにペーストするとプレビューに半円を表示します。

trim(from:to:)
このモディファイアー(修飾子)は図形のパスの表示に利用する範囲を指定します。

.trim(from: 0.5, to: 1.0) の from を 0.1 から 0.9 まで変化させ確認してください。

トリムの確認』(この記事後半の有料部分)も参照してください。

ステップ 5/11
「colors」の説明です。
コードの対応部分が強調表示されるので確認してください。

ステップ 6/11
「.foregroundColor(colors[arc])」を追加してそれぞれの円に色を指定します。

コードを確認してください。
arc は ForEachで 0 から 5 まで変化します。
このため colors 配列から .red ~ .purple の各色を foregroundColor として設定しています。

塗りつぶされた円は inset 指定で少し小さな円に順に隠れます。
ドーナツ型を使わなくても、シンプルな円だけで虹を描くことに成功しています。

ステップ 7/11

‼️用語「overlay」のリンクする英文ドキュメントが違っています。
⚠️GraphicsContextのタイププロパティのドキュメントが開きます。
⚠️まったく別の物ですのでご注意ください

コードの overlay から「ヘルプ」で実際のドキュメントを表示できます。
overlay(_:alignment:)  iOS 16まで(Deprecated)

ステップ 8/11
2番目の VStack に黒い円を表示するコードを追加します。

Circle()
    .frame(width: 300)
    .foregroundColor(.black)

ステップ 9/11

.overlay(
    Circle()
        .frame(width: 200)
        .foregroundColor(.white))

ここでは「overlay(_:alignment:)」を使っています。
web のドキュメントでは「Deprecated」と表示されます【iOS 16 までは問題なく利用可能なので、Playgronds のドキュメントには「Deprecated」はまだ表示されていません】。
「Deprecated」は「非推奨」のことです。

同じドキュメントで使用が推奨されている「overlay(alignment:content:)」は iOS15 以降で利用できトレイリングクロージャーでの記述に適しています。
コピーペーストしたコードの一番外側の ( と ) を { と } に置き換えるだけで「overlay(alignment:content:)」を使った記述になります
(引数からトレイリングクロージャーに変更したわけです)

.overlay {
    Circle()
        .frame(width: 200)
        .foregroundColor(.white)}


ステップ 10/11
さらにコピーペーストで overlay を追加します。


ステップ 11/11
さらにサイズの小さな円を overlay を追加し完成です。


タスク:基本アニメーション

ここからアニメーションです。

ステップ 1/10
このステップでは VStack の中身は空なのでプレビューには何も表示されません。

ステップ 2/10
‼️説明文では「四角形」となっていますが、コードは「RoundedRectangle」(角丸四角形)です。

ステップ 3/10
「@State private var isScaled = false」を追加します。

「状態プロパティ」の用語説明例は「private」指定がありませんが、コピーするコードには含まれています。
@State 属性の場合は「private」も明記してください。

ステップ 4/10
「.scaleEffect(isScaled ? 1 : 0.5)」を追加します。
isScaled プロパティの値によって表示倍率を変更するコードです。
まだ isScaled を変更するコードはないため、初期値の false で表示倍率は 0.5 になります。

ステップ 5/10
「.animation(.easeInOut, value: isScaled)」を追加します。

animation(_:value:)

func animation<V>(
    _ animation: Animation?,
    value: V
) -> some View where V : Equatable

value引数の値を監視し、値が変更されるたびにアニメーションを適用します。

ステップ 6/10
用語「onTapGesture」をタップするとなぜか Playgrounds のドキュメントウインドウではなく、webブラウザーで「onTapGesture(count:perform:)」を開きます。

.onTapGesture {
    isScaled.toggle()
}

を追加した後に、プレビューの長方形をタップするとアニメーションをともなって拡大縮小します。


「.animation(.easeInOut, value: isScaled)」の「.easeInOut」部分を「.spring()」や「.linear(duration:)」に変更しアニメーションの違いを確認してください。

ステップ 7/10
アニメーションのきっかけの別の記述方法を試します。

.onTapGesture {
     withAnimation(.easeInOut) {
           isScaled.toggle()
    }
}

各ビューに animation(_:) モディファイアを指定するのではなく、状態の変化にアニメーションを指定するのが withAnimation( _: _:) 関数です。

withAnimation( _: _:) はビューのモディファイアー(修飾子)ではありません。

func withAnimation<Result>(
    _ animation: Animation? = .default,
    _ body: () throws -> Result
) rethrows -> Result

ステップ 8/10
もう一つのアニメーション、トランジションのため状態プロパティを追加します。

ステップ 9/10
追加した状態プロパティで円の表示(する・しない)を切り替えるコードです。

if isCircleShowing {
         Circle()
          .frame(maxHeight: 200)
          .foregroundColor(.mint)
          .transition(.slide)
}

transition(_:) モディファイアー

ステップ 10/10
「isCircleShowing.toggle()」を onTapGesture のコールバックに追加し、タップで状態が変わるようにします。
これでステップ 9/10 で追加したコードで transition(.slide) により円が消える時にスライドするようになります。

ここから先は

15,799字 / 45画像
この記事のみ ¥ 200〜

今後も記事を増やすつもりです。 サポートしていただけると大変はげみになります。