0

I have a red SCNNode that 1/2 way hovers over a blue SCNNode. I need the dragEvent for both of them so when they are swiped they move (which works fine). The thing is I only need the tapEvent for the blue node.

I tried to follow this answer here to return nil if the red node is tapped so that it can continue on to the blue node. It's unable to return nil because there isn't a return value: Unexpected non-void return value in void function.

if hitResult.node.name == "Red" {

    return nil // Unexpected non-void return value in void function
}

if hitResult.node.name == "Blue" {
    // do something
}

How can I ignore the touch event when the red node is tapped and continue the touch event to the blue node underneath of it?

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(nodeWasTapped(_:)))
sceneView.addGestureRecognizer(tapGesture)

func nodeWasTapped(_ recognizer: UITapGestureRecognizer) {

    guard let sceneView = recognizer.view as? ARSCNView else { return }

    let touchLocation: CGPoint = recognizer.location(in: sceneView)

    let hitResults = sceneView.hitTest(touchLocation, options: [:])

    if !hitResults.isEmpty {

        guard let hitResult = hitResults.first else { return }

        if hitResult.node.name == "Red" {

            // *** ignore this tap and continue to the blue node ***
        }

        if hitResult.node.name == "Blue" {
            // do something
        }
    }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Have you tried removing `if hitResult.node.name == "Red" {}` and seeing if the other if statement is executed? – Xcoder Jan 21 '20 at 02:02
  • yes without the red statement the blue statement works fine but with the red statement the 1/2 of the blue node that is underneath of the red node can't receive taps – Lance Samaria Jan 21 '20 at 02:04
  • Maybe you could use the touch location to see where exactly the nodes are tapped. Like, if the bottom half of the red node is tapped then ignore the first if statement. – Xcoder Jan 21 '20 at 02:06
  • that's the issue right there. How do I ignore the bottom half of the red and let it pass the touch event through to the blue node? In the link I added returning nil is what lets the touch event pass on to whatever is underneath of it. For eg returning nil for red would let the blue node receive the tap. The thing is i can't return nil – Lance Samaria Jan 21 '20 at 02:09
  • Yep, you're right that returning a value would not work here. What you can do is [get the coordinates of the red node](https://stackoverflow.com/questions/47992268/arkit-finding-the-coordinates-of-a-scnnode-on-the-screen), then compare these coordinates to `touchLocation`'s x and y values using some math. If the bottom half of the red node is tapped, ignore the red if statement. Otherwise, execute the red if statement. – Xcoder Jan 21 '20 at 02:13
  • thanks for the help but the thing. Using the **hitResult.node.name** I can always tell wether the red or blue node is tapped, I haven't had any problems with differentiating between both of them. Where I'm lost at is how do I ignore the red node when it's tapped? For eg I can simply just just return when the re node is tapped but then the blue node would never get touched – Lance Samaria Jan 21 '20 at 02:16
  • Oh, so you've already figured out the main issue. The rest is simple: have an `if` statement checking if the node name is red and then the red `if` statement *inside* that if statement. Or am I missing something? – Xcoder Jan 21 '20 at 02:18
  • I see where the confusion, I must've explained the answer incorrectly. The red and blue nodes both receive swipes and taps, that's not the issue. The issue is I don't want the red node to receive taps and if if does it should pass the tap to the blue node underneath of it. There are actually a few other nodes but to keep it simple I just kept it red and blue. When the blue node is touched it should print "blue touched" but when the red node is touched is shouldn't so anything and pass on to the blue which the print statement will run. – Lance Samaria Jan 21 '20 at 02:23
  • So do you want the red node to stop receiving any taps? If so, then remove both if statements, then write code there that handles the blue node being tapped. This means that no matter which node(red or blue) is tapped, it will be as if only the blue node was tapped. – Xcoder Jan 21 '20 at 02:42
  • I already did that that. The problem is when the red node is tapped it's name isn't "Blue" so the blue statement never runs. For eg when I left the code that only handled the blue statement that statement is based on **if hitResult.node.name == "Blue" { }** and because the red node name is "Red" the blue statement will never run. So even without adding the red statement the blue still won't run. I think the guy who left an answer is on to something though but his answer is incomplete. I'm trying a few things now – Lance Samaria Jan 21 '20 at 02:47
  • I see what you're saying, but why can't you just remove both if statements? – Xcoder Jan 21 '20 at 02:48
  • Oh i see the issue, I tried to keep the question as simple as possible but there are other nodes that all do something when they are tapped. The red node also covers 1/2 of the yellow node and orange node. When the blue, yellow, or orange nodes are tapped they all do different things. The red node needs to be ignored for either one of them – Lance Samaria Jan 21 '20 at 02:50
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206329/discussion-between-lance-samaria-and-xcoder). – Lance Samaria Jan 21 '20 at 03:25
  • @Xcoder please see the answer I added. It only works for my question. Thanks for the help! – Lance Samaria Jan 21 '20 at 03:40

3 Answers3

0

Use below delegate method of gestureRecognizer to do this

add delegate to your recognizer

//when creating tapreconizer assign delegate too or may be are doing it already
yourTapRecognizer.delegate = self


func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view?.isDescendant(of: yourSubView) ?? false {
        return false
    }
    return true
}
Abu Ul Hassan
  • 1,340
  • 11
  • 28
  • Hi, where do I use the red node inside this method in place of **yourSubView**? For example both the red node and blue node are SCNNode types. How can i differentiate between the red node and the blue node using this example? – Lance Samaria Jan 21 '20 at 02:26
  • Also SCNNode can't be used in place of **yourSubView** because that expects type UIView. – Lance Samaria Jan 21 '20 at 02:31
  • hi, the answer didn't work and you didn't give any context on how to actually use your code. If anyone else wants to know how to use Abu's code in ARSCNView the answer is here: https://stackoverflow.com/a/51907242/4833705. Use the code inside touchesBegan – Lance Samaria Jan 21 '20 at 04:22
0

If you don't want the red node to receive any taps, just remove both if statements, because no matter where the user presses on the red and blue nodes, your goal is for the same code to be executed:

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(nodeWasTapped(_:)))
sceneView.addGestureRecognizer(tapGesture)

func nodeWasTapped(_ recognizer: UITapGestureRecognizer) {

    guard let sceneView = recognizer.view as? ARSCNView else { return }

    let touchLocation: CGPoint = recognizer.location(in: sceneView)

    let hitResults = sceneView.hitTest(touchLocation, options: [:])

    if !hitResults.isEmpty {

        guard let hitResult = hitResults.first else { return }

        //Do something here, no need for if statements
    }
}
Xcoder
  • 1,433
  • 3
  • 17
  • 37
  • I tried to keep the question as simple as possible but there are other nodes that all do something when they are tapped. The if statements are necessary for eg when the blue is tapped print blue but when yellow is tapped print yellow. Without the if statements when I tap a blue node yellow will also print. – Lance Samaria Jan 21 '20 at 02:52
  • Ah, now I see the issue. Then you could just use this `if` statement instead of your two current `if` statements: `if hitResult.node.name == "Red" || hitResult.node.name == "Blue"` then do something. Would that work? – Xcoder Jan 21 '20 at 02:54
  • 1
    nah I tried that earlier and same result. I added the code to the @AbuUlHassan and it's no different then adding a return statement because the blue view never gets the pass. I have to google around and find something similar. Thanks for the help. I really appreciate it :) – Lance Samaria Jan 21 '20 at 02:57
  • @LanceSamaria Hmm, that's really interesting. Have you tried setting breakpoints there to see what `hitResult.node.name` actually is? – Xcoder Jan 21 '20 at 02:59
  • yep, the correct name always prints out no matter which node is pressed, differentiating the touches isn't an issue. I think I found a way to do it using a switch statement and **fallthrough** but I have to add a lot of code, I'm trying it now. Gimme a few, I'll message you when I'm done – Lance Samaria Jan 21 '20 at 03:01
0

I used a switch statement and fallthrough which worked but there is a caveat. In my question I only have a red node and a blue node. Once the red node is tapped it falls through to the blue statement but if I have other nodes (which I do) this doesn't work.

Since I didn't add the other nodes to the question this is a simple workaround.

func nodeWasTapped(_ recognizer: UITapGestureRecognizer) {

    guard let sceneView = recognizer.view as? ARSCNView else { return }

    let touchLocation: CGPoint = recognizer.location(in: sceneView)

    let hitResults = sceneView.hitTest(touchLocation, options: [:])

    if !hitResults.isEmpty {

        guard let hitResult = hitResults.first else { return }

        switch hitResult.node.name {

            case "Red":
                fallthrough

            default:
                // do whatever for the blue node
            }
    }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256