Skip to content

Instantly share code, notes, and snippets.

@koromiko
Created June 8, 2019 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save koromiko/50f2da60cc1d4d5ec1e16f180225a2ca to your computer and use it in GitHub Desktop.
Save koromiko/50f2da60cc1d4d5ec1e16f180225a2ca to your computer and use it in GitHub Desktop.
import PlaygroundSupport
import SwiftUI
enum MoveAction: String {
case left
case right
case up
case down
var systemImageName: String {
return "arrow.\(self.rawValue)"
}
}
class StateMachine<State, Action, C: Comparable> {
private var previousState: State?
func nextState(state: State) -> [Action] {
if let previous = previousState {
previousState = state
return toAction(previous: previous, current: state)
} else {
previousState = state
return []
}
}
private var rules: [(KeyPath<State, C>, (C, C)->Bool, Action)]
required init(rules: [(KeyPath<State, C>, (C, C)->Bool, Action)]) {
self.rules = rules
}
private func toAction(previous: State, current: State) -> [Action] {
var actions: [Action] = []
for rule in rules {
if rule.1(previous[keyPath: rule.0], current[keyPath: rule.0]) {
actions.append(rule.2)
}
}
return actions
}
}
var machine = StateMachine<CGPoint, MoveAction, CGFloat>(rules: [
(\.x, >, .left),
(\.x, <, .right),
(\.y, >, .up),
(\.y, <, .down),
])
class DraggableView: UIView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
}
}
enum DragState {
case dragging(CGSize)
case inactive
func translation() -> CGSize {
switch self {
case .dragging(let offset):
return offset
default:
return .zero
}
}
}
struct ContentView: View {
var stateMachine: StateMachine = {
return StateMachine<CGPoint, MoveAction, CGFloat>(rules: [
(\.x, >, .left),
(\.x, <, .right),
(\.y, >, .up),
(\.y, <, .down),
])
}()
@State var position: CGPoint = .zero
@State var imageName: String = MoveAction.up.systemImageName
@GestureState var dragState: DragState = .inactive
var body: some View {
let dragging = DragGesture()
.updating($dragState) { (value, state, _) in
state = .dragging(value.translation)
}
.onChanged { (value) in
if let action = self.stateMachine.nextState(state: value.location).first {
self.imageName = action.systemImageName
}
}
.onEnded { (value) in
self.position.x += value.translation.width
self.position.y += value.translation.height
}
return VStack {
Image(systemName: imageName)
Circle()
.frame(width: 100, height: 100, alignment: .center)
.foregroundColor(.gray)
.offset(x: position.x + dragState.translation().width, y: position.y + dragState.translation().height)
.gesture(dragging)
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment