UIBarButtonItem Long Press / Short Press - ios

I have looked through the existing questions on this subject matter, and there appears to be no answers post iOS 11 (which appeared to break the gesturerecognizers).
Is there a way to detect the short press / long press on a UIBarButtonItem? Apple uses this functionality in Pages, Numbers, Keynote for Undo / Redo.

Just replace your default event handler for the tap event
-(void)handlerForBarButton:(UIBarButtonItem*)p_Sender
with its extend version
-(void)handlerForBarButton:(UIBarButtonItem*)p_Sender forEvent:(UIEvent*)p_Event
By inspecting the UIEvent object you can learn, if the event was a long press (tapCount of first UITouch object is 0).
This is my code for a playback rate button that returns the rate to '1x' if the bar button item is pressed longer the about one second:
/*
rateButtonPressed:forEvent:
*/
- (IBAction)rateButtonPressed:(UIBarButtonItem*)p_Sender
forEvent:(UIEvent*)p_Event
{
UITouch* firstTouch = nil;
if ( (nil != ((firstTouch = p_Event.allTouches.allObjects.firstObject)))
&& (0 == firstTouch.tapCount))
{
self.avAudioPlayer.rate = 1.0;
}
else
{ // Default tap
if (2.0 == self.avAudioPlayer.rate)
{
self.avAudioPlayer.rate = 0.5;
}
else
{
self.avAudioPlayer.rate += 0.25;
}
}
[WBSettingsSharedInstance.standardUserDefaults setDouble:self.avAudioPlayer.rate
forKey:#"audioPlayerRate"];
[self updateRateBarButton];
}

Try this
#IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, #selector (tap)) //Tap function will call when user tap on button
let longGesture = UILongPressGestureRecognizer(target: self, #selector(long)) //Long function will call when user long press on button.
tapGesture.numberOfTapsRequired = 1
btn.addGestureRecognizer(tapGesture)
btn.addGestureRecognizer(longGesture)
}
#objc func tap() {
print("Single tap done")
}
#objc func long() {
print("Long gesture recognized")
}

Related

How to hide a view's subviews (Floating Action button) from tapping two different areas

I created a floating action button which i would like to dismiss all the action button when a user either taps the FAB when it's open or when the user taps anywhere on the screen and FAB is open. It is also important to note that the FAB is being displayed over a tableview and i want to retain the ability to select tableview cells.
In my implementation of the FAB i added a target to the FAB button which i use to open and close the FAB and i also implemented a tapGesture on the viewController with the tableview such that when a tap gesture is invoked i can close the FAB if open.
To make this work i did a bit of research and found out that i have to set
tap.cancelsTouchesInView = false
so that the tableView events continue working. However the side effect is that when i tap on the fab to close it two events are fired one from the tapGesture of the FAB and another from the button target which results in the FAB not closing when u tap on it if its open.
Is there a more elegant way of making the FAB be able to close when tapped whilst its open and also have the tap Gesture on the viewController close the fab when its open and a user taps anywhere on the screen.
Here is some of my code:
ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self,
action: #selector(self.dismissActionButtons(_:)))
self.view.addGestureRecognizer(tap)
tap.cancelsTouchesInView = false
self.floatingActionButton.isHidden = true
}
#objc func dismissActionButtons(_ sender: UIButton) {
if !floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.animateActionButtonsDisappering()
}
}
Custom FAB View :
override init(frame: CGRect) {
super.init(frame: .zero)
actionButtonsCarousel.isHidden = true
self.translatesAutoresizingMaskIntoConstraints = false
self.mainButton.addTarget(self,
action: #selector(ACTFAB.fabButtonAction(_:)),
for: .touchUpInside)
}
#objc func fabButtonAction(_ sender: UIButton) {
if self.actionButtonsCarousel.isHidden {
self.animateActionButtonsAppering()
} else {
self.animateActionButtonsDisappering()
}
}
func animateActionButtonsAppering() {
self.actionButtonsCarousel.alpha = 0.0
self.actionButtonsCarousel.isHidden = false
UIView.transition(with: self, duration: 0.5, options: .preferredFramesPerSecond60, animations: {
self.actionButtonsCarousel.alpha = 1.0
})
self.mainButton.setImage(UIImage(named: "fab-open-icon"), for: .normal)
}
func animateActionButtonsDisappering() {
self.actionButtonsCarousel.alpha = 1.0
self.actionButtonsCarousel.isHidden = true
UIView.transition(with: self, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.actionButtonsCarousel.alpha = 0.0
})
self.mainButton.setImage(UIImage(named: "fab-closed-icon"), for: .normal)
}
Two valid scenarios:
1 FAB is open -> click FAB -> FAB closes
2 FAB is open -> click anywhere other than FAB -> FAB closes
Scenario number 1 fails with my current code.
If I understand your question correctly, the issue is that tapping the FAB causes both the button's action to be fired but also, as you are passing the event through to the underlying viewController, the gestureRecogniser to fire too.
I'm assuming the button action is the primary event, and that when this fires you need to stop the gestureRecogniser. A gestureRecogniser has a .location(in:) method which allows you to get the first tap location (for a tapGestureRecogniser) in terms of any view, and a UIView has a .point(inside: with:) method that checks whether a CGPoint (in terms of its own coordinate space) is inside it bounds. Therefore you should be able to do something like this (from memory and not compiled, so may need some tweaking but hopefully it should get you started):
#objc func dismissActionButtons(_ sender: UIButton) {
let tapPoint = sender.location(in: customFABview)
if customFABview.point(inside: tapPoint, with: nil) &&
!floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.animateActionButtonsDisappering()
}
}
Continuing with #flanker's answer i created a boolean in the fab to check if the event was coming from tapGesture then i added it to the check conditional statement as follows:
var isComingFromGestureEvent: Bool = false
#objc func fabButtonAction(_ sender: UIButton) {
if self.actionButtonsCarousel.isHidden && !isComingFromGestureEvent {
self.animateActionButtonsAppering()
} else {
self.animateActionButtonsDisappering()
}
}
In the ViewController i then just used flanker's answer to set the boolean state as follows :
var locationInView: CGPoint = CGPoint(x: 0, y: 0)
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self,
action: #selector(self.dismissActionButtons(_:)))
self.view.addGestureRecognizer(tap)
pointInView = tap.location(in: floatingActionButton)
tap.cancelsTouchesInView = false
self.floatingActionButton.isHidden = true
}
#objc func dismissActionButtons(_ sender: UIButton) {
let tapPoint = pointInView
if floatingActionButton.point(inside: tapPoint, with: nil) &&
!floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.isComingFromGestureEvent = true
self.floatingActionButton.animateActionButtonsDisappering()
} else {
self.floatingActionButton.isComingFromGestureEvent = false
}
}

fade in button so it won't automatically fade out again

I'm using a tap gesture recogniser so if the user taps on the screen the button fades out for 5 seconds and then if the user wants to see the buttons on the screen again they tap on the screen and the button fades in.
The Problem is:
I can't disable the button when it fades in so it won't automatically fade out again. I tried to invalidate the timer but that didn't work. To be more specific of what I want to do:
On app load, you see an enabled "Start Stop Button." - Tap anywhere on the screen and a 5 second timer starts to fade the button out and disables it. Once the button fades out and disables, I can tap anywhere on the screen to fade the button back in, enable it, and kill the timer so the button shows up as it was before I first tapped it.
class ViewController: UIViewController {
// Create these 3 properties in the top of your class
var secondToFadeOut = 5 // How many second do you want the view to idle before the button fades. You can change this to whatever you'd like.
var timer = Timer() // Create the timer!
var isTimerRunning: Bool = false // Need this to prevent multiple timers from running at the same time.
#IBOutlet weak var startStopButton: UIButton! // The outlet for your button. This is used to fade it in and out, and enable / disable it.
override func viewDidLoad() {
super.viewDidLoad()
startStopButton.isEnabled = true
runTimer()
// Add a tap gesture recognizer to the main view to determine when the screen was tapped (for the purpose of resetting the timer).
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
self.view.addGestureRecognizer(tapRecognizer)
}
func runTimer() {
// Create the timer to run a method (in this case... updateTimer) every 1 second.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
// Set the isTimerRunning bool to true
isTimerRunning = true
}
#objc func updateTimer() {
// Every 1 second that this method runs, 1 second will be chopped off the secondToFadeOut property. If that hits 0 (< 1), then run the fadeOutButton and invalidate the timer so it stops running.
secondToFadeOut -= 1
print(secondToFadeOut)
if secondToFadeOut < 1 {
fadeOutButton()
timer.invalidate()
isTimerRunning = false
}
}
#objc func tap(_ gestureRecognizer: UITapGestureRecognizer) {
// When the view is tapped (based on the gesture recognizer), reset the secondToFadeOut property, fade in (and enable) the button.
//secondToFadeOut = 5
fadeInButton()
timer.invalidate()
//if isTimerRunning == false {
// runTimer()
//}
}
func fadeOutButton() {
// Fade out your button! I also disabled it here. But you can do whatever your little heart desires.
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 0.25
}
self.startStopButton.isEnabled = false
}
func fadeInButton() {
// Fade the button back in, and set it back to active (so it's tappable)
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 1
}
self.startStopButton.isEnabled = true
}
#IBAction func startStopButtonPressed(_ sender: UIButton) {
print("Start Stop Button Pressed")
}
}
My best guess is that you have a rogue Timer object that remains in memory even after you invalidate your current timer, and that is causing the button to fade out again after it fades in.
I have made several edits to your class. Check the code out:
class ViewController: UIViewController {
// Create these 3 properties in the top of your class
var secondToFadeOut = 5 // How many second do you want the view to idle before the button fades. You can change this to whatever you'd like.
var timer? = nil // Create the timer!
#IBOutlet weak var startStopButton: UIButton! // The outlet for your button. This is used to fade it in and out, and enable / disable it.
override func viewDidLoad() {
super.viewDidLoad()
startStopButton.isEnabled = true
runTimer()
// Add a tap gesture recognizer to the main view to determine when the screen was tapped (for the purpose of resetting the timer).
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
self.view.addGestureRecognizer(tapRecognizer)
}
func runTimer() {
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: secondToFadeOut, target: self, selector: (#selector(ViewController.timerFired)), userInfo: nil, repeats: false)
}
}
#objc func timerFired() {
timer = nil
if self.startStopButton.isEnabled {
fadeOutButton()
} else {
fadeInButton()
}
}
#objc func tap(_ gestureRecognizer: UITapGestureRecognizer) {
runTimer()
}
func fadeOutButton() {
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 0.25
}
self.startStopButton.isEnabled = false
}
func fadeInButton() {
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 1
}
self.startStopButton.isEnabled = true
}
#IBAction func startStopButtonPressed(_ sender: UIButton) {
print("Start Stop Button Pressed")
}
}

Fade out button when not touching for 3 seconds and fade in when user touches the screen

Problem:
I have a UIButton that I'd like to fade out for a few seconds when the user doesn't touch the screen and fade them in when the user touches the screen. I think I might need to use a timer and some animation in the viewdidload part
#IBOutlet var startStopButton: UIButton!
#IBAction func startStopButtonTapped(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad() }
I can help you here. Let me preface this answer with a usability concern. I'm not sure it makes sense to tap anywhere on the screen to reset (fade in) the button after X seconds (will the user know to do that?). You might want to add a button or some kind of text indicator to "tap here to reset the button(s)". This all ultimately depends on your application, but just throwing that out there :)
On with the answer!
The general idea here is you want to have a timer that runs a method every 1 second. If the timer hits zero, then you fade out and disable the startStop button. If the user taps anywhere on the screen (via gesture recognizer on the whole view), then fade the button in and enable it.
Now for the code part!
class ViewController: UIViewController {
// Create these 3 properties in the top of your class
var secondToFadeOut = 5 // How many second do you want the view to idle before the button fades. You can change this to whatever you'd like.
var timer = Timer() // Create the timer!
var isTimerRunning: Bool = false // Need this to prevent multiple timers from running at the same time.
#IBOutlet weak var startStopButton: UIButton! // The outlet for your button. This is used to fade it in and out, and enable / disable it.
override func viewDidLoad() {
super.viewDidLoad()
startStopButton.isEnabled = true
runTimer()
// Add a tap gesture recognizer to the main view to determine when the screen was tapped (for the purpose of resetting the timer).
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
self.view.addGestureRecognizer(tapRecognizer)
}
func runTimer() {
// Create the timer to run a method (in this case... updateTimer) every 1 second.
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
// Set the isTimerRunning bool to true
isTimerRunning = true
}
#objc func updateTimer() {
// Every 1 second that this method runs, 1 second will be chopped off the secondToFadeOut property. If that hits 0 (< 1), then run the fadeOutButton and invalidate the timer so it stops running.
secondToFadeOut -= 1
print(secondToFadeOut)
if secondToFadeOut < 1 {
fadeOutButton()
timer.invalidate()
isTimerRunning = false
}
}
#objc func tap(_ gestureRecognizer: UITapGestureRecognizer) {
// When the view is tapped (based on the gesture recognizer), reset the secondToFadeOut property, fade in (and enable) the button.
//secondToFadeOut = 5
fadeInButton()
timer.invalidate()
//if isTimerRunning == false {
// runTimer()
//}
}
func fadeOutButton() {
// Fade out your button! I also disabled it here. But you can do whatever your little heart desires.
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 0.25
}
self.startStopButton.isEnabled = false
}
func fadeInButton() {
// Fade the button back in, and set it back to active (so it's tappable)
UIView.animate(withDuration: 0.5) {
self.startStopButton.alpha = 1
}
self.startStopButton.isEnabled = true
}
#IBAction func startStopButtonPressed(_ sender: UIButton) {
print("Start Stop Button Pressed")
}
}
Hope that makes sense, but do let me know if you have any more questions!

How to add a touch event to a UIView?

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];

Double touch on UIButton

How to recognize double touch on UIButton ?
Add an target-action for the control event UIControlEventTouchDownRepeat, and do action only when the touch's tapCount is 2.
Objective-C:
[button addTarget:self action:#selector(multipleTap:withEvent:)
forControlEvents:UIControlEventTouchDownRepeat];
...
-(IBAction)multipleTap:(id)sender withEvent:(UIEvent*)event {
UITouch* touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
// do action.
}
}
As #Gavin commented, double-tap on a button is an unusual gesture. On the iPhone OS double-tap is mostly used for zoomable views to zoom into/out of a region of focus. It may be unintuitive for the users if you make the gesture to perform other actions.
Swift 3:
button.addTarget(self, action: #selector(multipleTap(_:event:)), for: UIControlEvents.touchDownRepeat)
And then:
func multipleTap(_ sender: UIButton, event: UIEvent) {
let touch: UITouch = event.allTouches!.first!
if (touch.tapCount == 2) {
// do action.
}
}
If you are working with swift 5, #kennytm's solution won't work. So you can write an objective-c/swift function and add it as gesture with number of desired taps.
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped))
tap.numberOfTapsRequired = 2
btn.addGestureRecognizer(tap)
and then
#objc func doubleTapped() {
// your desired behaviour.
}
Here button can be tapped any number of times and it will show as tapped. But the above function won't execute until button tapped for required number of times.
[button addTarget:self action:#selector(button_TouchDown:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(button_TouchDownRepeat:withEvent:) forControlEvents:UIControlEventTouchDownRepeat];
-(void)button_one_tap:(UIButton*) button
{
NSLog(#"button_one_tap");
}
-(void)button_double_tap:(UIButton*) button
{
NSLog(#"button_double_tap");
}
-(void)button_TouchDown:(UIButton*) button
{
NSLog(#"button_TouchDown");
[self performSelector:#selector(button_one_tap:) withObject:button afterDelay:0.3 /*NSEvent.doubleClickInterval maybe too long*/];
}
-(void)button_TouchDownRepeat:(UIButton*) button withEvent:(UIEvent*)event {
NSLog(#"button_TouchDownRepeat");
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(button_one_tap:) object:button];
UITouch* touch = [[event allTouches] anyObject];
//NSLog(#"touch.tapCount = %ld", touch.tapCount);
if (touch.tapCount == 2) {
// do action.
[self button_double_tap:button];
}
}
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: "didTap:", forControlEvents: .TouchUpInside)
button.addTarget(self, action: "didDoubleTap:", forControlEvents: .TouchDownRepeat)
}
var ignoreTap = false
func didTap(sender: UIButton) {
if ignoreTap {
ignoreTap = false
print("ignoretap", sender)
return
}
print("didTap", sender)
}
func didDoubleTap(sender: UIButton) {
ignoreTap = true
print("didDoubleTap", sender)
}
try to use this for the button event
UIControlEventTouchDownRepeat

Resources