【じっくりSw1ftUI60】実践編30〜第42章 SwiftUI でジェスチャー認識機能を使用する〜前回からゆーてるけど、
結論先出し
アニメーションやジェスチャは動画やったところで、自分で動かさないと肌感覚がわからないからサンプルコードを変えたりして動かしてみてね🕺
👉オイラは最初から動画ではやらないとゆーておる
さてと、前回
で、
アニメーションとトランジションの基本
については紹介したので〜〜〜今回は、タップなんかで遊べる
ジェスチャーの基本
についてやってく🕺
オイラの学び直しなんざ関係ないって人は
にサンプルコードなんかは載ってるみたいだからそこ見たらいいんじゃね
んだば、今回も早速
じっくり第42章を読んでく👓
概要は、タップなんかのことを書いてるだけなので〜〜〜
今回は、早速動かす
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
}
}
}
#Preview {
Essentials42ContentsView()
}
まずはいつもどおり、今回用の新規でSwiftUIdファイル用意して
まずは基本
ちょっとビュー単体で動きを見せたいので、print()は使わずに
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName: String = "りんごちゃん"
var body: some View {
Image(fruitsName).gesture(
TapGesture()
.onEnded{ _ in
fruitsName = "みかんちゃん"
}
)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じで
ジェスチャー=画面に何らかの操作が加わった時に変化させる
てイメージ
タップ自体を再利用させる
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
var body: some View {
let tapEnded = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
self.fruitsName2 = "みかんちゃん"
}
Image(fruitsName1)
.gesture(tapEnded)
Image(fruitsName2)
.gesture(tapEnded)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じで〜〜〜
さらに、タップを3回すると、ブドウだけみかんに変わるようにしたいときは〜〜〜
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じで独立させてあげて、countを入れるだけ〜〜〜
お次は、長押しちゃん
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture()
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じで3枚目を追加〜〜〜
長押しの間隔も指定できるみたいなので〜〜〜
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じで
import SwiftUI
struct Essentials42ContentsView: View {
var body: some View {
ScrollView{
E42GestureStandardView()
}
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 10)
//LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
#Preview {
Essentials42ContentsView()
}
10秒に変更して、最大距離をなしにして〜〜〜
ここでポイント①:スマホ時間に慣れた現代人はせっかちなので
10秒は流石に長い
おそらく3秒が限界
なので、3秒に戻すけど、この見方がアプリ開発では大事。
<使ってるエンドユーザーさんが一般的にどのくらいな間隔を長いと感じるか>
<和やスローライフを根付かせる目的のアプリであれば、逆に短すぎないか>
といった視点で、
長押しなんかの間隔も最適解はアプリによって違う
のでそこは意識してね〜〜〜
お次は、チェンジジェスチャ
ちょいと、これまでの作りだと、ピンチがうまく反応しないので〜〜〜
(スクロールビューの中に入れると相性が悪い様子
👉結構、スクロールビューが厄介だね👀💦)
struct Essentials42ContentsView: View {
var body: some View {
// ScrollView{
// E42GestureStandardView()
E42GestureMagnificationView()
// }
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
struct E42GestureMagnificationView: View {
@State private var fruitsName1: String = "りんごちゃん"
var body: some View {
let pinchGesture = MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ _ in
self.fruitsName1 = "バナナさん"
})
.onEnded{ _ in
self.fruitsName1 = "ブドウ先輩"
}
Image(fruitsName1)
.resizable()
.font(.largeTitle)
.gesture(pinchGesture)
.aspectRatio(contentMode: .fit)
}
}
#Preview {
Essentials42ContentsView()
}
てな感じにしておいて〜〜〜
スケールサイズでも遊べる
struct E42GestureMagnificationView: View {
@State private var fruitsName1: String = "りんごちゃん"
//スケールサイズ
@State private var scaleSize: CGFloat = 1.0
var body: some View {
let pinchGesture = MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ pinchSize in
self.scaleSize = pinchSize
self.fruitsName1 = "バナナさん"
})
.onEnded{ _ in
self.fruitsName1 = "ブドウ先輩"
}
Image(fruitsName1)
.resizable()
.font(.largeTitle)
.scaleEffect(scaleSize)
.gesture(pinchGesture)
.aspectRatio(contentMode: .fit)
Text("\(scaleSize)")
.font(.largeTitle)
.foregroundStyle(Color.green)
}
}
てな感じで
ここでポイント②:コールバックアクションの基本
ジェスチャに関する情報を含む DragGesture.Value インスタンス。
ジェスチャがバインドされている @GestureState プロパティへの参照。
ジェスチャに対応するアニメーションの現在の状態を含む Transaction オブジェクト。
DragGesture.Value インスタンスは特に便利で、次のプロパティが含まれる。
location (CGPoint) – ドラッグ ジェスチャの現在の位置。
predictedEndLocation (CGPoint) – ドラッグが停止した場合のドラッグの速度に基づいて予測される最終位置。
predictedEndTranslation (CGSize) – 現在のドラッグ速度に基づいて、ドラッグが停止した場合の最終的な移動を予測。
startLocation (CGPoint) – ドラッグ ジェスチャが開始された場所。
time (Date) – 現在のドラッグ イベントのタイムスタンプ。
translation (CGSize) – ドラッグ ジェスチャの開始から現在のイベントまでの合計変換 (基本的には開始位置から現在のドラッグ位置までのオフセット)。
てな感じらしい🧐
実際にドラッグジェスチャで遊んでみると、、、
struct E42DragGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
var body: some View {
let dragger = DragGesture()
.updating($offsetFruits){ draggerLocate, state,transaction in
state = draggerLocate.translation
}
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(dragger)
Text("\(offsetFruits)")
}
}
てな感じで〜〜〜
struct E42DragGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
var body: some View {
let dragger = DragGesture()
.updating($offsetFruits){ draggerLocate, state,transaction in
state = draggerLocate.translation
}
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(dragger)
Text("\(offsetFruits)")
Text("\(dragger)")
}
}
てな感じにすると、
静止画の記事だけだと、ここも画像の採取が難しいから、
実際の動きはコードを組んで自分で試してみてね〜〜〜〜
最後は、複合ジェスチャ
struct E42ComposingGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
//長押しの状態
@GestureState private var longPressState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.updating($longPressState){value,state,transaction in
state = value
}
.simultaneously(with: DragGesture())
.updating($offsetFruits){value, state, transaction in
state = value.second?.translation ?? .zero
}
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(longPressDragger)
Text("\(offsetFruits)")
Text("\(longPressDragger.state)")
}
}
てな感じで〜〜〜
締めくくりはドラッグ制御〜〜
struct E42DragEnabledGestureView: View {
@GestureState private var offsetApple: CGSize = .zero
@State private var dragEnabledState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.onEnded( { _ in
self.dragEnabledState = true
})
.sequenced(before: DragGesture())
.updating($offsetApple) { value, state, transaction in
switch value {
case .first(true):
print("長押しされた")
case .second(true, let drag):
state = drag?.translation ?? .zero
default: break
}
}
.onEnded { value in
self.dragEnabledState = false
}
return Image(systemName: "apple.logo")
.foregroundColor(dragEnabledState ? Color.green : Color.black)
.font(.largeTitle)
.offset(offsetApple)
.gesture(longPressDragger)
}
}
てな感じで
さてと、以上。
今回のコードまとめ
いつもの記事公開後を組み込まないと、表示されないように一部変更した〜〜〜
スクロールビューとの相性が悪いもので〜〜〜
struct Essentials42ContentsView: View {
var body: some View {
E42GestureStandardView()
E42GestureMagnificationView()
E42DragGestureView()
E42ComposingGestureView()
E42DragEnabledGestureView()
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
VStack{
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
}
struct E42GestureMagnificationView: View {
@State private var fruitsName1: String = "りんごちゃん"
//スケールサイズ
@State private var scaleSize: CGFloat = 1.0
var body: some View {
let pinchGesture = MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ pinchSize in
self.scaleSize = pinchSize
self.fruitsName1 = "バナナさん"
})
.onEnded{ _ in
self.fruitsName1 = "ブドウ先輩"
}
VStack{
Image(fruitsName1)
.resizable()
.font(.largeTitle)
.scaleEffect(scaleSize)
.gesture(pinchGesture)
.aspectRatio(contentMode: .fit)
Text("\(scaleSize)")
.font(.largeTitle)
.foregroundStyle(Color.green)
}
}
}
struct E42DragGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
var body: some View {
let dragger = DragGesture()
.updating($offsetFruits){ draggerLocate, state,transaction in
state = draggerLocate.translation
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(dragger)
Text("\(offsetFruits)")
Text("\(dragger)")
}
}
}
struct E42ComposingGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
//長押しの状態
@GestureState private var longPressState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.updating($longPressState){value,state,transaction in
state = value
}
.simultaneously(with: DragGesture())
.updating($offsetFruits){value, state, transaction in
state = value.second?.translation ?? .zero
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(longPressDragger)
Text("\(offsetFruits)")
Text("\(longPressDragger.state)")
}
}
}
struct E42DragEnabledGestureView: View {
@GestureState private var offsetApple: CGSize = .zero
@State private var dragEnabledState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.onEnded( { _ in
self.dragEnabledState = true
})
.sequenced(before: DragGesture())
.updating($offsetApple) { value, state, transaction in
switch value {
case .first(true):
print("長押しされた")
case .second(true, let drag):
state = drag?.translation ?? .zero
default: break
}
}
.onEnded { value in
self.dragEnabledState = false
}
return Image(systemName: "apple.logo")
.foregroundColor(dragEnabledState ? Color.green : Color.black)
.font(.largeTitle)
.offset(offsetApple)
.gesture(longPressDragger)
}
}
#Preview {
Essentials42ContentsView()
}
Apple公式
さてと、次回は
ジェスチャまでやったところでビューに動きをつけるやり方にも大体触れたので〜〜〜
第43章 カスタマイズされた SwiftUI ProgressView の作成
をやってく🕺
記事公開後、
いつもどおり、
でやった操作を〜〜〜今回も画面の数が多いので一気に〜〜
以上。
サンプルコード
◾️Essentials42.swift
import SwiftUI
import WebKit
//タイトル
let essentialsChapter42NavigationTitle = "第42章"
let essentialsChapter42Title = "第42章 SwiftUI でジェスチャー認識機能を使用する"
let essentialsChapter42SubTitle = "第42章 SwiftUI でジェスチャー認識機能を使用する"
//コード
let codeEssentials42 = """
struct Essentials42ContentsView: View {
var body: some View {
E42GestureStandardView()
E42GestureMagnificationView()
E42DragGestureView()
E42ComposingGestureView()
E42DragEnabledGestureView()
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
VStack{
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
}
struct E42GestureMagnificationView: View {
@State private var fruitsName1: String = "りんごちゃん"
//スケールサイズ
@State private var scaleSize: CGFloat = 1.0
var body: some View {
let pinchGesture = MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ pinchSize in
self.scaleSize = pinchSize
self.fruitsName1 = "バナナさん"
})
.onEnded{ _ in
self.fruitsName1 = "ブドウ先輩"
}
VStack{
Image(fruitsName1)
.resizable()
.font(.largeTitle)
.scaleEffect(scaleSize)
.gesture(pinchGesture)
.aspectRatio(contentMode: .fit)
Text("\\(scaleSize)")
.font(.largeTitle)
.foregroundStyle(Color.green)
}
}
}
struct E42DragGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
var body: some View {
let dragger = DragGesture()
.updating($offsetFruits){ draggerLocate, state,transaction in
state = draggerLocate.translation
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(dragger)
Text("\\(offsetFruits)")
Text("\\(dragger)")
}
}
}
struct E42ComposingGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
//長押しの状態
@GestureState private var longPressState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.updating($longPressState){value,state,transaction in
state = value
}
.simultaneously(with: DragGesture())
.updating($offsetFruits){value, state, transaction in
state = value.second?.translation ?? .zero
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(longPressDragger)
Text("\\(offsetFruits)")
Text("\\(longPressDragger.state)")
}
}
}
struct E42DragEnabledGestureView: View {
@GestureState private var offsetApple: CGSize = .zero
@State private var dragEnabledState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.onEnded( { _ in
self.dragEnabledState = true
})
.sequenced(before: DragGesture())
.updating($offsetApple) { value, state, transaction in
switch value {
case .first(true):
print("長押しされた")
case .second(true, let drag):
state = drag?.translation ?? .zero
default: break
}
}
.onEnded { value in
self.dragEnabledState = false
}
return Image(systemName: "apple.logo")
.foregroundColor(dragEnabledState ? Color.green : Color.black)
.font(.largeTitle)
.offset(offsetApple)
.gesture(longPressDragger)
}
}
#Preview {
Essentials42ContentsView()
}
"""
//ポイント
let pointEssentials42 = """
1、スマホ時間に慣れた現代人はせっかちなので
10秒は流石に長い、おそらく3秒が限界
なので、3秒に戻すけど、この見方がアプリ開発では大事。
<使ってるエンドユーザーさんが一般的にどのくらいな間隔を長いと感じるか>
<和やスローライフを根付かせる目的のアプリであれば、逆に短すぎないか>
といった視点で、
長押しなんかの間隔も最適解はアプリによって違う
のでそこは意識してね〜〜〜
2、コールバックアクションの基本
ジェスチャに関する情報を含む DragGesture.Value インスタンス。
ジェスチャがバインドされている @GestureState プロパティへの参照。
ジェスチャに対応するアニメーションの現在の状態を含む Transaction オブジェクト。
◆DragGesture.Value インスタンスは特に便利で、次のプロパティが含まれる。
◆location (CGPoint) – ドラッグ ジェスチャの現在の位置。
◆predictedEndLocation (CGPoint) – ドラッグが停止した場合のドラッグの速度に基づいて予測される最終位置。
◆predictedEndTranslation (CGSize) – 現在のドラッグ速度に基づいて、ドラッグが停止した場合の最終的な移動を予測。
◆startLocation (CGPoint) – ドラッグ ジェスチャが開始された場所。
◆time (Date) – 現在のドラッグ イベントのタイムスタンプ。
◆translation (CGSize) – ドラッグ ジェスチャの開始から現在のイベントまでの合計変換 (基本的には開始位置から現在のドラッグ位置までのオフセット)。
てな感じらしい🧐
"""
//URL
let urlEssentials42 = "https://note.com/m_kakudo/n/n7e06f4f76e4c"
//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh42: Identifiable {
var id: Int
var title: String
var view: ViewEnumiOSApp17DevelopmentEssentialsCh42
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh42{
case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh42: [ListiOSApp17DevelopmentEssentialsCh42] = [
ListiOSApp17DevelopmentEssentialsCh42(id: 1, title: essentialsChapter42SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh42: View {
var body: some View {
VStack {
Divider()
List (dataiOSApp17DevelopmentEssentialsCh42) { data in
self.containedViewiOSApp17DevelopmentEssentialsCh42(dataiOSApp17DevelopmentEssentialsCh42: data)
}
.edgesIgnoringSafeArea([.bottom])
}
.navigationTitle(essentialsChapter42NavigationTitle)
.navigationBarTitleDisplayMode(.inline)
}
//タップ後に遷移先へ遷移させる関数
func containedViewiOSApp17DevelopmentEssentialsCh42(dataiOSApp17DevelopmentEssentialsCh42: ListiOSApp17DevelopmentEssentialsCh42) -> AnyView {
switch dataiOSApp17DevelopmentEssentialsCh42.view {
case .Sec1:
return AnyView(NavigationLink (destination: Essentials42()) {
Text(dataiOSApp17DevelopmentEssentialsCh42.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentialsCh42()
}
struct Essentials42: View {
var body: some View {
VStack{
TabView {
Essentials42ContentsView()
.tabItem {
Image(systemName: contentsImageTab)
Text(contentsTextTab)
}
Essentials42Code()
.tabItem {
Image(systemName: codeImageTab)
Text(codeTextTab)
}
Essentials42Points()
.tabItem {
Image(systemName: pointImageTab)
Text(pointTextTab)
}
Essentials42WEB()
.tabItem {
Image(systemName: webImageTab)
Text(webTextTab)
}
}
}
}
}
#Preview {
Essentials42()
}
struct Essentials42Code: View {
var body: some View {
ScrollView{
Text(codeEssentials42)
}
}
}
#Preview {
Essentials42Code()
}
struct Essentials42Points: View {
var body: some View {
ScrollView{
Text(pointEssentials42)
}
}
}
#Preview {
Essentials42Points()
}
struct Essentials42WebView: 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 Essentials42WEB: View {
private var url:URL = URL(string: urlEssentials42)!
var body: some View {Essentials42WebView(searchURL: url)
}
}
#Preview {
Essentials42WEB()
}
struct Essentials42ContentsView: View {
var body: some View {
E42GestureStandardView()
E42GestureMagnificationView()
E42DragGestureView()
E42ComposingGestureView()
E42DragEnabledGestureView()
}
}
struct E42GestureStandardView: View {
@State private var fruitsName1: String = "りんごちゃん"
@State private var fruitsName2: String = "ブドウ先輩"
@State private var fruitsName3: String = "バナナさん"
var body: some View {
let tapImage1Ended = TapGesture()
.onEnded{ _ in
self.fruitsName1 = "みかんちゃん"
}
let tapImage2Ended = TapGesture(count: 3)
.onEnded{ _ in
self.fruitsName2 = "みかんちゃん"
}
let tapImage3LongEnded = LongPressGesture(minimumDuration: 3,maximumDistance: 5)
.onEnded{ _ in
self.fruitsName3 = "ピーチ姫笑"
}
VStack{
Image(fruitsName1)
.gesture(tapImage1Ended)
Image(fruitsName2)
.gesture(tapImage2Ended)
Image(fruitsName3)
.gesture(tapImage3LongEnded)
}
}
}
struct E42GestureMagnificationView: View {
@State private var fruitsName1: String = "りんごちゃん"
//スケールサイズ
@State private var scaleSize: CGFloat = 1.0
var body: some View {
let pinchGesture = MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ pinchSize in
self.scaleSize = pinchSize
self.fruitsName1 = "バナナさん"
})
.onEnded{ _ in
self.fruitsName1 = "ブドウ先輩"
}
VStack{
Image(fruitsName1)
.resizable()
.font(.largeTitle)
.scaleEffect(scaleSize)
.gesture(pinchGesture)
.aspectRatio(contentMode: .fit)
Text("\(scaleSize)")
.font(.largeTitle)
.foregroundStyle(Color.green)
}
}
}
struct E42DragGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
var body: some View {
let dragger = DragGesture()
.updating($offsetFruits){ draggerLocate, state,transaction in
state = draggerLocate.translation
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(dragger)
Text("\(offsetFruits)")
Text("\(dragger)")
}
}
}
struct E42ComposingGestureView: View {
@State private var fruitsName1: String = "りんごちゃん"
//ドラッグの位置
@GestureState private var offsetFruits: CGSize = .zero
//長押しの状態
@GestureState private var longPressState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.updating($longPressState){value,state,transaction in
state = value
}
.simultaneously(with: DragGesture())
.updating($offsetFruits){value, state, transaction in
state = value.second?.translation ?? .zero
}
VStack{
Image(fruitsName1)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(offsetFruits)
.gesture(longPressDragger)
Text("\(offsetFruits)")
Text("\(longPressDragger.state)")
}
}
}
struct E42DragEnabledGestureView: View {
@GestureState private var offsetApple: CGSize = .zero
@State private var dragEnabledState: Bool = false
var body: some View {
let longPressDragger = LongPressGesture(minimumDuration: 2.0)
.onEnded( { _ in
self.dragEnabledState = true
})
.sequenced(before: DragGesture())
.updating($offsetApple) { value, state, transaction in
switch value {
case .first(true):
print("長押しされた")
case .second(true, let drag):
state = drag?.translation ?? .zero
default: break
}
}
.onEnded { value in
self.dragEnabledState = false
}
return Image(systemName: "apple.logo")
.foregroundColor(dragEnabledState ? Color.green : Color.black)
.font(.largeTitle)
.offset(offsetApple)
.gesture(longPressDragger)
}
}
#Preview {
Essentials42ContentsView()
}
◾️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
//じっくり60で追加
case Ch42
}
//各項目に表示する文字列
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),
//じっくり60で追加
ListiOSApp17DevelopmentEssentials(id: 42, title: essentialsChapter42Title, view: .Ch42),
]
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)
})
//じっくり60で追加
case .Ch42:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh42()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentials()
}
以上。
さてと、5連休も3日目
残り2日も無事に走り切ろう笑😝
こんな連休がないと、SwiftUIの記事は、量産できないからね〜〜〜