If I am using the default camera control in SceneKit, is there a way to disable the two fingered pan function and leave the others as they are?
I want users to be able to rotate and zoom an object, but not move it at all.
The sceneView already has a UIPanGestureRecognizer attached.
If you modify its maximumNumberOfTouches to be 1, you can disable the pan gesture.
for reco in sceneView.gestureRecognizers! {
if let panReco = reco as? UIPanGestureRecognizer {
panReco.maximumNumberOfTouches = 1
}
}
No. You'll have to make your own camera and use gesture recognizers.
Related
Currently working on a game with scene kit in swift. I've got a joystick added to a SKScene, which is then attached to my scene kit game scene. Typical HUD overlay for controls.
let hudScene = SKScene(size: view.frame.size)
scnView.overlaySKScene = hudScene
I have also added a joystick to hudScene and want to add some buttons. Now, all of this works fine until I add a gesture recognizer to my main game scene. Once I do, all tap interaction with the hudScene is completely ignored. Even if I tap on the joystick and log what node was tapped at that location, it gives me the node in my game scene and completely ignores the nodes in hudScene.
Can anybody provide any insight into why this is happening?
For reference, the touch events for the joystick are implemented from this library, and my gesture recognizer is implimented like this:
let touchDownRec = UILongPressGestureRecognizer(target: self, action: #selector(didTouchDown(_:)) )
touchDownRec.minimumPressDuration = 0
touchDownRec.numberOfTouchesRequired = 1
scnView.addGestureRecognizer(touchDownRec)
I had the same problem... you have to replace the touchesBegan with a TapGesture, or replace the UILongPressGestureRecognizer with touchesEnded, but is more precise the first option.
I'm making an iOS game and need to know how to get the location of tap for uitapgesturerecognizer. I do NOT want to use touchesBegan, touchesEnded, etc. I also need to know how to check if a user double tapped the sprite node. I already have set up uitapgesturerecognizer's all I need to know is how to find the location of touch and see if it is the same location as a sprite node.
Any help is greatly appreciated!
Assuming your sprite is of a class that inherits from UIView (and I think they generally are) the handler function for the gesture recognizer should look like this.
func handleDoubleTap(gestureRecognizer: UITapGestureRecognizer) {
let tapLocation = gestureRecognizer.location(in: gestureRecognizer.view)
if mySprite.frame.contains(tapLocation){
print("tapped")
}
}
On iPhone's with 3D touch enabled, there is a feature where long pressing the left hand side of the screen with enough force opens lets you change which app is active. Because of this, when a non-moving touch happens on the left hand side of the screen, the touch event is delayed for a second or two until the iPhone verifies that the user is not trying to switch tasks and is interacting with the app.
This is a major problem when developing a game with SpriteKit, as these touches are delayed by a second every time a user taps/holds their finger on the left edge of the screen. I was able to solve this problem by registering a UILongPressGestureRecognizer in the main Scene of the game, thus disabling TouchesBegan and implementing a custom touches function (used as a selector by the gesture recognizer):
-(void)handleLongPressGesture:(UITapGestureRecognizer *)gesture {
CGPoint location = [gesture locationInView:self.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
//
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
//
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
//
}
else if (gesture.state == UIGestureRecognizerStateCancelled)
{
//
}
}
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGesture:)];
longPressGestureRecognizer.delaysTouchesBegan = false;
longPressGestureRecognizer.minimumPressDuration = 0;
[view addGestureRecognizer:longPressGestureRecognizer];
// continue
}
The problem with this is that I would have to implement a gesture recognizer for every touch (including simultaneous ones) that I expect the user to enter. This interferes with any touchesBegan methods as subclasses of SKSpriteNode, SKScene, etc. and kills a lot of functionality.
Is there any way to disable this delay? When registering the gestureRecognizer, I was able to set delaysTouchesBegan property to false. Can I do the same somehow for my SKScene?
To see this issue in action, you can run the default SpriteKit project, and tap (hold for a second or two) near the left hand side of the screen. You will see that there is a delay between when you touch the screen and when the SKShapeNodes are rendered (as opposed to touching anywhere else on the screen).
* Edit 1 *
For those trying to find a way to get around this for now, you can keep the gesture recognizer but set its cancelsTouchesInView to false. Use the gesture recognizer to do everything you need to do until TouchesBegan kicks in (touchesBegan will receive the same touch event about a second after the gesture recognizer recognizes the touch). Once touchesBegan kicks in, you can disable everything happening in the gesture recognizer. This seems like a sloppy fix to me, but it works for now.
Still trying to find a more-or-less formal solution.
I have experienced this as an user and it is really annoying. The only thing that worked for me was to disable the 3D touch. Otherwise the left side of the touchscreen is almost useless.
All,
I have studied Gesture Recognisers and I have placed one in Interface builder and I am going to wire it all up. I have also enabled 'User Interaction Enabled' in IB on the UIImage.
How can I make the UIImage larger according to how far the finger is swiped down on the screen.
How can I make the UIImage larger in the UIView that it sites in ?
This code works for me, I'm using UIPinchGestureRecognizer, which is use as a default if you want to scale the view:
#IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
recognizer.view!.transform = CGAffineTransformScale(recognizer.view!.transform, recognizer.scale, recognizer.scale)
// Reset recognizer scale
recognizer.scale = 1
}
If you want to use swipe gesture, you need to calculate swipe difference between start point and current swipe point and convert it to scale value you required.
In my view I'm overriding all the "touches*" methods to let the user draw on the screen. I'm recording the locations.
In addition I have two gesture recognizers on my view to detect single tap and double tap.
If I now move my finger just a little bit and short enough, I will be recording a small "draw" gesture. However when raising the finger, an additional tap gesture will be triggered.
By trial and error I could possibly figure out a minimum time and movement threshold but I'm sure there are smarter ways?
I need to know after how much movement and/or it is save to assume that no tap gesture will trigger.
You can avoid tap gestures. Instead of that you can recognize taps in touch events itself.
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if(touches.count == 1)
{
if([[touches anyObject] tapCount] == 1)
{
// Do the action here for single tap
}
else if([[touches anyObject] tapCount] == 2)
{
// Do the action here for double tap
}
}
}
And you have to set a global bool variable for check whether user moved the finger on the screen.
BOOL _isMoved;
And make it TRUE in the touch move event
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
_isMoved = YES;
}
Before recording the track, you check whether this flag is TRUE or not? And also dont forget to make the flag to FALSE after saving the track
Hope this will help you :)