I'm trying to implement 2 distinct touch based behaviours. The first behaviour's logic is like this:
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
moveViewTo(touchLocation) //moves my view to touchLocation
}
Now, for my second behaviour, I want to rotate the view based on where I have my finger on the screen. For this, I tried this:
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
rotateViewTo(touchLocation) //rotates my view to face touchLocation
}
Now, I want these two distinct behaviours to work concurrently. In particular, I want the first touch to change the position of the view, and the second touch to rotate the view.
Rotating is possible only if there are two touches in the view, and changes direction based on the second touch.
Is there any way to differentiate which of the touches are first and which are second? I can't find a way to differentiate from the touches: Set<NSObject> because by nature Set is an unordered collection.
I believe your only option is to store your first touch as a var. My swift isn't strong enough to write out the code for you but here are the steps.
Loop through your touches instead of just grabbing the first one.
Check if there is a firstTouch (your var for tracking first touch)
If there isn't a firstTouch assign the first touch and do your position logic
If there is a first touch check to see if each touch is the firstTouch. If it isn't then do your rotation logic.
On touches ended and canceled loop through each touch. If it is the firstTouch set your var to nil.
Sorry I can't write all the code out for you, but hopefully that gets you going in the right direction.
Related
I am currently working on a game in SpriteKit, where I need to move a sprite in response to touch (i.e when user swipes or pans anywhere in SKView.
I want to get the direction of pan (for swipe I know how to do it),so that the sprite will move according to pan (I have a path defined for the sprite if user pans or according to swipe if user swipes), the way touch in iOS appdrawer works i.e it responds to slightest of swipes and also pans (i.e when you pan forwards or backwards, it makes a decision whether you want to move to the next screen or not).
Is there any documentation or so? (I have gone through the UIGestureRecognizer documentation, but I haven't been able to find a way to implement it.)
I use something similar on my MenuScene, I have 3 pages setup that the user can scroll through to get various game data. But I don't want the slightest touch to move the screen, it would be to jarring for the user. So I just watch the finger movements in the Touches functions and check if the movement is greater that an amount I designate as the minimum move amount and if it is greater than I scroll the page. In your case you could handle it as; if it is greater than the minimum move amount treat as a pan else treat it as a swipe
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
initialTouch = touch.location(in: self.view!)
moveAmtY = 0
moveAmtX = 0
initialPosition = menuScroller.position
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let movingPoint: CGPoint = touch.location(in: self.view!)
moveAmtX = movingPoint.x - initialTouch.x
moveAmtY = movingPoint.y - initialTouch.y
//their finger is on the page and is moving around just move the scroller and parallax backgrounds around with them
//Check if it needs to scroll to the next page when they release their finger
menuScroller.position = CGPoint(x: initialPosition.x + moveAmtX, y: initialPosition.y)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//they havent moved far enough so just reset the page to the original position
if fabs(moveAmtX) > 0 && fabs(moveAmtX) < minimum_detect_distance {
resetPages()
}
//the user has swiped past the designated distance, so assume that they want the page to scroll
if moveAmtX < -minimum_detect_distance {
moveLeft()
}
else if moveAmtX > minimum_detect_distance {
moveRight()
}
}
I am working on an iOS app and I want to have the effect of an action while the user's finger slides into a particular area. I first thought UIButton with touch dragged in will work, but it still requires a touch before the "drag". So is there a way to do it? Thanks so much!
You could use UITouch with touch began. Have your particular area view as a UIView IBOutlet. Lets call it particularAreaView
Maybe something like this:
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
let touch = touches.allObjects[0] as UITouch //get the latest touch
let touchLocation = touch.locationInView(self.view)
let particularAreaViewFrame = self.view.convertRect(particularAreaView.frame, fromView: self.view)
//Check if touch began is in your particularAreaViewFrame
if CGRectContainsPoint(particularAreaViewFrame, touchLocation) {
tiggerYourActionHere()
}
}
If you need it to consider touches that is moving into the area, you will have to override touchesMoved: withEvent: as well. Then maybe refactor the above into a function that use in both places.
I recently updated to iOS10 and Swift 3. And know, whenever I run my app on an iOS10 device (iPhone6s in my case) there is a randomly occurring delay of the touchesBegan function. Sometimes this is accompanied with the following error message:
"Gesture: Failed to receive system gesture state notification before
next touch"
Interestingly my older iPhone5s running iOS9 does not reproduce this problem.
Usually strange UI delays on iOS have to do with threads. UI related methods on iOS and Mac are all meant to be called from the main thread only. Because the APIs are becoming highly asynchronous, with many methods taking a block callback, it's easy to accidentally do something UI-related from a background thread.
Make sure that any UI-related activity, whether it's updating an image, a text field, reloading a table view, etc, is always done from the main thread. If you're doing UI stuff within a block, you can wrap the UI parts inside dispatch_async(dispatch_get_main_queue(), ...).
I'm my experience, an inadvertent background-thread call to one of these methods can have an effect of "poisoning the well," causing subsequent weirdness such as the delayed touch events you're seeing.
I had the same issue where I simulate a touch and hold to steer a car right and left. Here was what fixed it for me.
Changing the follow from:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let node: SKNode = atPoint(location)
if node == self.player1CarRight {
self.player1CarRotateRightTouching = true
}
}
}
To:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first! as UITouch
let touchLocation = touch.location(in: self)
if player1CarRight.contains(touchLocation) {
self.player1CarRotateRightTouching = true;
}
}
Same change in touchesEnded:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first! as UITouch
let touchLocation = touch.location(in: self)
if player1CarRight.contains(touchLocation) {
self.player1CarRotateRightTouching = false
}
}
I ran into this behavior today, but perhaps for a different reason. My scenario involved having a UITapGestureRecognizer on a superview of the view that implements touchesBegan. Since the UITapGestureRecognizer was recognizing a tap, and because the default for cancelsTouchesInView on the tap recognizer is YES, once the recognizer recognized the touch, the views don't get any of the touch callbacks. However, if I touch and hold, the UITapGestureRecognizer fails to recognize the touch, and then the views get the callbacks. This results in what looks like a delayed call to touchesBegan.
I also noticed as part of this investigation that UIButtons get special treatment here. If a UITapGestureRecognizer is in play, it will not consider touches on UIButtons.
I am making a SpriteKit game where in order to begin the game, you need to hold two separate spots (SKShapeNodes) for 3 seconds. If you let go either finger or move either finger off a node, the game will not start. I have it working fine with 1 spot, but when I try to do 2 spots, I'm stuck. What is the simplest way to detect the 2 correct touches on the correct nodes?
This doesn't seem like a very uncommon situation, so if anyone knows the best way to handle this, I would appreciate the help.
Swift preferred, also.
Set multipleTouchEnabled to YES and use the touchesForView: method.
You can get more specific information on multi touch from the Apple Docs Multitouch Events.
The main idea is to have all touches when users provides any actions and operate them at the same time.
So, add 3 handlers to your Scene class:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
checkTouches((event?.allTouches())!)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
checkTouches((event?.allTouches())!)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
checkTouches((event?.allTouches())!)
}
In the checkTouches function you will see all touches with updated properties (like positions etc).
Simple example:
func checkTouches(touches: Set<UITouch>) {
// iterate over all touches
for t in touches {
let touch = t as UITouch
let touchLocation = touch.locationInNode(self)
if... <-- YOUR CODE HERE TO CHECK NODE NAME AND TOUCHED TIME
}
}
Using this approach you will be able to handle any changes simultaniously.
E.g. user may touch on your node, then move finger outside it and then move bach to this node.
Enjoy!
I am Using A UIImageView with a globe image,if i touch on any region Example if i touch on any African country,I would like to fire a touch event and show that it is a particular country tapped by the user.I approached the way to get he co-ordinates on the images,Please give me any suggestions how can i get the touched positions country wise.
Thanks in advance.
Put the Image in UIImageView of a ViewController in Portrait mode
implement
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
println(touch.locationInView(self.view))
}
result
(155.5,164.5)