Detecting UITouch outside of view - ios

I have a UIButton which is acting as a "confirm" button. Basically, I want it to require two taps to trigger its action, so the first time you tap it, the picture will change to an "are you sure" type image, at which point, a second tap would trigger the action.
This works fine, but I want to set it up so that tapping anywhere outside of the button resets it back to the first image again so it requires 2 taps again.
Is there any way to detect if the user touches outside of a UIButton; perhaps is there a way to give it "focus" and check for a focus exit?
I thought of UITouch, but that will only send its event to the view you are touching that responds to it.

Attach a UITapGestureRecognizer to the button's superview (probably the view controller's content view.) Set userInteractionEnabled to true. Put your code that resets the button in the handler for the tap gesture recognizer.

As you know you can use UIControlEvent.touchUpInside for when the view is pressed
If you want to see when a press is outside of a view you can you UIControlEvent.touchUpOutside instead
button.addTarget(self, action:<SELECTOR>,forControlEvents: UIControlEvents.TouchUpOutside)

You can add UITapGestureRecognizer to get user touch outside button event
#interface YourViewController ()
#property NSInteger tapCount;
#property (weak) UITapGestureRecognizer *gestureRecognizer;
#end
add UITapGestureRecognizer
- (IBAction)buttonPressed:(id)sender {
self.tapCount ++;
if (self.tapCount == 1) {
// change button label, image here
// add gesture gesture recognizer
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOutSideAction)];
[tap setNumberOfTapsRequired:1];
[[[UIApplication sharedApplication] keyWindow] addGestureRecognizer:tap];
self.gestureRecognizer = tap;
} else if (self.tapCount == 2) {
// tap two times
self.tapCount = 0;
}
}
- (void)tapOutSideAction {
// reset button label, image here
// remove gesture recognizer
self.tapCount = 0;
[[[UIApplication sharedApplication] keyWindow] removeGestureRecognizer:self.gestureRecognizer];
}

Related

UILongPressGestures conflict

I have a container view which has two UILongPressGestureRecognizers on it (well not only those two, but other gestures are acting ok). One is to recognize one finger, the other is for two. When i try to fire gesture for one finger, it will not fire it's target method until finger is lift off. The second one for two fingers is working as expected, firing selector action after short period of time. When i disable gesture for two finger press, the one finger press gets back to normal. How gestures are initialized, first is one finger gesture, initialized inside vc setup.
- (void) setupGestures
{
[self.contentView addGestureRecognizer:self.longGestureRecognizer];
}
- (UILongPressGestureRecognizer *)longGestureRecognizer
{
if (!_longGestureRecognizer) {
_longGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressAction:)];
[_longGestureRecognizer setMinimumPressDuration:1];
}
return _longGestureRecognizer;
}
The second one is initalized a bit later, but not from host VC, but inside view which is added to it
//Called in host
rullerView = [[RullerView alloc] initWithContent:self.contentView viewController:self];
and in init of view:
//This is called from within previous call initWithContent:ciewController:
- (void)setupGestures
{
UILongPressGestureRecognizer *rullerGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(rullerTapAction:)];
[rullerGestureRecognizer setNumberOfTouchesRequired:2];
//view here is a content view passed into init, basically the same one
//to which first recognizer is added, however selector action is contained
//in this view, and this view is target for this recognizer
[view addGestureRecognizer:rullerGestureRecognizer];
}
Could this be a problem?? Already tried
[gesture requireGestureRecognizerToFail:otherGesture];
and delegate method of view which recognizers are added to:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}

How to show second button on pressing first one in Objective-C

I am new at iPhone programming (Objective C). I wanted to make button which will be visible on long pressing on another button. Just like system keyboard style. I should choose first or second button by holding my finger on them :-) How can I do this, I could not find tutorials. Thanks
create and attach the UILongPressGestureRecognizer instance to your UIButton.
-(IBAction)SelectImage:(id)sender
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressMethod:)];
[self.button addGestureRecognizer:longPress];
}
Then create the method that handles the gesture
- (void)longPressMethod:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateEnded )
{
NSLog(#"Long Press");
//Do your code here what you want to perform
}
}

IBAction not fired sometimes after UIGestureRecognizer handler

I'm trying to track hits on UI elements (tap and long press) using UIGestureRecognizer. After hit was tracked (let's say logged via NSLog) UI element should do it's job.
I'm creating gesture recognizers like this:
UITapGestureRecognizer* tap = [[UITapGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
tap.cancelsTouchesInView = NO;
tap.delegate = self;
[view addGestureRecognizer:tap];
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
longPress.cancelsTouchesInView = NO;
longPress.delegate = self;
[view addGestureRecognizer:longPress];
I've overridden some gesture recognizer methods:
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldReceiveTouch(UITouch*)_touch
{
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)_otherRecognizer
{
return YES;
}
Inside the gesture recognizer handler, I'm trying to find the exact subview of the tap by using the hitTest method.
-(void)OnGesture:(UIGestureRecognizer*)_recognizer
{
if([_recognizer.state == UIGestureRecognizerStateEnded])
{
if([_recognizer isKindOfClass:[UITapGestureRecognizer class]]
|| [_recognizer isKindOfClass:[UILongPressGestureRecognizer class])
{
CGPoint location = [_recognizer locationOfTouch:0 inView:_recognizer.view];
// my problem occurs here:
//---------------------------------------------------------------------------
UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
//---------------------------------------------------------------------------
NSLog(#"Hit on view: %#", hitView);
}
}
}
So my problem is:
Sometimes (1 out of 10 cases) when I press the UIButton OnGesture method fires, but the IBAction of the "Touch Up Inside" event of that button is not firing.
But when I comment out hitTest call:
//UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
the bug stops being reproducible. IBAction always gets called.
Why is this happening? How can I fix this?
P.S. there could be some typos in the sample code above.
According to the docs, in order for it to work:
This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.
So you might wanna do self.someSubview.userInteractionEnabled = YES;

Limit Gesture Recognizer to Only One Specific UIView?

I have a UIView called myView on myViewController. I have a UIGestureRecognizer called swipeLeft (code below) that detects when a user swipes left on it.
The problem is: myViewController recognises the same gesture on the whole screen and performs another action. So I would like my app to perform myMethod when you swipeLeft in this particular area of myViewController, the area being myView.
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(myMethod:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.delaysTouchesBegan = YES;
[self.myView addGestureRecognizer:swipeLeft];
More details: I am using RESideMenu and myViewController is the right menu, so when it is visible, the whole view of myViewController recognises swipes in all directions. I would like to change the recogniser in this particular UIView myView.
Thanks!
First you will need to add the swipe gesture to your view controllers header file.
#property (strong, nonatomic) UISwipeGestureRecognizer *swipeLeft;
If you look in DEMORootViewController.m, you will see this call:
self.rightMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"rightMenuViewController"];
This will call your awakeFromNib, and it's your first chance to do something. In here you will create that swipe gesture. You can not add it to your view yet though, because your outlets are not set at this point. The first time they are set is in viewDidLoad, so that's where you will add the gesture to your view. So add this to your view controllers implementation file
- (void)awakeFromNib
{
self.swipeLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(myMethod:)];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.swipeView addGestureRecognizer:self.swipeLeft];
}
- (void)myMethod:(UISwipeGestureRecognizer *)swipe
{
NSLog(#"Did swipe") ;
}
Finally you will need to tell the pan gesture in RESideMenu.m to fail whenever our swipeLeft gesture occurs. The way to do that is to add the following code to RESideMenu.m at line 220. That's at the end of the viewDidLoad method.
if ([self.rightMenuViewController isKindOfClass:[DEMOVC class]]) {
DEMOVC *rightVC = (DEMOVC *)self.rightMenuViewController;
if (rightVC.swipeLeft) {
[panGestureRecognizer requireGestureRecognizerToFail:rightVC.swipeGesture];
} } }
This is assuming your custom VC is called DEMOVC. You will also need to import your custom VC to RESideMenu.m like this:
#import "DEMOVC.h"
Please let me know if this worked out for you, and if there's something else I can help you with.

How can I tell a custom UIView with UITextField receive touch events

What I'm working with is I have a custom UIView that combines a UILabel with a UITextField as part of some user input. I have several of these throughout the view that is being displayed in the app. What would be good is to be able to have the user touch either the UILabel or the UITextField and then have the UITextField allow for input of the field.
Is there an easy way to do this?
Add a tap gesture recognizer to your label, and when tapped tell the corresponding textfield to become first responder (which brings up the keyboard).
// In your init or awakeFromNib:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTapped:)];
self.label.userInteractionEnabled = YES;
[self.label addGestureRecognizer:tap];
...
- (void)labelTapped:(id)sender
{
[self.textField becomeFirstResponder];
}
Yes, there is and I actually tested it. You can add a tap gesture recognizer to your view (the one that contains the label and the text view) like so:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
[self addGestureRecognizer:[tap autorelease]];
and then, inside viewTapped:
-(void)viewTapped:(id)sender {
[self.yourTextField becomeFirstResponder];
}
Hope this helps!
You can define UIGestureRecognizer delegate method and check that is the the required tap gesture area . If it is then return TRUE else return FALSE .
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourButton)) {//change it to your condition
return NO;
}
return YES;
}

Resources