How to add a touch event to a UIView?
Asked Answered
H

17

295

How do I add a touch event to a UIView?
I try:

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, nextY)] autorelease];
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];
// ERROR MESSAGE: UIView may not respond to '-addTarget:action:forControlEvents:'

I don't want to create a subclass and overwrite

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Husbandry answered 11/1, 2011 at 17:11 Comment(1)
For swift you can get it from here: #28675709Elson
A
589

In iOS 3.2 and higher, you can use gesture recognizers. For example, this is how you would handle a tap event:

//The setup code (in viewDidLoad in your view controller)
UITapGestureRecognizer *singleFingerTap = 
  [[UITapGestureRecognizer alloc] initWithTarget:self 
                                          action:@selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap];

//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
  CGPoint location = [recognizer locationInView:[recognizer.view superview]];

  //Do stuff here...
}

There are a bunch of built in gestures as well. Check out the docs for iOS event handling and UIGestureRecognizer. I also have a bunch of sample code up on github that might help.

Aftmost answered 11/1, 2011 at 22:12 Comment(9)
But that is overwriting touch defautlt action of the view. ¿Is it possible to call default touch action of the view? Something like [super touchesBegan] ?...Roxane
What does the CGPoint line do?Remuneration
@yourfriendzak the CGPoint represents the location of the tap in the superview of the tapped view. You can use this point to move the tapped view (or a sibling view) to the tapped location. This is more useful in the handler for a UIPanGestureRecognizer for dragging the view around the screen.Aftmost
great concise answer, thanks. but sheesh, wouldn't it be nice if this were a wee bit easier?! :)Sullen
It's not too bad, but I wish there were a block based API like github.com/neror/ftutils/blob/master/Headers/FTUtils/…. Xcode 4 does have support for adding/configuring gesture recognizers in Interface Builder, too.Aftmost
I got most of this to work; however, why is it that when i print the coordinates using NSLog(@"touched points: %f, %f", location.x, location.y); i always get 0.00000, 0.00000 ??? is it because of my simulator? i'm using my trackpad on my mac proLungi
@NathanEror UIView only can add ONE GestureRecognizer. I try to handle more than one EventHandler. gist.github.com/1450404Microcline
you have to add view.userInteractionEnabled = YES;Mordant
There is a difference between a single tap, and a touch event, with multiple pointers and down/move/up states. Almost each of the answers talks about single tap...Huckster
W
150

Gesture Recognizers

There are a number of commonly used touch events (or gestures) that you can be notified of when you add a Gesture Recognizer to your view. They following gesture types are supported by default:

  • UITapGestureRecognizer Tap (touching the screen briefly one or more times)
  • UILongPressGestureRecognizer Long touch (touching the screen for a long time)
  • UIPanGestureRecognizer Pan (moving your finger across the screen)
  • UISwipeGestureRecognizer Swipe (moving finger quickly)
  • UIPinchGestureRecognizer Pinch (moving two fingers together or apart - usually to zoom)
  • UIRotationGestureRecognizer Rotate (moving two fingers in a circular direction)

In addition to these, you can also make your own custom gesture recognizer.

Adding a Gesture in the Interface Builder

Drag a gesture recognizer from the object library onto your view.

enter image description here

Control drag from the gesture in the Document Outline to your View Controller code in order to make an Outlet and an Action.

enter image description here

This should be set by default, but also make sure that User Action Enabled is set to true for your view.

enter image description here

Adding a Gesture Programmatically

To add a gesture programmatically, you (1) create a gesture recognizer, (2) add it to a view, and (3) make a method that is called when the gesture is recognized.

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. create a gesture recognizer (tap gesture)
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
        
        // 2. add the gesture recognizer to a view
        myView.addGestureRecognizer(tapGesture)
    }
    
    // 3. this method is called when a tap is recognized
    @objc func handleTap(sender: UITapGestureRecognizer) {
        print("tap")
    }
}

Notes

  • The sender parameter is optional. If you don't need a reference to the gesture then you can leave it out. If you do so, though, remove the (sender:) after the action method name.
  • The naming of the handleTap method was arbitrary. Name it whatever you want using action: #selector(someMethodName(sender:)).

More Examples

You can study the gesture recognizers that I added to these views to see how they work.

enter image description here

Here is the code for that project:

import UIKit
class ViewController: UIViewController {
    
    @IBOutlet weak var tapView: UIView!
    @IBOutlet weak var doubleTapView: UIView!
    @IBOutlet weak var longPressView: UIView!
    @IBOutlet weak var panView: UIView!
    @IBOutlet weak var swipeView: UIView!
    @IBOutlet weak var pinchView: UIView!
    @IBOutlet weak var rotateView: UIView!
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Tap
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        tapView.addGestureRecognizer(tapGesture)
        
        // Double Tap
        let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
        doubleTapGesture.numberOfTapsRequired = 2
        doubleTapView.addGestureRecognizer(doubleTapGesture)
        
        // Long Press
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(gesture:)))
        longPressView.addGestureRecognizer(longPressGesture)
        
        // Pan
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
        panView.addGestureRecognizer(panGesture)
        
        // Swipe (right and left)
        let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        swipeRightGesture.direction = UISwipeGestureRecognizerDirection.right
        swipeLeftGesture.direction = UISwipeGestureRecognizerDirection.left
        swipeView.addGestureRecognizer(swipeRightGesture)
        swipeView.addGestureRecognizer(swipeLeftGesture)
        
        // Pinch
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(gesture:)))
        pinchView.addGestureRecognizer(pinchGesture)
        
        // Rotate
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotate(gesture:)))
        rotateView.addGestureRecognizer(rotateGesture)
        
    }
    
    // Tap action
    @objc func handleTap() {
        label.text = "Tap recognized"
        
        // example task: change background color
        if tapView.backgroundColor == UIColor.blue {
            tapView.backgroundColor = UIColor.red
        } else {
            tapView.backgroundColor = UIColor.blue
        }
        
    }
    
    // Double tap action
    @objc func handleDoubleTap() {
        label.text = "Double tap recognized"
        
        // example task: change background color
        if doubleTapView.backgroundColor == UIColor.yellow {
            doubleTapView.backgroundColor = UIColor.green
        } else {
            doubleTapView.backgroundColor = UIColor.yellow
        }
    }
    
    // Long press action
    @objc func handleLongPress(gesture: UILongPressGestureRecognizer) {
        label.text = "Long press recognized"
        
        // example task: show an alert
        if gesture.state == UIGestureRecognizerState.began {
            let alert = UIAlertController(title: "Long Press", message: "Can I help you?", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
    
    // Pan action
    @objc func handlePan(gesture: UIPanGestureRecognizer) {
        label.text = "Pan recognized"
        
        // example task: drag view
        let location = gesture.location(in: view) // root view
        panView.center = location
    }
    
    // Swipe action
    @objc func handleSwipe(gesture: UISwipeGestureRecognizer) {
        label.text = "Swipe recognized"
        
        // example task: animate view off screen
        let originalLocation = swipeView.center
        if gesture.direction == UISwipeGestureRecognizerDirection.right {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x += self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        } else if gesture.direction == UISwipeGestureRecognizerDirection.left {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x -= self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        }
    }
    
    // Pinch action
    @objc func handlePinch(gesture: UIPinchGestureRecognizer) {
        label.text = "Pinch recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(scaleX: gesture.scale, y: gesture.scale)
            pinchView.transform = transform
        }
    }
    
    // Rotate action
    @objc func handleRotate(gesture: UIRotationGestureRecognizer) {
        label.text = "Rotate recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(rotationAngle: gesture.rotation)
            rotateView.transform = transform
        }
    }
}

Notes

  • You can add multiple gesture recognizers to a single view. For the sake of simplicity, though, I didn't do that (except for the swipe gesture). If you need to for your project, you should read the gesture recognizer documentation. It is fairly understandable and helpful.
  • Known issues with my examples above: (1) Pan view resets its frame on next gesture event. (2) Swipe view comes from the wrong direction on the first swipe. (These bugs in my examples should not affect your understanding of how Gestures Recognizers work, though.)
Whitmer answered 9/9, 2015 at 13:20 Comment(3)
Your answer is really nice and helpful with detailed description. And thats why I have up vote you answer also. But you just missed one instruction which I think you must include in your answer as you have explained it in full detail. That instruction is about setting "<yourView>.EnableUserInteraction = TRUE". I hope you agree with me.Philipphilipa
In order to make this work on a UIView you need to also add: self.isUserInteractionEnabled = true;Tequila
I have view in which i have implemented DropMenu whenever a view is tapped. i am getting an error "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" at myView.addGestureRecognizer(tapGesture) when ever i go to next screenPlacet
I
56

I think you can simply use

UIControl *headerView = ...
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];

i mean headerView extends from UIControl.

Indubitability answered 5/12, 2011 at 1:37 Comment(6)
This is the better answer. UITapGestureRecognizer is not a replacement of UIControlEventTouchDown. A Tap usually composes of UIControlEventTouchDown and UIControlEventTouchUpInside.Dichroite
A UIView is not a UIControl and thus does not have access to addTarget:action:forControlEvents:.Subjection
Also note that a UIControl inherits from UIView. For my purposes all I had to do was a simple switch of subclass type.Jackquelinejackrabbit
you can set its class to UIControl @Subjection .. Go to xib file and set the View Custom class to UIControl . now you can handle event in it..Eolithic
So why this a better solution again since I'm changing inheritance just to handle taps?Fado
@AnthonyGlyadchenko this is a better answer because the original question asks: How do I add a touch event to a UIView? It isn't asking for a tap event. Specifically OP wants to implement UIControlEventTouchDown. Switching the UIView to UIControl is the right answer because Gesture Recognisers don't know anything about .touchDown, .touchUpInside, .touchUpOutside etc.Platina
M
27

Swift 3 & Swift 4

import UIKit

extension UIView {
  func addTapGesture(tapNumber: Int, target: Any, action: Selector) {
    let tap = UITapGestureRecognizer(target: target, action: action)
    tap.numberOfTapsRequired = tapNumber
    addGestureRecognizer(tap)
    isUserInteractionEnabled = true
  }
}

Use

yourView.addTapGesture(tapNumber: 1, target: self, action: #selector(yourMethod))
Mesosphere answered 6/10, 2016 at 11:24 Comment(2)
Thanks. Works great!Jonasjonathan
Love this! Adding to all of the projects ;)Goodale
P
17

Based on the accepted answer you can define a macro:

#define handle_tap(view, delegate, selector) do {\
    view.userInteractionEnabled = YES;\
    [view addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget:delegate action:selector]];\
} while(0)

This macro uses ARC, so there's no release call.

Macro usage example:

handle_tap(userpic, self, @selector(onTapUserpic:));
Potion answered 14/4, 2013 at 11:18 Comment(2)
If you create the view in storyboard, don't forget to enable the "user interaction enabled" option.Cornie
where we should define macros in .h or .m and what should be the name of the parameters . i mean #define handle_tap(UIView view,Delegate delegate , SEL selector)do..Eolithic
C
13

In Swift 4.2 and Xcode 10

Use UITapGestureRecognizer for to add touch event

//Add tap gesture to your view
let tap = UITapGestureRecognizer(target: self, action: #selector(handleGesture))
yourView.addGestureRecognizer(tap)

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
//Write your code here
}

If you want to use SharedClass

//This is my shared class
import UIKit

class SharedClass: NSObject {

    static let sharedInstance = SharedClass()

    //Tap gesture function
    func addTapGesture(view: UIView, target: Any, action: Selector) {
        let tap = UITapGestureRecognizer(target: target, action: action)
        view.addGestureRecognizer(tap)
    }
} 

I have 3 views in my ViewController called view1, view2 and view3.

override func viewDidLoad() {
    super.viewDidLoad()
    //Add gestures to your views
    SharedClass.sharedInstance.addTapGesture(view: view1, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view2, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view3, target: self, action: #selector(handleGesture2))

}

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
    print("printed 1&2...")
}
// GestureRecognizer
@objc func handleGesture2(gesture: UITapGestureRecognizer) -> Void {
    print("printed3...")
}
Cockcroft answered 12/10, 2018 at 8:34 Comment(0)
C
8

You can achieve this by adding Gesture Recogniser in your code.

Step 1: ViewController.m:

// Declare the Gesture.
UITapGestureRecognizer *gesRecognizer = [[UITapGestureRecognizer alloc] 
                                          initWithTarget:self 
                                          action:@selector(handleTap:)];
gesRecognizer.delegate = self;

// Add Gesture to your view.
[yourView addGestureRecognizer:gesRecognizer]; 

Step 2: ViewController.m:

// Declare the Gesture Recogniser handler method.
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer{
   NSLog(@"Tapped");
}

NOTE: here yourView in my case was @property (strong, nonatomic) IBOutlet UIView *localView;

EDIT: *localView is the white box in Main.storyboard from below

enter image description here

enter image description here

Chirk answered 5/3, 2015 at 7:51 Comment(2)
i am having multiple view so can i can the sender id ??Roubaix
You have to go to the inner subview, then you can paas the sender.---------for (UIView *view in self.topicScrollView.subviews) {//Go Inside the superview if ([view isKindOfClass:[UIButton class]]) { // Go to that particular view // Here you have reached upto that point. } }Chirk
P
6

Heres a Swift version:

// MARK: Gesture Extensions
extension UIView {

    func addTapGesture(#tapNumber: Int, target: AnyObject, action: Selector) {
        let tap = UITapGestureRecognizer (target: target, action: action)
        tap.numberOfTapsRequired = tapNumber
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }

    func addTapGesture(#tapNumber: Int, action: ((UITapGestureRecognizer)->())?) {
        let tap = BlockTap (tapCount: tapNumber, fingerCount: 1, action: action)
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }
}
Perturbation answered 24/8, 2015 at 12:54 Comment(2)
what is BlockTap?Concurrent
Oh man, forgot that part. Its a custom function. Here: github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/…Perturbation
T
6

Swift 3:

let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGestureRecognizer(_:)))
view.addGestureRecognizer(tapGestureRecognizer)

func handleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {

}
Teledu answered 21/6, 2017 at 6:19 Comment(0)
R
3

Swift 5.3

Solution with Closure, based on: UIGestureRecognizer with closure

final class BindableGestureRecognizer: UITapGestureRecognizer {
    private var action: () -> Void

    init(action: @escaping () -> Void) {
        self.action = action
        super.init(target: nil, action: nil)
        self.addTarget(self, action: #selector(execute))
    }

    @objc private func execute() {
        action()
    }
}

public extension UIView {
    /// A discrete gesture recognizer that interprets single or multiple taps.
    /// - Parameters:
    ///   - tapNumber: The number of taps necessary for gesture recognition.
    ///   - closure: A selector that identifies the method implemented by the target to handle the gesture recognized by the receiver. The action selector must conform to the signature described in the class overview. NULL is not a valid value.
    func addTapGesture(tapNumber: Int = 1, _ closure: (() -> Void)?) {
        guard let closure = closure else { return }

        let tap = BindableGestureRecognizer(action: closure)
        tap.numberOfTapsRequired = tapNumber
        addGestureRecognizer(tap)

        isUserInteractionEnabled = true
    }
}

Using:

view.addTapGesture { [weak self] in
    self?.view.backgroundColor = .red
}
Rozalin answered 11/10, 2021 at 15:25 Comment(0)
P
2

Objective-C:

UIControl *headerView = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, nextY)];
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];

Swift:

let headerView = UIControl(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: nextY))
headerView.addTarget(self, action: #selector(myEvent(_:)), for: .touchDown)

The question asks:

How do I add a touch event to a UIView?

It isn't asking for a tap event.

Specifically OP wants to implement UIControlEventTouchDown

Switching the UIView to UIControl is the right answer here because Gesture Recognisers don't know anything about .touchDown, .touchUpInside, .touchUpOutside etc.

Additionally, UIControl inherits from UIView so you're not losing any functionality.

If all you want is a tap, then you can use the Gesture Recogniser. But if you want finer control, like this question asks for, you'll need UIControl.

https://developer.apple.com/documentation/uikit/uicontrol?language=objc https://developer.apple.com/documentation/uikit/uigesturerecognizer?language=objc

Platina answered 14/5, 2020 at 14:5 Comment(0)
A
2

Simple and Useful Extension:

extension UIView {
private struct OnClickHolder {
    static var _closure:()->() = {}
}

private var onClickClosure: () -> () {
    get { return OnClickHolder._closure }
    set { OnClickHolder._closure = newValue }
}

func onTap(closure: @escaping ()->()) {
    self.onClickClosure = closure
    
    isUserInteractionEnabled = true
    let tap = UITapGestureRecognizer(target: self, action: #selector(onClickAction))
    addGestureRecognizer(tap)
}

@objc private func onClickAction() {
    onClickClosure()
}

}

Usage:

    override func viewDidLoad() {
    super.viewDidLoad()
    let view = UIView(frame: .init(x: 0, y: 0, width: 80, height: 50))
    view.backgroundColor  = .red
    view.onTap {
        print("View Tapped")
    }
}
Affectionate answered 14/2, 2022 at 12:43 Comment(1)
FYI.. if you use this on two different UIViews, the onTap handlers will get overridden by the last onTap call. Probably because of the static prop.Howarth
O
0

Here is ios tapgesture; First you need to create action for GestureRecognizer after write the below code under the action as shown below

- (IBAction)tapgesture:(id)sender

{


[_password resignFirstResponder];


[_username resignFirstResponder];

NSLog(@" TapGestureRecognizer  tapped");

}
Orgiastic answered 31/3, 2016 at 5:30 Comment(0)
W
0

Another way is adding a transparent button to the view

UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
b.frame = CGRectMake(0, 0, headerView.width, headerView.height);
[headerView addSubview:b];
[b addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchDown];

And then, handle click:

- (void)buttonClicked:(id)sender
{}
Wares answered 17/6, 2016 at 10:32 Comment(0)
A
0

Create a gesture recognizer (subclass), that will implement touch events, like touchesBegan. You can add it to the view after that.

This way you'll use composition instead subclassing (which was the request).

Abfarad answered 13/2, 2017 at 17:11 Comment(0)
C
0

Why don't you guys try SSEventListener?

You don't need to create any gesture recognizer and separate your logic apart to another method. SSEventListener supports setting listener blocks on a view to listen for single tap gesture, double tap gesture and N-tap gesture if you like, and long press gesture. Setting a single tap gesture listener becomes this way:

[view ss_addTapViewEventListener:^(UITapGestureRecognizer *recognizer) { ... } numberOfTapsRequired:1];

Cissy answered 15/1, 2018 at 6:53 Comment(0)
G
-1

Swift add UITapGestureRecognizer

1.

public class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }

    private func commonInit() {
        self.isUserInteractionEnabled = true
        let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
        self.addGestureRecognizer(tap)
    }

    @objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
        //logic
    }
}
public class MyView: UIView {
    private lazy var tapGesture: UITapGestureRecognizer = {
        let gesture = UITapGestureRecognizer(
            target: self,
            action: #selector(handleTap(_:))
        )
        gesture.isEnabled = false
        self.addGestureRecognizer(gesture)
        return gesture
    }()

    @objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
        //logic
    }
}
Gillett answered 19/3 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.