How do I block touch events from propagating through Unity.UI ?

There’s a huge basic problem with Unity.

If I touch a UI Panel, Button, etc and there’s a game object underneath it,

the game object receives an input event.

I don’t want this behavior and nobody does. Incredibly it seems UI does not “catch” input events. What to do?

There are three ways to do this, as demonstrated in this video tutorial.

  1. Use EventSystem.current.IsPointerOverGameObject
  2. Convert your OnMouseXXX and Raycasts to an EventSystem trigger. Use a physics raycaster on the camera
  3. Implement the various handler interfaces from the EventSystems namespace. Use a physics raycaster on the camera.

Number one is easiest for a quick and dirty solution. Number three is far more powerful, and I would recommend it as the default for all new projects.

Edit:Totally removed the old answer and put up a new one. Sorry if that’s killed the comments.

#From 2015 onwards simply do this:

  1. You’ll have an EventSystem in the hierarchy already.

  2. Add an empty game object with a collider, bigger than the screen. Call the object “draw”. Make the layer “Draw”. In Physics Settings make that layer interact with nothing.

If the camera moves around, you my wish to add this object simply under the camera; it will move with the camera. (If you think about it, this collider represents the “actual glass of the user’s phone”.)

  1. Add a physics raycaster to the camera. One click. Click the event mask, uncheck everything and check only the the “Draw” layer.

  2. Have the following script on the “draw” object. You’re done.

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class FingerMove:MonoBehaviour,
        IPointerDownHandler, IDragHandler, IPointerUpHandler
	{
	public void OnPointerDown (PointerEventData data)
		{
		Debug.Log("FINGER DOWN");
		prevPointWorldSpace =
                theCam.ScreenToWorldPoint( data.position );
		}
	
	public void OnDrag (PointerEventData data)
		{
		thisPointWorldSpace =
               theCam.ScreenToWorldPoint( data.position );
		realWorldTravel =
               thisPointWorldSpace - prevPointWorldSpace;
		_processRealWorldtravel();
		prevPointWorldSpace = thisPointWorldSpace;
		}
     
	public void OnPointerUp (PointerEventData data)
		{
		Debug.Log("clear finger...");
		}

Now if you’re just detecting finger on the “glass” - so, you don’t care about the user touching objects in the scene, you’re just interested in swipes on the glass. (Example, touch to orbit the camera.) Here’s the script - couldn’t be easier …

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class FingerMove:MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
	{
	private Vector2 prevPoint;
	private Vector2 newPoint;
	private Vector2 screenTravel;
	
	public void OnPointerDown (PointerEventData data)
		{
		Debug.Log("FINGER DOWN");
		prevPoint = data.position;
		}

	public void OnDrag (PointerEventData data)
		{
		newPoint = data.position;
		screenTravel = newPoint - prevPoint;
		_processSwipe();
		}

	public void OnPointerUp (PointerEventData data)
		{
		Debug.Log("FINEGR UP...");
		}
	
	private void _processSwipe()
		{
		// your code here
		Debug.Log("screenTravel left-right.. " + screenTravel.x.ToString("f2"));
		}
	
	}

It’s that simple.

It ignores clicks on the UI. Hooray.

Much discussion and examples…

Just about every single source of information related to this question supplies this line of code as the answer:

EventSystem.current.IsPointerOverGameObject

“Simply add this check. It will return true if over a UI element”

Ok, this is true but, it returns true when over a GameObject as well. Meaning it returns true for UI element clicks and GameObject clicks. Effectively accomplishing the exact opposite of the request by allowing user clicks to propagate through the UI rather then the desired behavior of the UI blocking clicks from passing through it.

I found success in combining the piece above with something like this:

if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
    if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject != null)
    {
        if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject.GetComponent<CanvasRenderer>() != null)
        {
          //setting a boolean here, if it was true this means I clicked on a UI element
          clickingGuiElement = true;
        }
        else
        {
          clickingGuiElement = false;
        }
    }
    else
    {
      clickingGuiElement = false;
    }
}
else
{
  clickingGuiElement = false;
}

Then modifying my input code:

if (Input.GetMouseButtonDown(0))
{
    if (Physics.Raycast(ray, out hit))
    {
        if (clickingGuiElement == true)
            return;
        else
            // interaction for GOs not on GUI here
    }
}

Hope this helps!

Everyone! Here is what solved everything for me! All of this scripting and event stuff gave me trouble. So I thought, what if when I bring up the ui I bring up a 3d object with a collider because those block other objects and input. I put a cube under my main canvas and I have my ui showing and hiding on a mouse click. Now the cube shows up as well and it stops clicks! I didn’t want it physically blocking me so I checked the “is trigger” check box on the box collider. I also turned off the mesh renderer so now it is completely invisible and blocks everything. This solved my background click interactions and it solved my ui mouse clicks from locking the mouse back to the center! This was easy easy and works like a charm! At least in the editor. I hope it helps everyone!

I have tryed all suggestions but for my kind of problem this doesn’t work:
I have an image larger than the screen that I’d like to move dragging the finger moving the camera.
An UI with buttons.

In the camera script I have:

void LateUpdate () 
	{

		if (EventSystem.current.IsPointerOverGameObject ()) {
//		if( EventSystem.current.currentSelectedGameObject != null ) {
			Debug.Log ("wwwwwwwwwwwww");
		}

		if (SystemInfo.deviceType == DeviceType.Handheld) {
			Touch touch;
			if (Input.touchCount == 1 && Input.GetTouch (0).phase == TouchPhase.Moved) {
				touch = Input.GetTouch (0);
				x += touch.deltaPosition.x * xSpeed * 0.02f;
				y -= touch.deltaPosition.y * ySpeed * 0.02f;

that moves the camera.
When I drag the finger starting from an UI element the camera moves!!! AND

EventSystem.current.IsPointerOverGameObject ()) {
if( EventSystem.current.currentSelectedGameObject != null )

detect the element only when the finger is released, if the finger is released outside the UI element
currentSelectedGameObject is not null blocking everything

OnMouseXXX and Raycasts to an EventSystem trigger

should be inside the gameobject that I don’t want to select if is under another object, but I want to block the movement of the camera…
Can someont put me on the right direction?
thanks

Hi there,
if you are working with the UI system you can create a touch-blocker which blocks everything underneath it by using a very simple graphic data type.

Create a new Component with this code:

using UnityEngine;
using UnityEngine.UI;
public class SimpleGraphic : Graphic 
{
	protected override void Start () 
	{
		material.color = Color.white;
	}
}

Now create a GameObject and add the component.

This is a component based on the UI.Graphic type which is the base for the UI.Image or UI.Text types.
It therefore shares the base functionality but does not have the bloated array of tools that these other types provide.
It will still let you choose to allow rayCast or not (block or not).
As an example you can have a G.O. wth this Comp sit at the top of the siblings list. Objects that are arranged below it will appear above it and are ‘clickable’, those arranged above it are not, neither are any ray-cast-targetable parent-nodes.
If you wish to have it act as a full-screen blocker add a RectTransform to your G.O. and use the stretching/anchoring options.
The Color setting is just to visualise that it is actually there. Adjust the alpha for transparency.
Hope this helps

I made this solution:

 using UnityEngine.EventSystems;
 ...
 
 if (EventSystem.current.currentSelectedGameObject == null) { 
             //not toch UI
 }

if you have a panel or something to block only add button component, and changue color tint to none

Here’s what I did to solve this problem:

[RequireComponent(typeof(Button))]
public class EventBlocker : MonoBehaviour
{
    void Awake() {
        GetComponent<Button>().onClick.AddListener(OnClick);
    }

    void OnClick() {
        Event.current.Use();
    }
}  

The line in OnClick consumes the event, making all other UI elements ignore it.
Add this script to any UI element that you wish would block touches/clicks.
As you can see, it requires a button, to catch the event.
You can turn transition off, and set the graphic to whatever you want, even transparent, provided it’s still there and has “Raycast Target” checked.

I have no idea why this isn’t default behavior, or built-in, like in Android where buttons return a Boolean indicating if the event was handled or not.
But that’s the best solution to this that I have found so far.