SwiftUI

[SwiftUI]オブジェクトのドラッグを可能にし、移動した座標を表示する. How to drag an object and display its position in SwiftUI.

SwiftUI

Xcode: 12.4, Swift: 5

SwiftUIのViewにおいて、作成したオブジェクトをドラッグし、そのドラッグ後の座標をリアルタイムに表示する方法を紹介します。ポイントとなるキーワードは以下のとおりです。

  • @State
  • .gesture
  • onChanged

1. オブジェクトを作成(ここではCircle)

[ads]
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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)
    }
}
インプリした青の80*80のサークルオブジェクトが表示される。
特に指定していないため、画面中央に表示される。

2. @State変数を追加

[ads]
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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]
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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変数を、現在の座標で都度更新するような動作。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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にリアルタイムに更新され、その値が同時にリアルタイムにテキストボックスに表示されるようになります

すこし動かした状態
ドラッグに追従して座標が変化していることがわかります
Ads