state for UITapGestureRecognizer - ios

The UIGestureRecognizer reference says: "discrete events such as a tap or a swipe can not report changes within the gesture"
How can I get notified when a finger is just touching the screen (and hasn't left it yet), and get then notified when it leaves the screen?
Thanks for your help!

you can detect touches by using
ObjC answer:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Swift 3.0 answer:
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)

Well your answer lies in your question title i.e. UIGestureRecognizerState. You need to check for the state of the gestureRecognizer in the target method. like:
- (void)handleTap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
// handling code
}
else {
//do anything else
}
}
These are the possible enums:
UIGestureRecognizerStatePossible,
UIGestureRecognizerStateBegan,
UIGestureRecognizerStateChanged,
UIGestureRecognizerStateEnded,
UIGestureRecognizerStateCancelled,
UIGestureRecognizerStateFailed,
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
May be that'll help.

Related

How to track clicks outside the custom view

I've a custom view, it looks so
How can I track click on the white space (outside the view) and hide it?
U can use touchesBegan to track it like so:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch= [touches anyObject];
if ([touch view] == self.view)
{
// do stuff
}
}
For swift:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
if touch.view == self.view {
// do stuff
}
}
super.touchesBegan(touches, withEvent:event)
}
You can add a UIView under the Custom View covering the whole screen, give it an alpha of 0.1 or so. You can then add a tapGestureRecognizer to it to catch all the touches outside the custom view.
Just remember to hide the overlay too when you hide the custom view, so that the touches are not blocked afterwards.

touchesEnded not called if i do not move finger

I have a simple piece of code in which i have overridden touchesBegan,Ended,Cancelled(empty block) and touchesMoved.
If i click ( i'm testing on Desktop PC ) with the mouse touchesBegan it's called, but touchesEnded is called only when i move finger for a while. That makes impossible to recognize a single tap or a drag of the finger, and handle them differently.
I don't understand if this is an emulator problem or i am misunderstanding the whole process. Did you have the same problem?
I have a simple solution for my application, like check a "first move" variable in touchesBegan, but this is a pure technical question.
Thank you in advance.
This is all i use, apart from drawRect that it's not important.
I guess it's not a problem in my code.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesBegan")
if (Draw!){
path.moveToPoint(touchPoint)
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesMoved")
if (Draw!){
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesEnded")
if (Draw!){
path.addLineToPoint(touchPoint
path = UIBezierPath() // Create new path for next line
self.setNeedsDisplay()
}
}
I am quite sure this is an emulator problem.
Please see the code below. It works like a charm at my end.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = false;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = true;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (didMove == true)
{
//finger was moved
NSLog(#"finger was moved");
}
else
{
//it's a tap
NSLog(#"finger not moved it's a tap");
}
}
While we are at it, can I bring your attention to UIGestureRecognizers? Try to use them since they make life a breeze.

resignFirstResponder in Swift

How can I implement it in the new language of Apple:
Objective-C code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UIView *view in self.view.subviews)
[view resignFirstResponder];
}
I have tried to do so. But the keyboard does not disappear:
Swift code:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
self.view.resignFirstResponder()
}
You could probably go with:
self.view.endEditing(true)
Try this
self.view.endEditing(true)

How to implement touch and hold for sprite kit?

I recently started working with sprite-kit. I know touchesBegan works for just one tap but is there something i can use that will recognize a touch being held down?
If you want to implement something like shooting then you need to start shooting in touchesBegan method and stop shooting in touchesEnded method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self startShooting];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self stopShooting];
}
For other purposes you can add UILongPressGestureRecognizer to the SKScene
Alternatively you can use a boolean with the update method:
bool isTouching = false;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
isTouching = true;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
isTouching = false;
}
-(void)update:(CFTimeInterval)currentTime {
if(isTouching){
//shooting!
}
}
You can easily combine method's of yours inside the isTouching block if you prefer, and use touchesBegan to lets say aim the bullets at the same time.
You don't need to worry about timers as the update method will keep executing the code block as long as isTouching == true
var isTouching = false
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
handleTouches(touches)
isTouching = true;
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
handleTouches(touches)
isTouching = false;
}
override func update(currentTime: NSTimeInterval) {
if isTouching{
//Shoot CODE!
}
}

IOS: correct number of touch on screen

In my app I need to catch the exact number of finger on the screen, I try two ways but I have 2 different problem.
First way:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSArray *allTouches = [touches allObjects];
int count = [allTouches count];
NSLog(#"number of touch:%d", count);
}
this give me a NOT accurate number of touches if I use more finger at the same time.
Second way:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
int num = [touches count];
totalTouch = totalTouch+num;
NSLog(#"number of touch:%d", totalTouch);
}
in this way I use a global var (totalTouch) that I increment everytime touchbegan is called and I have a perfect number of touches.
Naturally i set at '0' this var in the touchend
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
totalTouch = 0;
}
My problem is that, with the second way I do a control in touchbegan, this:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
int num = [touches count];
totalTouch = totalTouch+num;
if (totalTouch == numberToVerify){
//IT ENTER HERE EVERYTIME INSIDE THIS IF
}
else{
}
}
so everytime it enter inside if-conditions, and I don't want it, I want do this control only when I have a final number of touch...
Within your
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
int num = [touches count];
totalTouch = totalTouch+num;
NSLog(#"number of touch:%d", totalTouch);
}
You can get the number of fingers on the screen with
[[event allTouches]count]
This can be accessed from - (void)touchesBegan: - (void)touchesEnded: or - (void)touchesMoved:
In touchesBegan:withEvent:, the touches argument only contains touches in the “begin” phase (UITouchPhaseBegan). In touchesEnded:withEvent:, the touches argument only contains touches in the “end” phase (UITouchPhaseEnded). Similarly for touchesMoved:withEvent: and touchesCancelled:withEvent:.
If you want all touches known to the system, look at event.allTouches.
If you want all touches known to the system that belong to a specific view, look at [event touchesForView:someView].
UIEvent Class Reference
UPDATE For Swift 3 :
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
var countTouch = event?.allTouches?.count
//Do whatever you want with that
}
If you want to know if it changed at any moment, do the same in touchesMoved and put it in an array, you'll be able to analyze it.
Like this :
var countTouch:[Int] = []
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
countTouch.append((event?.allTouches?.count)!) //Not really necessary
//Do whatever you want with that
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
countTouch.append((event?.allTouches?.count)!)
}
Hope it helped someone, don't hesitate to edit or improve it. I'm a beginner in Swift, so it's totally possible there are mistakes.
The issue with your 2nd solution is that in your touchesEnded function, you are setting totalTouch to 0 instead of decrementing by the size of allObjects. The function touchesEnded does not get called when all touches are ended; it is called when any touches are ended.
If I touch once, touchesBegan is called. If I touch again simultaneously, touchesBegan is called again and totalTouch is now at 2. If I lift one of the touches, touchesEnded is called with an allObjects length of 1, because only one touch was lifted. If we had set the counter to 0 here instead of decrementing the counter by the size of allObjects, this would mess up the whole process. This is my solution:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
...
touchCount += touches.allObjects.count
...
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
...
touchCount -= touches.allObjects.count
...
}

Resources