- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *aTouch in touches) {
if (aTouch.tapCount >= 2) {
// The view responds to the tap
}
}
}
I'm using the code above to detect double tap gesture; however, how can I set the code to happen only once?
In other words, when you tap once, the character jumps. When you tap twice in quick succession, the character will double jump. But how do you set the taps in a way that the character will not continuously double jump and go higher off the single-view without initially taping once?
A very simple approach of achieving this is by declaring a global bool variable and set its value once the double tap has been detected!
Something like this:
#interface MyViewController()
{
bool isTapped;
}
#end
#implementation MyViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *aTouch in touches) {
if (aTouch.tapCount >= 2) {
if(!isTapped) {
// The view responds to the tap
isTapped = YES;
}
}
}
}
#end
Hope this helps
Not sure if this helps and also just take one flag and set it yes or no accordingly:-
UILongPressGestureRecognizer *tapRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapRecognizer.delegate = self;
tapRecognizer.minimumPressDuration = //Up to you;
[self.someView addGestureRecognizer:tapRecognizer];
Related
I have a custom view which is not being deallocated. I dismiss controller on close button pressed. Now if I only press the button the view is deallocated alright. But If press button with one finger with other finger touching the view its not deallocated on dismiss but on the next touch event.
Its UITouch which is keeping the reference of my view and not releasing it. How can I fix this?
Here is my code for my close action:
- (IBAction)closePressed:(UIButton *)sender {
NSLog(#"Close pressed");
if (self.loader)
[self.loader cancelJsonLoading];
[self.plView quit];
[self dismissViewControllerAnimated:YES completion:nil];
}
Did you try to call:
[self.view resignFirstResponder];
That should cancel all pending UITouches.
If this doesn't work, you can keep trace of your touches:
define a NSMutableSet where you store current touches:
NSMutableSet *_currentTouches;
in your init():
_currentTouches = [[NSMutableSet alloc] init];
And implement:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super.touchesBegan:touches withEvent:event];
[_currentTouches unionSet:touches]; // record new touches
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super.touchesEnded:touches withEvent:event];
[_currentTouches minusSet:touches]; // remove ended touches
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super.touchesEnded:touches withEvent:event];
[_currentTouches minusSet:touches]; // remove cancelled touches
}
Then, when you need to clean your touches (when you release your view for instance):
- (void)cleanCurrentTouches {
self touchesCancelled:_currentTouches withEvent:nil];
_currentTouchesremoveAllObjects];
}
It is, I think, a bit hacky, but the doc says:
When an object receives a touchesCancelled:withEvent: message it
should clean up any state information that was established in its
touchesBegan:withEvent: implementation.
I am following through the Stanford iOS class and have a bit of a design question. In the class we are making a card matching game that has some 20 cards on screen or so. I recently made the cards UIViews so I could draw them properly.
I gave them each a method tap that will swap faceUp to YES/NO, thus flipping the cards. I add the gesture recognizer to each in my ViewController and it works. The individual cards know when they're touched and flip.
However, I need to know in my ViewController that a cardView has been touched... and which one.
How/what ways do I have to do this? Can I broadcast something in my View that the ViewController will listen for? Can I have my viewController handle that taps (but is there a way to get the sending view if I do this?) I apologize if this is really base, but I'm new to iOS and would like to not learn by patching and implementing a broken MVC pattern.
EDIT: Just for information, this is my final implementation.
Each CardView has a tap recognizer on it. When a tap is recognized it calls:
- (void)cardTapped:(UIGestureRecognizer *)gesture
{
UIView *view = [gesture view]; // This method is what I was looking for.
if ([view isKindOfClass:[PlayingCardView class]]) {
PlayingCardView *playingCardView = (PlayingCardView *)view;
[playingCardView flip]; // flips card
// Game code
if (!self.game.hasStarted) {
[self startGame];
}
int cardIndex = [self.cardViews indexOfObject:playingCardView];
[self.game chooseCardAtIndex:cardIndex];
[self updateUI];
}
}
The tag property will tell you which view has been tapped. Set the proper tag when you create your view and in your action method that's been triggered on tap you can call a delegate method that will notify your delegate about which view has been tapped. Make your viewcontroller has the delegate and it will received the notification.
// your target method will look like this:
- (void) didTap:(id)sender {
//... your code that handle flipping the card
[self.delegate didTapOnCard:self]; // where delegate is your view controller
}
You can use touchesBegan method to detect which view was tapped.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
NSLog(#"%d", [touch view].tag); // Considering you have set tags for your UIViews..
if([touch view] == cardView1) // Considering you have a view as cardView1
{
NSLog(#"cardView1 is tapped");
}
}
In your UIView card class add
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)e {
UITouch *touch = [touches anyObject];
if ([self pointInside:[touch locationInView:self] withEvent:nil]) {
[self touchesCancelled:touches withEvent:e];
// Add your card flipping code here
}
}
The broadcast approach: in your UIView's tap method, send a notification:
[[NSNotificationCenter defaultCenter] postNotificationName:#"cardTapped" object:self userInfo:#{ #"card": #(self.cardId), #"faceUp": #(self.faceup) }];
and in your ViewController subscribe to that notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cardTapped:) name:#"cardTapped" object:nil];
and implement
-(void)cardTapped:(NSNotification*)notification
{
int card = [[notification.userInfo objectForKey:#"card"] intValue];
BOOL faceUp = [[notification.userInfo objectForKey:#"faceUp"] boolValue];
}
I have a problem as following, I've developed a ios app, to be concice , It has a UIViewController as parent, also it has a button , and the UIViewController popup a transparent UIView as a mask. When I click on the UIView(exactly within the underlying button boundary ) , the button could not receive any event(such as "touch up inside"), how could the button get the "touch up inside" event from transparent the UIView which is above the UIViewController?
This is not possible directly as the event triggered will not be for the button as button is not visible
(ie. another View is completly covering the button and is blocking the interaction with the user).
But i can give u a work around.
1.Declare a Bool Variable in your UIViewController
2.Implement the touches methods as shown below
- (void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
if(CGRectContainsPoint(button.frame, p) && !boolVariable) {
boolVariable = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
// If the below condition is true then it mean there the user tapped on the same location as that of button..(touchesEnded and touchesCanceled was not called) so the event is just like touchUpInside
if(CGRectContainsPoint(button.frame, p) && boolVariable) {
boolVariable = NO;
[Here you can call the method which you wanted to call on touchUpInside of the button];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
I was not able to test the able code on Xcode but i think it will work..
Note: The frame of the button should be with respect to the UIViewController.
Hope this helps u out :)
UIView instances, do not listen for touches. In order to get callbacks for events such as "touch up inside" are only sent to subclasses of UIControl
The most basic concrete subclass is UIButton.
Without code or more details about your app's setup, it's difficult to give better advice.
who knows which would be the following gesture:
Imagine I click within some region (say a button), I hold the finger down,
don't release it, and move the finger outside the region area??
is there some gesture to it?
I have a button which starts up in a selected/highlighted state, but if a user clicks
on it (using a finger), doesn't release his/her finger, moves his/her finger outside the button area, my button gets deselected - which is something I don't want. Can someone help?
You need to use, UIGesuture recongnizers: UIPanGestureRecognizer.
You need to handle "Touch Up Outside" button event.
==EDIT==
You can override UIControl's *TrackingWithTouch:withEvent: methods to get the behaviour you want:
#interface CustomButton : UIButton
#end
implementation
#interface CustomButton()
// Auxiliary property
#property(nonatomic, assign) BOOL isHighlightedBeforeTouch;
#end
#implementation CustomButton
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
self.isHighlightedBeforeTouch = self.highlighted;
return [super beginTrackingWithTouch:touch withEvent:event];
}
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
BOOL result = [super continueTrackingWithTouch:touch withEvent:event];
if( self.isHighlightedBeforeTouch && !CGRectContainsPoint(self.bounds, [touch locationInView:self]) ) {
dispatch_async(dispatch_get_main_queue(), ^{
self.highlighted = self.isHighlightedBeforeTouch;
});
}
return result;
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
self.highlighted = self.isHighlightedBeforeTouch;
});
[super endTrackingWithTouch:touch withEvent:event];
}
#end
to change highlighted property e.g. on when button is pressed:
-(IBAction)buttonPressed:(UIButton*)sender
{
dispatch_async(dispatch_get_main_queue(), ^{
[sender setHighlighted:![sender isHighlighted]];
});
}
I have subclassed a UIView that already handles single touches and drags. I want to enhance the interaction of this view so that, while dragging, if the user touches with a second finger (anywhere else in the view), then the system prints a message. I've made a stab at the code:
In my header file I've declared:
NSString *key; // This unique key identifies the first touch
My .m file I have:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if (key == NULL)
{
key = [[[NSValue valueWithPointer:t] description] copy];
}
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
NSLog(#"calling parent to handle single touch");
[super touchesBegan:[NSSet setWithObject:t] withEvent:event];
}
else
{
[self twoTouchDetected];
}
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
[super touchesMoved:[NSSet setWithObject:t] withEvent:event];
}
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
[super touchesEnded:[NSSet setWithObject:t] withEvent:event];
key = NULL;
}
}
}
Unfortunately there are issues with this implementation. The first time (while dragging with one finger) if I touch with the second figer, the system will register it immediately. However, the second time I touch with a second finger (while still continuing to drag with the first finger), the second finger touch does not register until the first finger is lifted up. The events from the second finger are backed up...
What is also strange is that sometimes, the parent gets called with touch data from the 2nd finger and not the 1st.
It turns out my code worked, but the problem was that I subclassed an object belonging to the Core Plot framework. This framework does weird things to their objects and therefore the touches were coming back in the wrong order.
I created an empty project to receive touches and everything came out great.