My situation is that i have two views, one view -> MoveView should be movable inside of the other view -> FrameView. But when moving the inner view, it can be moved outside the FrameView!? I hope all the Views didn't disturbed you ;D!
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var MoveViewLocation = CGPoint()
super.touchesBegan(touches , withEvent:event)
if let touch = touches.first as? UITouch {
MoveViewLocation = touch.locationInView(FrameView)
MoveView.center = MoveViewLocation
}
}
Related
I'm implementing a passThroughView by creating a transparent View on top and override hitTest().
passThroughView should consume touches from Apple Pencil and if touch type is not from pencil, it pass touches to the view underneath.
The problems are:
parameter "event" in hitTest contains no touch, so I can't check touch type in hitTest
I can get touch from touchesBegan and check touch type, but it get called only after hitTest returned true
I subclass UIWindow and override sendEvent() but this function also called after hitTest (and I don't know why)
class WindowAbleToKnowTouchTypes: UIWindow {
override func sendEvent(_ event: UIEvent) {
if event.type == .touches {
// This get called after Hittest
if event.allTouches!.first!.type == .pencil {
print("This touch is from Apple Pencil")
}
}
super.sendEvent(event)
}
}
Is there anyway to check touchType to decide to pass or consume touches?
I ended up using a different approach, it could be useful for many cases: If I can't get touchType in hitTest(), I can still get the touchType with GestureRecognize:
class CustomGestureRecognizer : ImmediatePanGesture {
var beganTouch : UITouch!
var movedTouch : UITouch!
var endedTouch : UITouch!
override func shouldReceive(_ event: UIEvent) -> Bool {
// You can check for touchType here and decide if this gesture should receice the touch or not
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let firstTouch = touches.first {
beganTouch = firstTouch
}
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let touch = touches.first {
movedTouch = touch
}
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let touch = touches.first {
endedTouch = touch
}
super.touchesEnded(touches, with: event)
}
}
In the target function of the gestureRecognizer, you can get the UITouch by:
let beganTouch = customGesture.beganTouch
let touchType = beganTouch.touchType
So I've been messing around trying to get the coordinates of touches on the screen. So far I can get the coordinates of one touch with this:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
println(location)
}
But when touching with two fingers I only get the coordinates of the first touch. Multi-touch works (I tested with this little tutorial: http://www.techotopia.com/index.php/An_Example_Swift_iOS_8_Touch,_Multitouch_and_Tap_Application). So my question is, how do I get the coordinates of the second (and third, fourth...) touch?
** Updated to Swift 4 and Xcode 9 (8 Oct 2017) **
First of all, remember to enable multi-touch events by setting
self.view.isMultipleTouchEnabled = true
in your UIViewController's code, or using the appropriate storyboard option in Xcode:
Otherwise you'll always get a single touch in touchesBegan (see documentation here).
Then, inside touchesBegan, iterate over the set of touches to get their coordinates:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self.view)
print(location)
}
}
the given touches argument is a set of detected touches.
You only see one touch because you select one of the touches with :
touches.anyObject() // Selects a random object (touch) from the set
In order to get all touches iterate the given set
for obj in touches.allObjects {
let touch = obj as UITouch
let location = touch.locationInView(self.view)
println(location)
}
You have to iterate over the different touches. That way you can access every touch.
for touch in touches{
//Handle touch
let touchLocation = touch.locationInView(self.view)
}
In Swift 1.2 this has changed, and touchesBegan now provides a Set of NSObjects.
To iterate through them, cast the touches collection as a Set of UITouch objects as follows:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touchSet = touches as! Set<UITouch>
for touch in touchSet{
let location = touch.locationInView(self.view)
println(location)
}
}
For Swift 3, based on #Andrew's answer :
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchSet = touches
for touch in touchSet{
let location = touch.location(in: self.view)
print(location)
}
}
EDIT, My bad, that's not answering your question, had the same problem and someone linked me to this previous answer :
Anyway, I had to change few things to make it works in swift 3, here is my current code :
var fingers = [String?](repeating: nil, count:5)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
for touch in touches{
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if finger == nil {
fingers[index] = String(format: "%p", touch)
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
for touch in touches {
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
for touch in touches {
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
fingers[index] = nil
break
}
}
}
}
I still have a little problem but I think it's linked to my GestureRecognizer in my code.
But that should do the trick.
It will print you the coordinate of each point in your consol.
In Swift 3,4
Identify touch pointer by its hash:
// SmallDraw
func pointerHashFromTouch(_ touch:UITouch) -> Int {
return Unmanaged.passUnretained(touch).toOpaque().hashValue
}
I have a subclass of SKNode called Player which pretty much consists of this: http://hub.ae/blog/2014/03/26/soft-body-physics-jellyusing-spritekit/ converted to swift (With a few changes). I've allowed the user to move the player node with his finger with the following code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
player.runAction(SKAction.moveTo(touch.locationInNode(world), duration: 1))
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
player.runAction(SKAction.moveTo(touch.locationInNode(world), duration: 1))
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
player.removeAllActions()
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
player.removeAllActions()
}
*As a note, the 'world' var you see is just another SKNode. 'player' is a child of this node.
This looks like it should work, however the node moves in very strange directions. This is an example of how it looks:
http://gyazo.com/87b0d09101bbbfd3ac0f2a3cdbf42e4c
How can I fix this? I found that settings the anchor point of the scene to 0.5 fixes this issue, however then the physics body of 'player' node gets messed up.
I had same problem but i can not remember if it is how i fixed it. Try changing this:
world > self
player.runAction(SKAction.moveTo(touch.locationInNode(world), duration: 1))
To:
player.runAction(SKAction.moveTo(touch.locationInNode(self), duration: 1))
If i am wrong let me know :)
EDIT
set anchor point to (0.5,0.5) and write those 2 methods to center on your node.
override func didSimulatePhysics() {
camera.position = CGPointMake(player.position.x, player.position.y);
centerOnNode(camera)
}
func centerOnNode(node: SKNode){
var positionInScene : CGPoint = convertPoint(node.position, fromNode: node.parent)
world.position = CGPointMake(world.position.x - positionInScene.x, world.position.y - positionInScene.y)
}
So I'm trying to move an object using the touches moved function, however I'm not sure how to do that when the coordinates are not predetermined. This is what I've got going :
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
var touch: AnyObject? = touches.anyObject()
var point = touch?.locationInView(self.view)
}
So basically I'm making the point variable the coordinates (points).
Then what needs to happen is that i move an object (in this case Label1) to that location:
func MoveObject(point) {
Label1.frame.origin = ( /* Here would the ´point´ be */ )
}
Also keep in mind that later when this problem is out of the way I'm going to change the location so its not where you touch but it moves accordingly. I don't know if that matters really but I'm just saying in case it does ;)
What about
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
var touch: AnyObject? = touches.anyObject()
var point = touch?.locationInView(self.view)
moveObject(point)
}
...
func MoveObject(point) {
Label1.center = point
}
Basically I need to create a UIScrollView in my SpriteKit project but i'm having a lot of problem adding SKButtons (custom SKNode class for button management). So I proceeded to create a scrollable SKNode with touch gesture but, obviously, this bar won't have the native UIScrollView acceleration: the feature I was looking for.
So tried to overtake this problem by adding a native UIScrollView and catch every change of position, like this:
using this code:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[blueNode setPosition:CGPointMake(blueNode.position.x, scrollerUI.contentOffset)];
}
This works right but foolishly I forgot that if I add buttons, the touch gesture won't recognize the button's touch event! (The UIScrollView has the priority).
Maybe is just a stupid question but I don't really know how to figure it out. Maybe programming my own acceleration methods?
I think I came up with a solution to handle touches. Since the UIScrollView eats all the touches to allow it to scroll you need to pass them to the SKScene. You can do that by subclassing UIScrollView:
class SpriteScrollView: UIScrollView {
var scene: SKScene?
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
scene?.touchesBegan(touches, withEvent: event)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
scene?.touchesMoved(touches, withEvent: event)
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
scene?.touchesCancelled(touches, withEvent: event)
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
scene?.touchesEnded(touches, withEvent: event)
}
}
Then your scene needs to see if the touch hits any nodes and appropriately send the touches to those nodes. So in your SKScene add this:
var nodesTouched: [AnyObject] = []
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
let location = (touches.first as! UITouch).locationInNode(self)
nodesTouched = nodesAtPoint(location)
for node in nodesTouched as! [SKNode] {
node.touchesBegan(touches, withEvent: event)
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
for node in nodesTouched as! [SKNode] {
node.touchesMoved(touches, withEvent: event)
}
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
for node in nodesTouched as! [SKNode] {
node.touchesCancelled(touches, withEvent: event)
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
for node in nodesTouched as! [SKNode] {
node.touchesEnded(touches, withEvent: event)
}
}
Note this is for single touches and does not handle errors well, you'll need to wrap some of the forced down casting in if statements if you want to do this properly
I hope this helps someone even though the question is months old.
One possible solution would be to add tap gesture to scene:
override func didMoveToView(view: SKView) {
let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapped:"))
view.addGestureRecognizer(tap)
}
func tapped(tap : UITapGestureRecognizer) {
let viewLocation = tap.locationInView(tap.view)
let location = convertPointFromView(viewLocation)
let node = nodeAtPoint(location)
if node.name == "lvlButton" {
var button = node as! SKButton
button.touchBegan(location)
}
}