I am stuck with a problem of determining how to detect a UIView being touched down and UIView being tapped. When it is touched down, I want the UIView to change its backgrou
In every UIControl subclass (UIButton, etc) you can use this to subscribe to a specific set of UIControlEvent:
addTarget:action:forControlEvents
You should add target with appropriate selector for UIControlEventTouchDown and another target/selector for UIControlEventTouchUpInside event.
UIControl reference
Thanks to Holly's answer I built a ButtonView
convenience class.
Edit: As this answer says, UILongPressGestureRecognizer
reacts quite faster so I updated my class.
Usage:
let btn = ButtonView()
btn.onNormal = { btn.backgroundColor = .clearColor() }
btn.onPressed = { btn.backgroundColor = .blueColor() }
btn.onReleased = yourAction // Function to be called
Class:
/** View that can be pressed like a button */
import UIKit
class ButtonView : UIView {
/* Called when the view goes to normal state (set desired appearance) */
var onNormal = {}
/* Called when the view goes to pressed state (set desired appearance) */
var onPressed = {}
/* Called when the view is released (perform desired action) */
var onReleased = {}
override init(frame: CGRect)
{
super.init(frame: frame)
let recognizer = UILongPressGestureRecognizer(target: self, action: Selector("touched:"))
recognizer.delegate = self
recognizer.minimumPressDuration = 0.0
addGestureRecognizer(recognizer)
userInteractionEnabled = true
onNormal()
}
func touched(sender: UILongPressGestureRecognizer)
{
if sender.state == .Began {
onPressed(self)
} else if sender.state == .Ended {
onNormal(self)
onReleased()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
First off, by your selector handleDismissDoubleTap:
, I'd assume you're looking for a double tap to dismiss. To achieve this, you should be doing: dismissGestureRecognition.numberOfTapsRequired = 2;
Secondly, if by touch down you mean a prolonged tap (or a "tap and hold") kind of gesture, you should use a UILongPressGestureRecognizer
instead of the UITapGestureRecognizer
.
A Gesture Recognizer is probably overkill for what you want. You probably just want to use a combination of -touchesBegan:withEvent:
and -touchesEnded:withEvent:
.
This is flawed, but it should give you and idea of what you want to do.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchDown = YES;
self.backgroundColor = [UIColor redColor];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Triggered when touch is released
if (self.isTouchDown) {
self.backgroundColor = [UIColor whiteColor];
self.touchDown = NO;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
// Triggered if touch leaves view
if (self.isTouchDown) {
self.backgroundColor = [UIColor whiteColor];
self.touchDown = NO;
}
}
This code should go in a custom subclass of UIView
that you create. Then use this custom view type instead of UIView
and you'll get touch handling.
This method does not require subclassing anything. You just add a UILongPressGestureRecognizer
to the view and set the minimumPressDuration
to zero. Then you check the state when the gesture events are called to see if the touch event is beginning or ending.
Here is the entire project code for the example image above.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler))
tap.minimumPressDuration = 0
myView.addGestureRecognizer(tap)
}
@objc func tapHandler(gesture: UITapGestureRecognizer) {
// there are seven possible events which must be handled
if gesture.state == .began {
myView.backgroundColor = UIColor.darkGray
return
}
if gesture.state == .changed {
print("very likely, just that the finger wiggled around while the user was holding down the button. generally, just ignore this")
return
}
if gesture.state == .possible || gesture.state == .recognized {
print("in almost all cases, simply ignore these two, unless you are creating very unusual custom subclasses")
return
}
// the three remaining states are
// .cancelled, .failed, and .ended
// in all three cases, must return to the normal button look:
myView.backgroundColor = UIColor.lightGray
}
}
Thanks to this answer for the idea.