![見出し画像](https://assets.st-note.com/production/uploads/images/150324128/rectangle_large_type_2_e8ddac1b0cc49810596a0249059810b0.png?width=1200)
【じっくりSw1ftUI59】実践編29〜第41章 SwiftUI アニメーションとトランジション(ここは自分で組み込みながら動作検証してね〜〜〜画像じゃ無理)
さてと、いつもの温泉にも入ってさっぱりしたところで〜〜〜前回、
で、
Shapeの基本
についてはやったので〜〜〜今回は
アニメーションとトランジションの基本
に入ってく🕺
毎度、オイラの学びなんざ関係ないって人は、
でサンプルなんかも載ってるみたいだし、そっちでやればいいんじゃね?(知らんけど)
んだば、今回も早速
じっくり第40章を読んでく👓
概要としては、
ま、ビューに動きを出すのがアニメーションやトランジションやっでね
てことを言いたいだけだから、
早速、動かす
まずは、
import SwiftUI
struct Essentials41ContentsView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
Essentials41ContentsView()
}
![](https://assets.st-note.com/img/1723352978006-Uu7inFmlZr.png)
今回用のSwiftUIファイルを追記したところから開始〜〜〜〜🕺
まずは、アニメーションの一番シンプルなやつ
struct Essentials41ContentsView: View {
var body: some View {
VStack{
Essentials41AnimationStandardView()
}
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1))
}
Text("タップ \(tapCount) 回目")
}
}
}
#Preview {
Essentials41ContentsView()
}
てな感じにして〜〜〜
![](https://assets.st-note.com/img/1723355547252-dJ60Dr8QfV.png)
これだと、9の倍数のタップ目で初期値に戻るね笑🤣
サンプルコードがそーなってるからしゃーない💦
でこれにスケールを入れてくと〜〜〜
struct Essentials41ContentsView: View {
var body: some View {
VStack{
Essentials41AnimationStandardView()
}
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1))
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
#Preview {
Essentials41ContentsView()
}
てな感じにすると
![](https://assets.st-note.com/img/1723356038627-j0MI1Rmse4.png)
![](https://assets.st-note.com/img/1723356074346-nGZqnSZYX9.png)
ここでポイント①:アニメーションの基本
linear – アニメーションは指定された期間、一定の速度で実行され、上記のコード例で宣言されたオプション。
easyOut – アニメーションは最初は速く始まり、シーケンスの終わりに近づくにつれて遅くなる。
easyIn – アニメーション シーケンスはゆっくりと始まり、終わりに近づくにつれて速度が上がる。
easyInOut – アニメーションはゆっくりと始まり、加速し、その後再び遅くなる。
てな感じらしい
さらに変化に富んだアニメーションにしたいなら
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
}
}
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1))
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1))
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
#Preview {
Essentials41ContentsView()
}
てな感じにして動かすと、、、
![](https://assets.st-note.com/img/1723356539187-NFOTQcTAgv.png)
画像では伝わらないので〜〜〜
ま、実際にここは自分でコードをコピペして動かして藁😝
スケールサイズなしで楽しみたいなら
struct Essentials41AnimationVarietyNoScaleView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1))
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
てな感じで、
![](https://assets.st-note.com/img/1723356782383-AXmmVXtEfd.png)
セーフエリアのはみ出しが嫌いな(オイラも日本人ではあるが、几帳面で生真面目な日本人に多い)人なら
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
Essentials41AnimationVarietyNoScaleView()
}
}
.safeAreaPadding()
}
}
でセーフエリアパッディングを入れるだけで
![](https://assets.st-note.com/img/1723356902420-S60PoUG7qN.png)
繰り返しも〜〜〜
struct Essentials41AnimationRepeatingView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("みかんちゃん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
![](https://assets.st-note.com/img/1723357214914-EDWh4IHlpy.png)
さらにAutoreverseで遊ぶと
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
Essentials41AnimationVarietyNoScaleView()
Essentials41AnimationRepeatingView()
Essentials41AnimationRepeatingAutoreverseFalseView()
Essentials41AnimationRepeatingAutoreverseTrueView()
}
}
.safeAreaPadding()
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1))
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1))
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyNoScaleView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1))
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("みかんちゃん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseFalseView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: false),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseTrueView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: true),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
#Preview {
Essentials41ContentsView()
}
![](https://assets.st-note.com/img/1723357470329-araSwoc7eM.png?width=1200)
かなり面白い動きに目視でもなってるね😛
明示アニメーション
struct Essentials41ExplicitAnimationView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{ withAnimation(.linear (duration:1.4)){
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
}
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("ブドウ先輩")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
#Preview {
Essentials41ContentsView()
}
てな感じでビューを追加して〜〜〜
![](https://assets.st-note.com/img/1723358237496-kSDR1iKcNf.png)
ジェスチャーなんかで組み合わせてさらに面白そうなこともできそうなので〜〜〜
struct Essentials41ExplicitAnimationTapGestureView: View {
@State private var redCircle = false
var body: some View {
Circle()
.fill(redCircle ? .red: .orange)
.frame(width: 200, height: 150)
.onTapGesture {
withAnimation{
redCircle.toggle()
}
}
}
}
てな感じにして、
![](https://assets.st-note.com/img/1723358614925-mPRZ5C5Gbk.png)
![](https://assets.st-note.com/img/1723358643474-a5nDLH43MN.png)
トグルを使ってアニメーションを効果的に〜〜〜
struct Essentials41AnimationStateAndBindingView: View {
@State private var visibility = false
var body: some View {
VStack{
Toggle(isOn: $visibility.animation(.linear(duration: 4))){
Text("桃からりんごへ")
}
.padding()
if visibility{
Image("りんごちゃん")
} else {
Image("ピーチ姫笑")
}
}
}
}
てな感じで
![](https://assets.st-note.com/img/1723359563951-lDNkWX1Ra8.png)
![](https://assets.st-note.com/img/1723359605161-c0ZQBntU4l.png)
りんごにゆっくり変わった〜〜〜
自動開始アニメーション
struct Essentials41AnimationAutomaticallyStartView: View {
@State private var rotate: Double = 0
@State private var isSpinning: Bool = true
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 2)
.foregroundColor(Color.blue)
.frame(width: 360, height: 360)
Image(systemName: "apple.logo")
.font(.largeTitle)
.offset(y: -180)
.rotationEffect(.degrees(isSpinning ? 0 : 360))
.animation(Animation.linear(duration: 10.5)
.repeatForever(autoreverses: false))
}
.onAppear() {
self.isSpinning.toggle()
}
}
}
てな感じで〜〜〜
![](https://assets.st-note.com/img/1723360761404-CjSOqkBsKy.png)
クルクル回っております笑👀💦
最後にトランジションを〜〜〜
struct Essentials41TransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.fadeAndMove)
//エクステンションを使うのでコメントアウト
//.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
}
}
}
}
extension AnyTransition{
static var fadeAndMove: AnyTransition{
AnyTransition.opacity.combined(with: .move(edge: .top))
}
}
てな感じで遊ばせておいて〜〜〜
![](https://assets.st-note.com/img/1723362393832-UW3JWjevrZ.png)
![](https://assets.st-note.com/img/1723362436095-17nu8S8p2B.png)
ま、画像では分からないけどねん
そして、非対称トランジション〜〜〜
struct Essentials41AsymmetricalTransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.asymmetric(insertion: .scale, removal: .move(edge: .bottom)))
}
}
}
}
![](https://assets.st-note.com/img/1723362617330-dWvRjejXSS.png)
![](https://assets.st-note.com/img/1723362651939-qVaYNp5xbC.png)
オフにすると、
![](https://assets.st-note.com/img/1723362684446-aT3fsF06V0.png)
以上。
今回のコードまとめ
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
Essentials41AnimationVarietyNoScaleView()
Essentials41AnimationRepeatingView()
Essentials41AnimationRepeatingAutoreverseFalseView()
Essentials41AnimationRepeatingAutoreverseTrueView()
Essentials41ExplicitAnimationView()
Essentials41ExplicitAnimationTapGestureView()
Essentials41AnimationStateAndBindingView()
Essentials41AnimationAutomaticallyStartView()
Essentials41TransitionView()
Essentials41AsymmetricalTransitionView()
}
}
.safeAreaPadding()
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyNoScaleView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("みかんちゃん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseFalseView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: false),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseTrueView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: true),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41ExplicitAnimationView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{ withAnimation(.linear (duration:1.4)){
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
}
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("ブドウ先輩")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41ExplicitAnimationTapGestureView: View {
@State private var redCircle = false
var body: some View {
Circle()
.fill(redCircle ? .red: .orange)
.frame(width: 200, height: 150)
.onTapGesture {
withAnimation{
redCircle.toggle()
}
}
}
}
struct Essentials41AnimationStateAndBindingView: View {
@State private var visibility = false
var body: some View {
VStack{
Toggle(isOn: $visibility.animation(.linear(duration: 4))){
Text("桃からりんごへ")
}
.padding()
if visibility{
Image("りんごちゃん")
} else {
Image("ピーチ姫笑")
}
}
}
}
struct Essentials41AnimationAutomaticallyStartView: View {
@State private var rotate: Double = 0
@State private var isSpinning: Bool = true
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 2)
.foregroundColor(Color.blue)
.frame(width: 360, height: 360)
Image(systemName: "apple.logo")
.font(.largeTitle)
.offset(y: -180)
.rotationEffect(.degrees(isSpinning ? 0 : 360))
.animation(Animation.linear(duration: 10.5)
.repeatForever(autoreverses: false),value: rotate)
}
.onAppear() {
self.isSpinning.toggle()
}
}
}
struct Essentials41TransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.fadeAndMove)
//エクステンションを使うのでコメントアウト
//.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
}
}
}
}
struct Essentials41AsymmetricalTransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.asymmetric(insertion: .scale, removal: .move(edge: .bottom)))
}
}
}
}
extension AnyTransition{
static var fadeAndMove: AnyTransition{
AnyTransition.opacity.combined(with: .move(edge: .top))
}
}
#Preview {
Essentials41ContentsView()
}
Apple公式
さてと次回は、
今回でTapGestureが出てきたところで、
第42章 SwiftUI でジェスチャー認識機能を使用する
をやってく〜〜〜
記事公開後、
いつもどおり、
でやった操作を〜〜〜
![](https://assets.st-note.com/img/1723363634210-01dmqa8cLu.png?width=1200)
![](https://assets.st-note.com/img/1723363672320-17EC9TEPez.png?width=1200)
![](https://assets.st-note.com/img/1723363651015-e8hUoe7KmI.png?width=1200)
![](https://assets.st-note.com/img/1723363689227-XRUMdIzCY2.png?width=1200)
サンプルコード
◆Essentials41.swift
import SwiftUI
import WebKit
//タイトル
let essentialsChapter41NavigationTitle = "第41章"
let essentialsChapter41Title = "第41章 SwiftUI アニメーションとトランジション"
let essentialsChapter41SubTitle = "第41章 SwiftUI アニメーションとトランジション"
//コード
let codeEssentials41 = """
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
Essentials41AnimationVarietyNoScaleView()
Essentials41AnimationRepeatingView()
Essentials41AnimationRepeatingAutoreverseFalseView()
Essentials41AnimationRepeatingAutoreverseTrueView()
Essentials41ExplicitAnimationView()
Essentials41ExplicitAnimationTapGestureView()
Essentials41AnimationStateAndBindingView()
Essentials41AnimationAutomaticallyStartView()
Essentials41TransitionView()
Essentials41AsymmetricalTransitionView()
}
}
.safeAreaPadding()
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \\(tapCount) 回目")
Text("サイズ \\(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \\(tapCount) 回目")
Text("サイズ \\(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyNoScaleView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
Text("タップ \\(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("みかんちゃん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10),value:rotate)
.padding()
}
Spacer()
Text("タップ \\(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseFalseView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: false),value:rotate)
.padding()
}
Spacer()
Text("タップ \\(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseTrueView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: true),value:rotate)
.padding()
}
Spacer()
Text("タップ \\(tapCount) 回目")
}
}
}
struct Essentials41ExplicitAnimationView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \\(turnShapeCount) 周目")
Spacer()
Button(action:{ withAnimation(.linear (duration:1.4)){
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
}
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("ブドウ先輩")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
}
Spacer()
HStack{
Text("タップ \\(tapCount) 回目")
Text("サイズ \\(scaleSize)")
}
}
}
}
struct Essentials41ExplicitAnimationTapGestureView: View {
@State private var redCircle = false
var body: some View {
Circle()
.fill(redCircle ? .red: .orange)
.frame(width: 200, height: 150)
.onTapGesture {
withAnimation{
redCircle.toggle()
}
}
}
}
struct Essentials41AnimationStateAndBindingView: View {
@State private var visibility = false
var body: some View {
VStack{
Toggle(isOn: $visibility.animation(.linear(duration: 4))){
Text("桃からりんごへ")
}
.padding()
if visibility{
Image("りんごちゃん")
} else {
Image("ピーチ姫笑")
}
}
}
}
struct Essentials41AnimationAutomaticallyStartView: View {
@State private var rotate: Double = 0
@State private var isSpinning: Bool = true
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 2)
.foregroundColor(Color.blue)
.frame(width: 360, height: 360)
Image(systemName: "apple.logo")
.font(.largeTitle)
.offset(y: -180)
.rotationEffect(.degrees(isSpinning ? 0 : 360))
.animation(Animation.linear(duration: 10.5)
.repeatForever(autoreverses: false),value: rotate)
}
.onAppear() {
self.isSpinning.toggle()
}
}
}
struct Essentials41TransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.fadeAndMove)
//エクステンションを使うのでコメントアウト
//.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
}
}
}
}
struct Essentials41AsymmetricalTransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.asymmetric(insertion: .scale, removal: .move(edge: .bottom)))
}
}
}
}
extension AnyTransition{
static var fadeAndMove: AnyTransition{
AnyTransition.opacity.combined(with: .move(edge: .top))
}
}
#Preview {
Essentials41ContentsView()
}
"""
//ポイント
let pointEssentials41 = """
◆linear – アニメーションは指定された期間、一定の速度で実行され、上記のコード例で宣言されたオプション。
◆easyOut – アニメーションは最初は速く始まり、シーケンスの終わりに近づくにつれて遅くなる。
◆easyIn – アニメーション シーケンスはゆっくりと始まり、終わりに近づくにつれて速度が上がる。
◆easyInOut – アニメーションはゆっくりと始まり、加速し、その後再び遅くなる。
"""
//URL
let urlEssentials41 = "https://note.com/m_kakudo/n/nfff7745f09db"
//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh41: Identifiable {
var id: Int
var title: String
var view: ViewEnumiOSApp17DevelopmentEssentialsCh41
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh41{
case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh41: [ListiOSApp17DevelopmentEssentialsCh41] = [
ListiOSApp17DevelopmentEssentialsCh41(id: 1, title: essentialsChapter41SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh41: View {
var body: some View {
VStack {
Divider()
List (dataiOSApp17DevelopmentEssentialsCh41) { data in
self.containedViewiOSApp17DevelopmentEssentialsCh41(dataiOSApp17DevelopmentEssentialsCh41: data)
}
.edgesIgnoringSafeArea([.bottom])
}
.navigationTitle(essentialsChapter41NavigationTitle)
.navigationBarTitleDisplayMode(.inline)
}
//タップ後に遷移先へ遷移させる関数
func containedViewiOSApp17DevelopmentEssentialsCh41(dataiOSApp17DevelopmentEssentialsCh41: ListiOSApp17DevelopmentEssentialsCh41) -> AnyView {
switch dataiOSApp17DevelopmentEssentialsCh41.view {
case .Sec1:
return AnyView(NavigationLink (destination: Essentials41()) {
Text(dataiOSApp17DevelopmentEssentialsCh41.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentialsCh41()
}
struct Essentials41: View {
var body: some View {
VStack{
TabView {
Essentials41ContentsView()
.tabItem {
Image(systemName: contentsImageTab)
Text(contentsTextTab)
}
Essentials41Code()
.tabItem {
Image(systemName: codeImageTab)
Text(codeTextTab)
}
Essentials41Points()
.tabItem {
Image(systemName: pointImageTab)
Text(pointTextTab)
}
Essentials41WEB()
.tabItem {
Image(systemName: webImageTab)
Text(webTextTab)
}
}
}
}
}
#Preview {
Essentials41()
}
struct Essentials41Code: View {
var body: some View {
ScrollView{
Text(codeEssentials41)
}
}
}
#Preview {
Essentials41Code()
}
struct Essentials41Points: View {
var body: some View {
ScrollView{
Text(pointEssentials41)
}
}
}
#Preview {
Essentials41Points()
}
struct Essentials41WebView: UIViewRepresentable {
let searchURL: URL
func makeUIView(context: Context) -> WKWebView {
let view = WKWebView()
let request = URLRequest(url: searchURL)
view.load(request)
return view
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
struct Essentials41WEB: View {
private var url:URL = URL(string: urlEssentials41)!
var body: some View {Essentials41WebView(searchURL: url)
}
}
#Preview {
Essentials41WEB()
}
struct Essentials41ContentsView: View {
var body: some View {
ScrollView{
VStack{
Essentials41AnimationStandardView()
Essentials41AnimationVarietyView()
Essentials41AnimationVarietyNoScaleView()
Essentials41AnimationRepeatingView()
Essentials41AnimationRepeatingAutoreverseFalseView()
Essentials41AnimationRepeatingAutoreverseTrueView()
Essentials41ExplicitAnimationView()
Essentials41ExplicitAnimationTapGestureView()
Essentials41AnimationStateAndBindingView()
Essentials41AnimationAutomaticallyStartView()
Essentials41TransitionView()
Essentials41AsymmetricalTransitionView()
}
}
.safeAreaPadding()
}
}
struct Essentials41AnimationStandardView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.linear(duration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("りんごちゃん")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41AnimationVarietyNoScaleView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("りんごちゃん")
.rotationEffect(.degrees(rotate))
.animation(.spring(response: 1, dampingFraction: 0.3,blendDuration: 0.1),value: rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("みかんちゃん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseFalseView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: false),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41AnimationRepeatingAutoreverseTrueView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
}
}){
Image("バナナさん")
.rotationEffect(.degrees(rotate))
.animation(Animation.linear(duration: 0.1).repeatCount(10,autoreverses: true),value:rotate)
.padding()
}
Spacer()
Text("タップ \(tapCount) 回目")
}
}
}
struct Essentials41ExplicitAnimationView: View {
@State private var rotate: Double = 0
@State private var tapCount: Int = 0
@State private var turnShapeCount: Int = 0
@State private var scaleSize: CGFloat = 1
var body: some View {
VStack{
Text("回転 \(turnShapeCount) 周目")
Spacer()
Button(action:{ withAnimation(.linear (duration:1.4)){
self.rotate = (self.rotate < 360 ? self.rotate + 45 : 0)
tapCount = tapCount + 1
}
self.scaleSize = (self.scaleSize < 3.1 ? self.scaleSize + 0.3 : 0)
if (tapCount % 9 == 0){
turnShapeCount = turnShapeCount + 1
scaleSize = 1
}
}){
Image("ブドウ先輩")
.scaleEffect(scaleSize)
.rotationEffect(.degrees(rotate))
}
Spacer()
HStack{
Text("タップ \(tapCount) 回目")
Text("サイズ \(scaleSize)")
}
}
}
}
struct Essentials41ExplicitAnimationTapGestureView: View {
@State private var redCircle = false
var body: some View {
Circle()
.fill(redCircle ? .red: .orange)
.frame(width: 200, height: 150)
.onTapGesture {
withAnimation{
redCircle.toggle()
}
}
}
}
struct Essentials41AnimationStateAndBindingView: View {
@State private var visibility = false
var body: some View {
VStack{
Toggle(isOn: $visibility.animation(.linear(duration: 4))){
Text("桃からりんごへ")
}
.padding()
if visibility{
Image("りんごちゃん")
} else {
Image("ピーチ姫笑")
}
}
}
}
struct Essentials41AnimationAutomaticallyStartView: View {
@State private var rotate: Double = 0
@State private var isSpinning: Bool = true
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 2)
.foregroundColor(Color.blue)
.frame(width: 360, height: 360)
Image(systemName: "apple.logo")
.font(.largeTitle)
.offset(y: -180)
.rotationEffect(.degrees(isSpinning ? 0 : 360))
.animation(Animation.linear(duration: 10.5)
.repeatForever(autoreverses: false),value: rotate)
}
.onAppear() {
self.isSpinning.toggle()
}
}
}
struct Essentials41TransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.fadeAndMove)
//エクステンションを使うのでコメントアウト
//.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
}
}
}
}
struct Essentials41AsymmetricalTransitionView: View {
@State private var isButtonVisible: Bool = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation(.linear(duration: 1.5))){
Text("果物ちゃんたち現れり🕺")
}
.padding()
if isButtonVisible {
Button(action:{}){
Text("🍎🍊🍌🍇🍑")
}
.font(.largeTitle)
.transition(.asymmetric(insertion: .scale, removal: .move(edge: .bottom)))
}
}
}
}
extension AnyTransition{
static var fadeAndMove: AnyTransition{
AnyTransition.opacity.combined(with: .move(edge: .top))
}
}
#Preview {
Essentials41ContentsView()
}
◆EssentialsMenu.swift
//フレームワーク
import SwiftUI
import WebKit
//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
var id: Int
var title: String
var view: ViewEnumiOSApp17DevelopmentEssentials
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
case Ch1
//じっくり13で追加
case Ch2
//じっくり14で追加
case Ch3
//じっくり15で追加
case Ch4
//じっくり16で追加
case Ch5
//じっくり17で追加
case Ch6
//じっくり18で追加
case Ch7
//じっくり19で追加
case Ch8
//じっくり20、21で追加
case Ch9
//じっくり22、23で追加
case Ch10
//じっくり24で追加
case Ch11
//じっくり25で追加
case Ch12
//じっくり26で追加
case Ch13
//じっくり27,28で追加
case Ch14
//じっくり29で追加
case Ch15
//じっくり31で追加
case Ch16
//じっくり32で追加
case Ch17
//じっくり33で追加
case Ch18
//じっくり34で追加
case Ch19
//じっくり35で追加
case Ch20
//じっくり36で追加
case Ch21
//じっくり37で追加
case Ch22
//じっくり40で追加
case Ch23
//じっくり41で追加
case Ch24
//じっくり43で追加
case Ch25
//じっくり44で追加
case Ch26
//じっくり45で追加
case Ch27
//じっくり46で追加
case Ch28
//じっくり47で追加
case Ch29
//じっくり48で追加
case Ch30
//じっくり49で追加
case Ch31
//じっくり50で追加
case Ch32
//じっくり51で追加
case Ch33
//じっくり52で追加
case Ch34
//じっくり53で追加
case Ch35
//じっくり54で追加
case Ch36
//じっくり55で追加
case Ch37
//じっくり56で追加
case Ch38
//じっくり57で追加
case Ch39
//じっくり58で追加
case Ch40
//じっくり59で追加
case Ch41
}
//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
//じっくり13で追加
ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
//じっくり13で追加
ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
//じっくり15で追加
ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
//じっくり16で追加
ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
//じっくり17で追加
ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
//じっくり18で追加
ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
//じっくり19で追加
ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
//じっくり20、21で追加
ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
//じっくり22、23で追加
ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
//じっくり24で追加
ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
//じっくり25で追加
ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
//じっくり26で追加
ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
//じっくり27,28で追加
ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
//じっくり29で追加
ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15),
//じっくり31で追加
ListiOSApp17DevelopmentEssentials(id: 16, title: essentialsChapter16Title, view: .Ch16),
//じっくり32で追加
ListiOSApp17DevelopmentEssentials(id: 17, title: essentialsChapter17Title, view: .Ch17),
//じっくり33で追加
ListiOSApp17DevelopmentEssentials(id: 18, title: essentialsChapter18Title, view: .Ch18),
//じっくり34で追加
ListiOSApp17DevelopmentEssentials(id: 19, title: essentialsChapter19Title, view: .Ch19),
//じっくり35で追加
ListiOSApp17DevelopmentEssentials(id: 20, title: essentialsChapter20Title, view: .Ch20),
//じっくり36で追加
ListiOSApp17DevelopmentEssentials(id: 21, title: essentialsChapter21Title, view: .Ch21),
//じっくり37で追加
ListiOSApp17DevelopmentEssentials(id: 22, title: essentialsChapter22Title, view: .Ch22),
//じっくり40で追加
ListiOSApp17DevelopmentEssentials(id: 23, title: essentialsChapter23Title, view: .Ch23),
//じっくり41で追加
ListiOSApp17DevelopmentEssentials(id: 24, title: essentialsChapter24Title, view: .Ch24),
//じっくり43で追加
ListiOSApp17DevelopmentEssentials(id: 25, title: essentialsChapter25Title, view: .Ch25),
//じっくり44で追加
ListiOSApp17DevelopmentEssentials(id: 26, title: essentialsChapter26Title, view: .Ch26),
//じっくり45で追加
ListiOSApp17DevelopmentEssentials(id: 27, title: essentialsChapter27Title, view: .Ch27),
//じっくり46で追加
ListiOSApp17DevelopmentEssentials(id: 28, title: essentialsChapter28Title, view: .Ch28),
//じっくり47で追加
ListiOSApp17DevelopmentEssentials(id: 29, title: essentialsChapter29Title, view: .Ch29),
//じっくり48で追加
ListiOSApp17DevelopmentEssentials(id: 30, title: essentialsChapter30Title, view: .Ch30),
//じっくり49で追加
ListiOSApp17DevelopmentEssentials(id: 31, title: essentialsChapter31Title, view: .Ch31),
//じっくり50で追加
ListiOSApp17DevelopmentEssentials(id: 32, title: essentialsChapter32Title, view: .Ch32),
//じっくり51で追加
ListiOSApp17DevelopmentEssentials(id: 33, title: essentialsChapter33Title, view: .Ch33),
//じっくり52で追加
ListiOSApp17DevelopmentEssentials(id: 34, title: essentialsChapter34Title, view: .Ch34),
//じっくり53で追加
ListiOSApp17DevelopmentEssentials(id: 35, title: essentialsChapter35Title, view: .Ch35),
//じっくり54で追加
ListiOSApp17DevelopmentEssentials(id: 36, title: essentialsChapter36Title, view: .Ch36),
//じっくり55で追加
ListiOSApp17DevelopmentEssentials(id: 37, title: essentialsChapter37Title, view: .Ch37),
//じっくり56で追加
ListiOSApp17DevelopmentEssentials(id: 38, title: essentialsChapter38Title, view: .Ch38),
//じっくり57で追加
ListiOSApp17DevelopmentEssentials(id: 39, title: essentialsChapter39Title, view: .Ch39),
//じっくり58で追加
ListiOSApp17DevelopmentEssentials(id: 40, title: essentialsChapter40Title, view: .Ch40),
//じっくり59で追加
ListiOSApp17DevelopmentEssentials(id: 41, title: essentialsChapter41Title, view: .Ch41),
]
struct iOSApp17DevelopmentEssentials: View {
var body: some View {
VStack {
Divider()
List (dataiOSApp17DevelopmentEssentials) { data in
self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
}
.edgesIgnoringSafeArea([.bottom])
}
.navigationTitle("iOS開発の章目次")
.navigationBarTitleDisplayMode(.inline)
}
//タップ後に遷移先へ遷移させる関数
func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
switch dataiOSApp17DevelopmentEssentials.view {
case .Ch1:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり13で追加
case .Ch2:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり13で追加
case .Ch3:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり15で追加
case .Ch4:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり16で追加
case .Ch5:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり17で追加
case .Ch6:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり18で追加
case .Ch7:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり19で追加
case .Ch8:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり20、21で追加
case .Ch9:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり22、23で追加
case .Ch10:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり24で追加
case .Ch11:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり25で追加
case .Ch12:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり26で追加
case .Ch13:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり27,28で追加
case .Ch14:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり29で追加
case .Ch15:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり31で追加
case .Ch16:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh16()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり32で追加
case .Ch17:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh17()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり33で追加
case .Ch18:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh18()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり34で追加
case .Ch19:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh19()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり35で追加
case .Ch20:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh20()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり36で追加
case .Ch21:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh21()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり37で追加
case .Ch22:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh22()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり40で追加
case .Ch23:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh23()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり41で追加
case .Ch24:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh24()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり43で追加
case .Ch25:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh25()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり44で追加
case .Ch26:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh26()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり45で追加
case .Ch27:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh27()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり46で追加
case .Ch28:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh28()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり47で追加
case .Ch29:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh29()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり48で追加
case .Ch30:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh30()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり49で追加
case .Ch31:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh31()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり50で追加
case .Ch32:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh32()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり51で追加
case .Ch33:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh33()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり52で追加
case .Ch34:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh34()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり53で追加
case .Ch35:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh35()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり54で追加
case .Ch36:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh36()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり55で追加
case .Ch37:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh37()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり56で追加
case .Ch38:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh38()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり57で追加
case .Ch39:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh39()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり58で追加
case .Ch40:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh40()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり59で追加
case .Ch41:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh41()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentials()
}
以上。
実際の動きが知りたければ、当たり前の話だけど、
(今回のコードをコピペでもいいから)
自分でちゃんとコードを組んで、プレビューで確認してみてね👀💦
元々、マガジンの最初に、
しおり機能なんかもないし、動画ではなく記事でやる
って予め断って始めてるから、
流石に、動画でやってほしいは少し甘え過ぎだと思う。。。
きちんと動くコードを無料で公開してもらってるんだから。
ま、そんなトチ狂ったクライアントが業務ではよく居るけど、
あくまでも趣味なので〜〜〜
この記事が気に入ったらサポートをしてみませんか?