-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathJoystickView.swift
126 lines (100 loc) · 4.23 KB
/
JoystickView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import PlaygroundSupport
import SwiftUI
struct JoystickView: View {
struct framePreferenceKey: PreferenceKey {
static var defaultValue = CGRect()
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
init(offset: Binding<CGSize>, isTapped: Binding<Bool>, maxRadius: CGFloat){
self._offset = offset
self._isTapped = isTapped
self.maxRadius = maxRadius
}
let maxRadius : CGFloat
@Binding var offset: CGSize
@Binding var isTapped: Bool
@State var gestureLocation = CGSize(width: 0, height: 0)
@State var startLocation = CGSize(width: 0, height: 0)
@State var joystickFrame = CGRect()
var body: some View{
ZStack{
//Rectangle().frame(width: 500, height: 500)
if isTapped{
ZStack{
Circle()
.frame(width: maxRadius * 3, height: maxRadius * 3)
.opacity(0.5)
.offset(startLocation)
Circle().frame(width: maxRadius, height: maxRadius)
.offset(gestureLocation)
}
}
Color.clear
.contentShape(Rectangle())
.gesture(
DragGesture(minimumDistance:0, coordinateSpace:.global)
.onChanged { gesture in
if !isTapped {
isTapped = true
let startWidth = gesture.startLocation.x - joystickFrame.minX - joystickFrame.width/2
let startHeight = gesture.startLocation.y - joystickFrame.minY - joystickFrame.height/2
startLocation = CGSize(width: startWidth, height: startHeight)
}
var x = gesture.translation.width
var y = gesture.translation.height
var r = sqrt(pow(x, 2) + pow(y, 2))
if r > maxRadius{
let q = maxRadius / r
x *= q
y *= q
}
let gestLocX = gesture.startLocation.x + x - joystickFrame.minX - joystickFrame.width/2
let gestLocY = gesture.startLocation.y + y - joystickFrame.minY - joystickFrame.height/2
offset = CGSize(width: x, height: y)
gestureLocation = CGSize(width: gestLocX, height: gestLocY)
}
.onEnded { _ in
offset = .zero
isTapped = false
}
)
.overlay(
GeometryReader { geo in
Color.clear
.preference(key: framePreferenceKey.self, value: geo.frame(in:.global))
}.onPreferenceChange(framePreferenceKey.self){self.joystickFrame = $0}
)
}.clipShape(Rectangle())
}
}
//
//
//Use Example
//
//
struct TestView: View {
let radius : CGFloat = 50
@State var offset = CGSize(width: 0, height: 0)
@State var isTapped = false
var body: some View{
HStack{
VStack{
VStack{
Text("X: \(offset.width)")
Text("Y: \(offset.height)")
if isTapped{ Text("isTapped")} else { Text("notTapped")}
}
Rectangle()
.fill(Color.blue)
.overlay(
JoystickView(offset: $offset, isTapped: $isTapped, maxRadius: radius)
.frame(width: 300, height: 300)
.opacity(0.5)
, alignment: .bottomTrailing)
}
}
}
}
PlaygroundPage.current.setLiveView(TestView())