I may have been misusing what GestureState was intended for.
I came up with another approach that seems easier for what I want.
This is what I want:
-
I have a SpriteView with a GameScene (subclassed from SKScene).
-
I want the SpriteView to handle the gestures (since it is way easier than the old approach).
-
I want the tapped buttonNode in the GameScene to track the location for highlighting and unhighlighting.
-
When the tracking ends, I want the tapped buttonNode to do its action if it is still being selected.
-
But I cannot attach the SwiftUI gestures to the SKNode buttonNodes of the GameScene since they are not Views.
-
Thus, I have to have a custom SwiftUI dragGesture to handle the above.
-
The major problem is to remember the originally touched buttonNode.
-
Instead of trying to use GestureState, I just use a locally captured variable within the "var" or "function" gesture.
Here is a modified version of what I do. The modification uses force unwrapping to keep it simple.
I put the dragGesture as part of the GameScene rather than as part of the file with the SpriteView.
This is because of the needs for my particular program.
The code can be simplified if you put it with the file with the SpriteView. For example, you can eliminate the guard statements for gameScene.
The major point of the example is to how to capture the "begin" state of the gesture without using GestureState.
extension GameScene {
var buttonNodeGesture: some Gesture {
var hitButtonNode:Button3Node?
return DragGesture(minimumDistance: 0)
.onChanged { [weak self] gesture in
guard let gameScene = self else { return }
let buttonNodes = gameScene.buttonNodes
let firstButtonNode = buttonNodes.first!
let parent = firstButtonNode.parent!
do { // updating stuff
if hitButtonNode == nil {
let startLocation = gameScene.convert(fromGesture: gesture.startLocation, to: parent)
hitButtonNode = gameScene.buttonNodes.filter{$0.contains(startLocation)}.first
}
}
do { // onChanged stuff
if let hitButtonNode = hitButtonNode {
let location = gameScene.convert(fromGesture: gesture.location, to: parent)
if hitButtonNode.contains(location) {
hitButtonNode.highlight()
}
else {
hitButtonNode.unhighlight()
}
}
}
}
.onEnded { [weak self] gesture in
guard let gameScene = self else { return }
let buttonNodes = gameScene.buttonNodes
let firstButtonNode = buttonNodes.first!
let parent = firstButtonNode.parent!
do { // onEnded stuff
if let hitButtonNode = hitButtonNode {
hitButtonNode.unhighlight()
let location = gameScene.convert(fromGesture: gesture.location, to: parent)
if hitButtonNode.contains(location) {
hitButtonNode.doButtonAction()
}
}
}
hitButtonNode = nil // Note: probably not needed
}
}
}