Xcode: 12.4, Swift: 5
SwiftUIのViewにおいて、作成したオブジェクトをドラッグし、そのドラッグ後の座標をリアルタイムに表示する方法を紹介します。ポイントとなるキーワードは以下のとおりです。
- @State
- .gesture
- onChanged
1. オブジェクトを作成(ここではCircle)
[ads]
<span class="hljs-keyword">import</span> SwiftUI
struct Sample: View {
var body: some View {
Circle()
.<span class="hljs-built_in">fill</span>(Color.<span class="hljs-built_in">blue</span>)
.frame(<span class="hljs-built_in">width</span>: <span class="hljs-number">80</span>, <span class="hljs-built_in">height</span>: <span class="hljs-number">80</span>)
}
}
<span class="hljs-keyword">import</span> SwiftUI
struct Sample: View {
var body: some View {
Circle()
.<span class="hljs-built_in">fill</span>(Color.<span class="hljs-built_in">blue</span>)
.frame(<span class="hljs-built_in">width</span>: <span class="hljs-number">80</span>, <span class="hljs-built_in">height</span>: <span class="hljs-number">80</span>)
}
}
import SwiftUI
struct Sample: View {
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 80, height: 80)
}
}

特に指定していないため、画面中央に表示される。
2. @State変数を追加
[ads]
<span class="hljs-selector-tag">import</span> <span class="hljs-selector-tag">SwiftUI</span>
<span class="hljs-selector-tag">struct</span> <span class="hljs-selector-tag">Sample</span>: <span class="hljs-selector-tag">View</span> {
<span class="hljs-variable">@State</span> private var <span class="hljs-attribute">location</span>: CGPoint = CGPoint(<span class="hljs-attribute">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attribute">y</span>: <span class="hljs-number">0</span>)
var <span class="hljs-attribute">body</span>: some View {
<span class="hljs-selector-tag">VStack</span> {
<span class="hljs-selector-tag">Circle</span>()
<span class="hljs-selector-class">.fill</span>(Color.blue)
<span class="hljs-selector-class">.frame</span>(<span class="hljs-attribute">width</span>: <span class="hljs-number">80</span>, <span class="hljs-attribute">height</span>: <span class="hljs-number">80</span>)
<span class="hljs-selector-class">.position</span>(location)
}
}
}
<span class="hljs-selector-tag">import</span> <span class="hljs-selector-tag">SwiftUI</span>
<span class="hljs-selector-tag">struct</span> <span class="hljs-selector-tag">Sample</span>: <span class="hljs-selector-tag">View</span> {
<span class="hljs-variable">@State</span> private var <span class="hljs-attribute">location</span>: CGPoint = CGPoint(<span class="hljs-attribute">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attribute">y</span>: <span class="hljs-number">0</span>)
var <span class="hljs-attribute">body</span>: some View {
<span class="hljs-selector-tag">VStack</span> {
<span class="hljs-selector-tag">Circle</span>()
<span class="hljs-selector-class">.fill</span>(Color.blue)
<span class="hljs-selector-class">.frame</span>(<span class="hljs-attribute">width</span>: <span class="hljs-number">80</span>, <span class="hljs-attribute">height</span>: <span class="hljs-number">80</span>)
<span class="hljs-selector-class">.position</span>(location)
}
}
}
import SwiftUI
struct Sample: View {
@State private var location: CGPoint = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
Circle()
.fill(Color.blue)
.frame(width: 80, height: 80)
.position(location)
}
}
}

.positionモディファイアを使用し、@State変数にバインディングすることで、この@State変数であるlocationの変化にサークルオブジェクトが追従するようになります。
3. 座標表示用のテキストボックス追加
[ads]
<span class="hljs-selector-tag">import</span> <span class="hljs-selector-tag">SwiftUI</span>
<span class="hljs-selector-tag">struct</span> <span class="hljs-selector-tag">Sample</span>: <span class="hljs-selector-tag">View</span> {
<span class="hljs-variable">@State</span> private var <span class="hljs-attribute">location</span>: CGPoint = CGPoint(<span class="hljs-attribute">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attribute">y</span>: <span class="hljs-number">0</span>)
var <span class="hljs-attribute">body</span>: some View {
<span class="hljs-selector-tag">VStack</span> {
<span class="hljs-selector-tag">Text</span>(<span class="hljs-string">"x: \(location.x)"</span>)
<span class="hljs-selector-tag">Text</span>(<span class="hljs-string">"y: \(location.y)"</span>)
<span class="hljs-selector-tag">Circle</span>()
<span class="hljs-selector-class">.fill</span>(Color.blue)
<span class="hljs-selector-class">.frame</span>(<span class="hljs-attribute">width</span>: <span class="hljs-number">80</span>, <span class="hljs-attribute">height</span>: <span class="hljs-number">80</span>)
<span class="hljs-selector-class">.position</span>(location)
}
}
}
<span class="hljs-selector-tag">import</span> <span class="hljs-selector-tag">SwiftUI</span>
<span class="hljs-selector-tag">struct</span> <span class="hljs-selector-tag">Sample</span>: <span class="hljs-selector-tag">View</span> {
<span class="hljs-variable">@State</span> private var <span class="hljs-attribute">location</span>: CGPoint = CGPoint(<span class="hljs-attribute">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attribute">y</span>: <span class="hljs-number">0</span>)
var <span class="hljs-attribute">body</span>: some View {
<span class="hljs-selector-tag">VStack</span> {
<span class="hljs-selector-tag">Text</span>(<span class="hljs-string">"x: \(location.x)"</span>)
<span class="hljs-selector-tag">Text</span>(<span class="hljs-string">"y: \(location.y)"</span>)
<span class="hljs-selector-tag">Circle</span>()
<span class="hljs-selector-class">.fill</span>(Color.blue)
<span class="hljs-selector-class">.frame</span>(<span class="hljs-attribute">width</span>: <span class="hljs-number">80</span>, <span class="hljs-attribute">height</span>: <span class="hljs-number">80</span>)
<span class="hljs-selector-class">.position</span>(location)
}
}
}
import SwiftUI
struct Sample: View {
@State private var location: CGPoint = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
Text("x: \(location.x)")
Text("y: \(location.y)")
Circle()
.fill(Color.blue)
.frame(width: 80, height: 80)
.position(location)
}
}
}

座標表示用のテキストボックスを追加。
VStackで追加しているため、サークルの座標は(0, 0)のままだが、テキストボックスの分下方向にずれています。
4. gestureモディファイアを追加
[ads]
.gestureを追加することで、ジェスチャーを認識することができる。その一つである、onChangedイベントを利用する。Closureを用いて、このサークルオブジェクトのpositionモディファイアにバインディングされているlocation変数を、現在の座標で都度更新するような動作。
import SwiftUI
<span class="hljs-keyword">struct</span> Sample: View {
@State <span class="hljs-keyword">private</span> var location: CGPoint = <span class="hljs-constructor">CGPoint(<span class="hljs-params">x</span>: 0, <span class="hljs-params">y</span>: 0)</span>
var body: some View {
VStack {
<span class="hljs-constructor">Text(<span class="hljs-string">"x: \(location.x)"</span>)</span>
<span class="hljs-constructor">Text(<span class="hljs-string">"y: \(location.y)"</span>)</span>
<span class="hljs-constructor">Circle()</span>
.fill(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Color</span>.</span></span>blue)
.frame(width: <span class="hljs-number">80</span>, height: <span class="hljs-number">80</span>)
.position(location)
.gesture(<span class="hljs-constructor">DragGesture()</span>.on<span class="hljs-constructor">Changed({ <span class="hljs-params">value</span> <span class="hljs-params">in</span> <span class="hljs-params">self</span>.<span class="hljs-params">location</span> = <span class="hljs-params">value</span>.<span class="hljs-params">location</span>})</span>)
}
}
}
import SwiftUI
<span class="hljs-keyword">struct</span> Sample: View {
@State <span class="hljs-keyword">private</span> var location: CGPoint = <span class="hljs-constructor">CGPoint(<span class="hljs-params">x</span>: 0, <span class="hljs-params">y</span>: 0)</span>
var body: some View {
VStack {
<span class="hljs-constructor">Text(<span class="hljs-string">"x: \(location.x)"</span>)</span>
<span class="hljs-constructor">Text(<span class="hljs-string">"y: \(location.y)"</span>)</span>
<span class="hljs-constructor">Circle()</span>
.fill(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Color</span>.</span></span>blue)
.frame(width: <span class="hljs-number">80</span>, height: <span class="hljs-number">80</span>)
.position(location)
.gesture(<span class="hljs-constructor">DragGesture()</span>.on<span class="hljs-constructor">Changed({ <span class="hljs-params">value</span> <span class="hljs-params">in</span> <span class="hljs-params">self</span>.<span class="hljs-params">location</span> = <span class="hljs-params">value</span>.<span class="hljs-params">location</span>})</span>)
}
}
}
import SwiftUI
struct Sample: View {
@State private var location: CGPoint = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
Text("x: \(location.x)")
Text("y: \(location.y)")
Circle()
.fill(Color.blue)
.frame(width: 80, height: 80)
.position(location)
.gesture(DragGesture().onChanged({ value in self.location = value.location}))
}
}
}

Live Previewにすることで、ドラッグイベントの変化が@State変数のlocationにリアルタイムに更新され、その値が同時にリアルタイムにテキストボックスに表示されるようになります

