Calculating the velocity of a swipe - ios

I'm trying to do something (print "Swiped") IF the velocity of a swipe is greater than a specified criteria. Based on researching I've come up with this code. "Began" gets printed whenever I touch the screen, but "Ended" and "Swiped" only print when I first tap and hold the screen, then drag, then lift up (a rather slow process). I hope i can just flick the screen and my text gets printed, but right now it will only print if i take my time with the swipe process. Not exactly a quick swipe.. you know? Hopefully this makes sense!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Began")
for touch in touches {
let location:CGPoint = touch.locationInView(self.view!)
start = location
startTime = touch.timestamp
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Ended")
for touch in touches {
let location:CGPoint = touch.locationInView(self.view!)
var dx:CGFloat = location.x - start!.x;
var dy:CGFloat = location.y - start!.y;
var magnitude:CGFloat = sqrt(dx*dx+dy*dy)
if (magnitude >= kMinDistance) {
print("OK")
var dt:CGFloat = CGFloat(touch.timestamp - startTime!)
if (dt > kMinDuration) {
var speed:CGFloat = magnitude / dt;
if (speed >= kMinSpeed && speed <= kMaxSpeed) {
print("Swiped")
}
}
}
}
}

Related

swipe gesture in touches methods in swift

So I already have gesture recongizers implemented into my game to detect the movement of my player but have found them to be not giving me the result I want, so I am looking at making the swipe gesture in the touches method and the taps also in the touches method. I have managed to make the tap functionality work in the touches method but I'm unable to implement the ability for swiping in the touches method and I can't seem to find a tutorial on how to do this. My code below shows the touches methods I'm using to try and achieve this:
class GameScene: SKScene {
var touchOrigin = CGPoint()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
backgroundColor = .black
player = SKSpriteNode(texture: nil, color: .orange, size: CGSize(width: 50, height: 50))
player.position = CGPoint(x: 0, y: 0)
addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
var currentTouchPosition = touch.location(in: self)
touchOrigin = touch.location(in: self)
if (Int(touchOrigin.x) > Int(currentTouchPosition.x)) {
player.position.x -= 50
} else if (Int(touchOrigin.x) < Int(currentTouchPosition.x)) {
player.position.x += 50
}
if touch.tapCount == 1 {
player.position.y += 50 //replace this with function :)
} else if touch.tapCount >= 2 {
player.position.y += 150 // change to run shield effect :)
}
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
How can I make the touches method recognise a swipe gesture in a certain direction? and if they swipe in a direction and not take their finger off the screen and swipe back to the origin point in one motion how can i make it so it is then recognised as a tap instead?
Here's an example of how to detect a swipe gesture.
First, define instance variables to store the starting location and time
var start:(location:CGPoint, time:TimeInterval)?
and define parameters of the swipe gesture. Adjust these accordingly:
let minDistance:CGFloat = 25
let minSpeed:CGFloat = 1000
let maxSpeed:CGFloat = 6000
In touchesBegan, save the location/time of each new touch event
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
start = (touch.location(in:self), touch.timestamp)
}
}
In touchesEnded, determine if the user's gesture was a swipe by comparing the distance and speed of the gesture. The tests for diagonal swipes are optional.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
var swiped = false
if let touch = touches.first, let startTime = self.start?.time,
let startLocation = self.start?.location {
let location = touch.location(in:self)
let dx = location.x - startLocation.x
let dy = location.y - startLocation.y
let distance = sqrt(dx*dx+dy*dy)
// Check if the user's finger moved a minimum distance
if distance > minDistance {
let deltaTime = CGFloat(touch.timestamp - startTime)
let speed = distance / deltaTime
// Check if the speed was consistent with a swipe
if speed >= minSpeed && speed <= maxSpeed {
// Determine the direction of the swipe
let x = abs(dx/distance) > 0.4 ? Int(sign(Float(dx))) : 0
let y = abs(dy/distance) > 0.4 ? Int(sign(Float(dy))) : 0
swiped = true
switch (x,y) {
case (0,1):
print("swiped up")
case (0,-1):
print("swiped down")
case (-1,0):
print("swiped left")
case (1,0):
print("swiped right")
case (1,1):
print("swiped diag up-right")
case (-1,-1):
print("swiped diag down-left")
case (-1,1):
print("swiped diag up-left")
case (1,-1):
print("swiped diag down-right")
default:
swiped = false
break
}
}
}
}
start = nil
if !swiped {
// Process non-swipes (taps, etc.)
print("not a swipe")
}
}

How to prevent sensitive UISlider triggering “Value Changed” on touching down and moving until after 10 pixels

The UISlider is extremely sensitive. For instance, when a user just touches down on a UISlider, in most cases, the slider moves and Value Changed is called.
I have the below class that aims to prevent/delay the slider from moving until 10 pixels, however it only works partially. Value Changed is still called on touching down, so initial movement isn't prevented.
1 - What different techniques can I employ to prevent the UISlider from being extra sensitive and moving only after a delay of 10 pixels are reached?
2 - How can I prevent the UISlider Value Changed triggering instantly when touching down and instead trigger only after at least 10 pixels of movement?
Code:
let DelayPoint:CGFloat = 10
class DelaySlider: UISlider {
var startPoint:CGPoint?
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if self.startPoint == nil {
self.startPoint = touches.first?.previousLocationInView(self)
}
if self.shouldAllowForSendActionForPoint((touches.first?.locationInView(self))!) {
super.touchesMoved(touches, withEvent: event)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.startPoint = nil
super.touchesEnded(touches, withEvent: event)
}
func shouldAllowForSendActionForPoint(location:CGPoint) -> Bool {
if self.startPoint != nil {
let xDiff = (self.startPoint?.x)! - location.x
let yDiff = (self.startPoint?.y)! - location.y
if (xDiff > DelayPoint || xDiff < -DelayPoint || yDiff > DelayPoint || yDiff < -DelayPoint) {
return true
}
}
return false
}
}

How can I avoid a delay with continues touch detection in swift?

I want control a character on my screen with a continuous swipe gesture. As soon as I do a swipe +x or -x my character moves left or right. And as long as I hold my finger it continuous to move to the direction in that I swiped. When I swipe into the opposite direction of my last swipe without leaving my finger from the touch ground, my character should move into this direction. When I release my finger from the touch screen, my character should stops instantly.
My problem is that there is often a small delay within the change of direction. And this delay makes it less precise.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
lastFingerPosition = location
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent){
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if(location.x > lastFingerPosition?.x){
movePlayer("right")
} else if(location.x < lastFingerPosition?.x)
movePlayer("left")
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if(touchedNode.name != "buttonFire"){
player?.removeAllActions()
lastMoveDirection = ""
}
}
}
func movePlayer(direction: String) {
var player = self.childNodeWithName("man")
if(lastMoveDirection != direction){
player?.removeAllActions()
lastMoveDirection = direction
var duration:CGFloat? = 0
var x = player?.position.x
duration = x!
if(direction == "left"){
var xDestination:CGFloat = 10
var run = SKAction.moveToX(10, duration: NSTimeInterval(Double(duration! / runSpeed)))
player?.runAction(run, withKey: "moveLeft")
} else if(direction == "right") {
duration = self.frame.width - x!
var xDestination = frame.size.width - 1
var run = SKAction.moveToX(xDestination, duration: NSTimeInterval(Double(duration! / runSpeed)))
player?.runAction(run, withKey: "moveRight")
}
}
}
I think it is not a programmatic problem. It is more an issue of the control itself. Because a swipe with your finger into one direction and then, a swipe to the opposite direction results in a rotation of your thumb.
And while it's rotating a little bit, it is not moving and that causes the feeling of a delay.
I guess this control is useless, if you need really precise moves - just because of the anatomy of our thumbs.

One touch moving in 2 points, but it is not moving

Please help!
I want to let my ball moving when the screen is touched to other position and on next touch to original position.
I only want to change X position the Y will be the same.
But when I touch the screen it is doing nothing.
This is the code, I have written for the ball.
override func didMoveToView(view: SKView) {
addball()
}
func addball() {
ball = SKSpriteNode(imageNamed: "Ball")
self.ball.position = CGPointMake(frame.size.width/4, frame.size.height/3)
self.addChild(ball)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
if touches.count % 2 == 0 {
let right = SKAction.moveToX(frame.size.width/4*3, duration: 0.1)
self.ball.runAction(right)
} else {
let left = SKAction.moveToX(frame.size.width/4, duration: 0.1)
self.ball.runAction(left)
}
}
}
Thanks for all answers:)
This may be a silly answer, but are you touching it with 2 fingers at the same time?
Try doing touches.count > 0 instead to test if it is just the touch.
If you are touching 1 finger, then another, your touch count will stay at 1 because only 1 touch began event happened.
The first touch will be part of the move state, the second touch is your begin state
Edit:
var toggle = false;
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
if toggle {
let right = SKAction.moveToX(frame.size.width/4*3, duration: 0.1)
self.ball.runAction(right)
} else {
let left = SKAction.moveToX(frame.size.width/4, duration: 0.1)
self.ball.runAction(left)
}
}
toggle = !toggle;
}

Making a rubbing screen gesture in iOS?

Basically, I want to make a mobile game using a custom hand gesture. The gesture is kind of like rubbing the screen, from left to right and from right to left.
If the gesture moves from the left to right, this will call a method and add a point. And once the gesture rubs from right to left, the user can also get one point.
But the problem is, when I use Swipe gesture recognizer, I have to release my finger from the screen, in order to call the method. If I just do the rub gesture, I am unable to call the method to add points.
Instead, I try the touchesBegan, touchesMoved method to detect the finger's position. However, touchesMoved will create many points to compare with the starting point, which leads to calling the methods many times instead of once.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches
{
self.touchStartPoint = touch.locationInView(self.myView).x
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches
{
self.touchOffsetPoint = touch.locationInView(self.myView).x - touchStartPoint
if tempTouchOffsetPoint < touchOffsetPoint
{
var xValueIncreaseArray: NSMutableArray = []
xValueIncreaseArray.addObject(touchOffsetPoint)
var maxValue: Double = (xValueIncreaseArray as AnyObject).valueForKeyPath("#max.self") as Double
println("\(maxValue)")
if maxValue - Double (self.touchStartPoint) > 50
{
println("right")
}
println("right")
}
else if tempTouchOffsetPoint > touchOffsetPoint
{
/* var xValueDecreaseArray: NSMutableArray = []
xValueDecreaseArray.addObject(touchOffsetPoint)*/
println("left")
}
else if tempTouchOffsetPoint == touchOffsetPoint
{
println("Remain")
}
tempTouchOffsetPoint = touchOffsetPoint
}
Are there any ways to detect the rubbing gesture? And every time the finger turns its direction, it will call a method only ONCE to add a score to user? Thank you so much!
This works for me:
let deadZone:CGFloat = 10.0
var previousTouchPoint: CGFloat!
var isMovingLeft:Bool? = nil
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()?.locationInView(self)
if let newPoint = point?.x {
if previousTouchPoint == nil {
println("Started")
previousTouchPoint = newPoint
} else {
// Check if they have moved beyond the dead zone
if (newPoint < (previousTouchPoint - deadZone)) || (newPoint > (previousTouchPoint + deadZone)) {
let newDirectionIsLeft = newPoint < previousTouchPoint
// Check if the direction has changed
if isMovingLeft != nil && newDirectionIsLeft != isMovingLeft! {
println("Direction Changed: Point")
}
println((newDirectionIsLeft) ? "Moving Left" : "Moving Right")
isMovingLeft = newDirectionIsLeft
previousTouchPoint = newPoint
}
}
}
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
previousTouchPoint = nil
isMovingLeft = nil
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
previousTouchPoint = nil
isMovingLeft = nil
}

Resources