I am defining gesture recognizers in an ImageView subclass but calling a method in the ViewController crashes with "unrecognized selector". The gesture works if I define the gestures in the VC, but I'm trying to put the code in the custom view class to cut down on clutter.
Is this possible? This is what I'm doing:
class DetailPhotoImageView: UIImageView {
func setupGestures(){
let tapped = UITapGestureRecognizer(target: self, action: #selector(TripDetailVC.tapped(_:)))
tapped.numberOfTapsRequired = 2
addGestureRecognizer(tapped)
}
}
And then in the VC I have this function and call imageView.setupGestures()
func tapped(_ gesture:UIGestureRecognizer) {
if let tapGesture = gesture as? UITapGestureRecognizer {
switch tapGesture.numberOfTapsRequired {
case 2:
print("Worked")
default:
break
}
}
}
So going off of Randall Wang's suggestion, they are right, you should could also go about this another, space-saving way. Pass the viewController as a parameter and set the it as the target:
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
func setupGestures(_ target: UIViewController) {
guard let aTarget = target as? MyViewController else { return }
let tap = UITapGestureRecognizer(target: aTarget, action: #selector(aTarget.someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
}
And in your viewController class:
detailPhoto.setupGestures(self)
Make a protocol for your UIImageView that takes in the gesture and set your ViewController as the delegate (having the ImageView as a parameter as well is just good practice.. or common):
protocol DetailPhotoDelegate {
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor gesture: UITapGestureRecognizer)
}
Add a delegate variable to you UIImageView subclass and preform your protocol function when the view is tapped. Also, make sure your UIImageView is a UIGestureRecognizerDelegate.
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
var delegate: DetailPhotoDelegate?
func setupGestures() {
let tap = UITapGestureRecognizer(target: self, action: #selector(someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
func someAction(_ guesture: UITapGestureRecognizer) {
print("tap - subclass func")
guard let aDelegate = aDelegate else { assertionFailure(); return }
delegate.detailPhoto(self, actionFor: gesture)
}
}
Then add the protocol to your ViewController, set it as your DetailPhotoImageView's delegate somewhere prior to the action being implemented (I did it in viewDidLoad() for the example), and implement the protocol method however you wish:
class ViewController: UIViewController, DetailPhotoDelegate {
#IBAction weak var detailPhoto: DetailPhotoImageView!
override func viewDidLoad() {
super.viewDidLoad()
detailPhoto.delegate = self
}
// Mark: DetailPhotoDelegate
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor guesture: UITapGestureRecognizer) {
switch guesture.numberOfTapsRequired {
case 1: print("1 tap"); return
case 2: print("2 taps"); return
default: print("\(guesture.numberOfTapsRequired) taps"); return
}
}
I think target shouldn't be self
I have this Setup in my Storyboard.
In my first ViewController Scene I have a MapView from MapBox. In there I have put a TextField (AddressTextField). On that TextField when touching the view, i'm running self.addressTextField.resignFirstResponder(), but after that neither the mapview, nor any other element in there or in the Embedded Segues react on a touch or click. Probably this is because I didn't completely understand the system of the First Responder. I'm thankful for every help.
Edit 1:
I think I know what's going on now, but I don't know how to fix it. When I add the Gesture Recognizer to the View (or to the mapView, that doesn't matter), the other UIViews and the MapView do not recognize my Tap-Gestures anymore. When I am not adding the Recognizer everything works fine. It seems as if the Gesture Recognizer is recognizing every tap I make on either the UIViews or the MapView and therefore other gestures are not recognized.
Edit 2:
I just added a print() to dismissKeyboard(). As soon as any Touch Event gets recognized on the MapView or the other UIViews, dismissKeyboard() gets called. So I think my thought of Edit 1 was correct. Does anyone know how I can solve this, so that it's not only dismissKeyboard() that gets called ?
Some Code:
func dismissKeyboard(){
self.addressTextField.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
dismissKeyboard()
return true
}
//Class (only partially)
class ViewController: UIViewController, MGLMapViewDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
override func viewDidLoad(){
mapView.delegate = self
addressTextField.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
}
Others are just #IBActions linked to the Buttons, or other elements.
try this:
func dismissKeyboard(){
view.endEditing(true)
}
hope it helps!
After I knew the real issue I was able to solve the problem. I declared a var keyboardEnabled. Then I added these lines to my class.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
var keyboardEnabled = false
override func viewDidLoad(){
super.viewDidLoad()
//Looks for single tap
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
/* Setting keyboardEnabled */
//Editing Target did end
#IBAction func editingTargetDidEnd(_ sender: Any) {
keyboardEnabled = false
}
//Editing TextField Started
#IBAction func editingAdressBegin(_ sender: Any) {
keyboardEnabled = true
}
//Call this function when the tap is recognized.
func dismissKeyboard() {
self.mapView.endEditing(true)
keyboardEnabled = false
}
//Implementing the delegate method, so that I can add a statement
//decide when the gesture should be recognized or not
//Delegate Method of UITapGestureRecognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return keyboardEnabled
}
}
With this solution keyboardEnabled takes care of deciding wether my UIGestureRecognizer should react or not. If the Recognizer doesn't react, the Gesture is simply passed on to the UIViews or other Elements that are in my MapView.
Thanks for all your answers!
I got a gesture related issue, somewhat similar to: gesture-problem-uiswipegesturerecognizer-uislider
But I code in swift and so I need a solution in swift.
What happens in my project is this:
I have a ViewController on which a tap on the screen by the user, will perform a segue. But I have a UISlider on this viewcontroller, and when a user 'releases' the slider it is sometimes (why sometimes and not always, confuses me) recognized as a tap on the screen.
So I understand I have to prevent that the gesture recognizer 'sees/recognizes' touches on the UIslider.
But how do I prevent this? (in swift 2.0, using Xcode7. but I also understand it if u use earlier swift coding in an answer)
I am fairly new to coding in swift. I hope someone can help!
Here is the code in the viewcontroller:
// The slider
#IBOutlet weak var sliderValue: UISlider!
#IBAction func sliderValueChanged(sender: UISlider) {
// Do stuff
}
// UITapGestureRecognizer
override func viewDidLoad() {
super.viewDidLoad()
let touch = UITapGestureRecognizer(target: self, action: "touched:")
self.view.addGestureRecognizer(touch)
}
// Perform Segue
func touched (_: UIGestureRecognizer) {
//Segue to another viewcontroller
performSegueWithIdentifier("nice", sender: self)
}
(EDIT:) I updated my code with information I have found here on stackoverflow. I have added UIGestureRecognizerDelegate:
class LampOn: UIViewController, UIGestureRecognizerDelegate {...}
And I have added shouldReceiveTouch:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
gestureRecognizer.delegate = self
if (touch.view == sliderValue){
print("touching slider")
return false
}
else{
print("touching elsewhere")
return true
}
}
But the 'shouldReceiveTouch` func never gets called from the console. So what am I missing? Did I set up the delegate correctly?
The "shouldReceiveTouch" function is never called because you set the delegate inside the "shouldReceiveTouch" function. The delegate needs to be set to self BEFORE the function can be called.
What you need to do is to set the delegate inside the viewDidLoad() and everything should be fine.
override func viewDidLoad() {
super.viewDidLoad()
let touch = UITapGestureRecognizer(target: self, action: "touched:")
self.view.addGestureRecognizer(touch)
touch.delegate = self
}
How can I add a UITapGestureRecognizer to a UITextView but still have the touches getting through to the UITextView as normal?
Currently as soon as I add a custom gesture to my textView it blocks the tap for UITextView default actions like positioning the cursor.
var tapTerm:UITapGestureRecognizer = UITapGestureRecognizer()
override func viewDidLoad() {
tapTerm = UITapGestureRecognizer(target: self, action: "tapTextView:")
textView.addGestureRecognizer(tapTerm)
}
func tapTextView(sender:UITapGestureRecognizer) {
println("tapped term – but blocking the tap for textView :-/")
…
}
How can I process taps but keep any textView behaviour like cursor positioning as is?
To do that make your view controller adopt to UIGestureRecognizerDelegate and override should recognize simultaneously with gesture recognizer method like:
override func viewDidLoad() {
tapTerm = UITapGestureRecognizer(target: self, action: "tapTextView:")
tapTerm.delegate = self
textView.addGestureRecognizer(tapTerm)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
In case anyone came here looking for #Zell B.'s answer in Objective C, here's the code:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(textViewTapped:)];
tap.delegate = self;
tap.numberOfTapsRequired = 1;
[self.textView addGestureRecognizer:tap];
}
- (void)textViewTapped:(UITapGestureRecognizer *)tap {
//DO SOMTHING
}
#pragma mark - Gesture recognizer delegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
PS: Don't forget < UIGestureRecognizerDelegate >
Swift 4.2
The following steps allows me to escape a full-screen UITextView with a tap, whilst allowing to scroll the contents of the UITextView:
Disconnected the UIGestureRecognizer from the UITableView.
Made a CustomTextView: UITextView.
Added a 'sender' var to the particular UIViewController with the CustomTextView.
Trap for 'Touches Ended...'
Call for an excape function within the sender UIViewController.
class CustomTextView: UITextView {
var sender: DocViewController?
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let controller = sender {
controller.handleSwipeGesture()
}
}
}
I can either scroll the contents of the UITextView or merely tap to exit.
The 'sender' is set from the hosting UIViewController at creation.
Somehow I could not assign a delegate inside UIView so I came up with a button solution.
I insert a button that completely covers the textView and when tapped on textViewButton I do resign the button so textView shows.
#IBAction func textViewTapped(_ sender: UIButton) {
self.placeholderLabel.isHidden = true
self.textViewButton.resignFirstResponder()
}
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
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.
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.
Control drag from the gesture in the Document Outline to your View Controller code in order to make an Outlet and an Action.
This should be set by default, but also make sure that User Action Enabled is set to true for your view.
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.
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.)
I think you can simply use
UIControl *headerView = ...
[headerView addTarget:self action:#selector(myEvent:) forControlEvents:UIControlEventTouchDown];
i mean headerView extends from UIControl.
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))
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:));
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...")
}
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
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
}
}
Swift 3:
let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGestureRecognizer(_:)))
view.addGestureRecognizer(tapGestureRecognizer)
func handleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {
}
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
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
}
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")
}
}
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");
}
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
{}
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).
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];