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]];
});
}
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.
- (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];
I am making a custom iOS keyboard and have a UIControl subclass to represent my button. I am trying to get the same behaviour as the normal iOS keyboard:
User begins touch on one button
User drags over other buttons (need to detect this so they can highlight/dehighlight accordingly)
Register the actual keyboard "press" when the user lifts their finger; that is, the touch ends
I am testing using the touch tracking methods like this:
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super beginTrackingWithTouch:touch withEvent:event];
NSLog(#"Begin for %#", [self label]);
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super continueTrackingWithTouch:touch withEvent:event];
NSLog(#"Continue for %#", [self label]);
return YES;
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super endTrackingWithTouch:touch withEvent:event];
NSLog(#"End for %#", [self label]);
}
These methods are all called, except they are only ever called on the UIControl where the touch began.
What is the best way to recognise touches coming and going across all my buttons? Do I have to do it all via the parent view?
I'll select a better answer if offered... but in case anybody finds this by search, I've managed to get what I need this way:
Set userInteractionEnabled to NO for the button class (UIControl subclass)
Don't override any touch methods in the button class
Implement touchesBegan:withEvent:, touchesMoved:withEvent: and touchesEnded:withEvent: in the view controller
On each event, extract the location from the UITouch object
Iterate over all of the button subviews and find the one containing the touch location:
- (MyButton *)buttonForTouch:(UITouch *)touch
{
CGPoint windowLocation = [touch locationInView:keyboardView];
for (MyButton *button in buttons) {
if (CGRectContainsPoint([button frame], windowLocation)) {
return button;
}
}
return nil;
}
Having determined which button the user is interacting with, make the view controller send messages to the relevant buttons to adjust their appearance
If appropriate, keep a reference to the UITouch instance in touchesBegan:withEvent: so you can be sure that you're tracking the same one in the other methods
I think that you should have a single big UIControl which has different subviews (like UIButton) but tracks touches by itself like you did already but finds out which subview to highlight depending on the touch position.
I tried to use custom UIControl in my view controller. My custom class which subclasses the UIControl and allocate the instance for my custom control and adding in to my view controller's view by following code
I have implemented following delegates which returns for YES to ensure the continuous touch.
- (BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super beginTrackingWithTouch:touch withEvent:event];
return YES;
}
- (BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super continueTrackingWithTouch:touch withEvent:event];
return YES;
}
- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super endTrackingWithTouch:touch withEvent:event];
}
- (void)cancelTrackingWithEvent:(UIEvent *)event
{
NSLog(#"Touch cancelled");
}
But - (void)cancelTrackingWithEvent:(UIEvent *)event get called when I am tracking. After that I should have to taken up my finger and drag again. then only I receive begin and continue tracking delegates
According to your question, While tracking,cancelTrackingWithEvent: get called. Right. Check your view or superview with Gesture call backs. If You've added pan Gesture, This type of problem will rise. That is your control touch will begin and get tracked upto this tracking change to panning.
To solve this issue, set tag to your view and cancel gesture call as below.
During your view creation
yourView.tag = CANCELVIEWTAG;
Cancel gesture if touch happen in your view.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.tag == CANCELVIEWTAG) {
return NO;
}
return YES;
}
Is there a way I can query a UIView to determine if it currently being touched? The reason I ask is because I am using "touchedBegan", "touchesEnded", and "touchesMoved" to keep a UIView under a user's finger. However, if the user moves his/her finger really fast and manages to "escape" the window I want to remove that window.
I was thinking I could use a timer to periodically test each view to determine if it is currently being touched, if it is not, I will remove it. An "IsTouchedProperty" would be perfect.
Any thoughts?
I had a similar problem and solved it by using the tracking property. From the documentation:
A Boolean value that indicates whether the receiver is currently
tracking touches related to an event. (read-only)
This is a method on UIControl, but aren't you trying to create one?
You can also hitTest views. Check the apple docs
Since UIView doesn't inherit from UIControl you would need to subclass UIView and roll your own isTouching property using touch events. Something like:
// MyView.h
#interface MyView : UIView
#property (nonatomic, assign) BOOL isTouching;
#end
// MyView.m
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
self.isTouching = YES;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
self.isTouching = NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
self.isTouching = NO;
}