I've tried to implement the UIGestureRecognizerDelegate in my ViewController and somehow the methods aren't called. This is the controller:
#import "DiaryEntryViewController.h"
#import "UINavigationController+BarManagement.h"
#interface DiaryEntryViewController ()<UIGestureRecognizerDelegate>
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (weak, nonatomic) IBOutlet UITextView *textView;
#end
#implementation DiaryEntryViewController
-(void)viewWillAppear:(BOOL)inAnimated{
[super viewWillAppear:inAnimated];
self.navigationController.barsHidden = NO;
}
-(void)viewDidAppear:(BOOL)inAnimated{
[super viewDidAppear:inAnimated];
[self.navigationController hideBarsWithDelay:2.0];
}
-(void)viewWillDisappear:(BOOL)inAnimated{
[self.navigationController setBarsHidden:NO animated:YES];
[super viewWillDisappear:inAnimated];
}
-(NSManagedObjectContext *)managedObjectContext{
return self.diaryEntry.managedObjectContext;
}
-(BOOL)saveDiaryEntry{
BOOL theResult = NO;
NSError *theError = nil;
theResult = [self.managedObjectContext save:&theError];
if(!theResult){
NSLog(#"saveItem %#", theError);
}
return theResult;
}
-(CGRect)visibleBounds{
CGRect theBounds = self.view.bounds;
if([self respondsToSelector:#selector(topLayoutGuide)] && [self respondsToSelector:#selector(bottomLayoutGuide)]){
theBounds.origin.y = [self.topLayoutGuide length];
theBounds.size.height -= [self.topLayoutGuide length] + [self.bottomLayoutGuide length];
}
return theBounds;
}
-(IBAction)toogleBars:(id)sender{
NSLog(#"toogleBars");
UINavigationController *theController = self.navigationController;
BOOL theHiddenFlag = theController.barsHidden;
[theController setBarsHidden:!theHiddenFlag animated:YES];
if(theHiddenFlag){
[theController hideBarsWithDelay:2.0];
}
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)inRecognizer{
NSLog(#"gestureRecognizerShouldBegin");
UIView *theView = self.textView;
CGPoint thePoint = [inRecognizer locationInView:theView];
return !CGRectContainsPoint(theView.bounds, thePoint);
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"bla");
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"ble");
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"blä");
return YES;
}
#end
It does call toogleBars methods, but none of the recognizer methods.
Do not forget to declare and add the recognizer to the view you want to detect that tap or swipe in
example:
Add a property like "theTapRecognizer" to the VC.
Alloc and init that recognizer:
self.theTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(someMethod:)];
self.theTapRecognizer.delegate = self;
[someView addGestureRecognizer: selftheTapRecognizer];
someView is the placeholder text to the view you want to init that recognizer in, it can be the whole self.view or some subview,
you can listen for any interaction with that gesture recognizer with the following delegate method
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
Assuming you have added the UIGestureRecognizer from the storyboard, you need to remember to CTRL + Drag the gesture object to the UIViewController yellow circle image and set the delegate.
Your UIGestureRecognizer object will be on the top of the UIViewController view. CTL Drag this to the yellow circle ViewController icon
Then this pop-up will show and select 'Delegate' so that the gesutureRecognizer will use the delegate methods.
Related
I have a button in my interface builder (using nibs by the way), i control dragged from the button to the view controller .h file and called it "cancelReminder" (and set it to be action):
#interface HomeViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *homeLabel;
#property (strong, nonatomic) IBOutlet UIScrollView *myScrollView;
- (IBAction)cancelReminder:(id)sender;
#end
this created me a method in the .m file, and I just nslog'd something there that says "cancel" to test it.
In the interface builder I controlled dragged from the button to the files owner to make sure its connected to the method "cancelReminder".
this should perform it right?? its not.
something important to mention: the cancel button is a child of a view, and this view is a child of the main view.
this looks like that:
I know I shouldnt do this but just for testing the behaviour if I control drag from the cancel button to the file owner and select view, when i run the app, i see black screen and i can see the button and its working...by working i mean the method is responding:
I took it back, i tried it so maybe someone counld understand from this behaviour what is happening.
please please help me figure this out :/// it happened before :(
thannks a bunch!!
my viewDiLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.slidingView.hidden = YES;
// black BG to make the background darker when the reminder view is sliding in
self.reminderBG.hidden = YES;
self.navigationController.navigationBarHidden = YES;
self.homeLabel.font = [UIFont fontWithName:#"Candara-Bold" size:40];
self.homeLabel.text = stackTableViewController.currentTarget;
self.homeLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.homeLabel.numberOfLines = 0;
self.myScrollView.delegate = self;
//setting up the button bounds
self.buttonBounds = self.createButton.bounds;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapResponder)];
tapRecognizer.numberOfTapsRequired =1;
tapRecognizer.numberOfTouchesRequired = 1;
[self.homeLabel addGestureRecognizer:tapRecognizer];
}
and my action sheet method that slides the reminderView in:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
} else if (buttonIndex == 1) {
self.reminderBG.hidden = NO;
self.slidingView.hidden = NO;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
self.slidingView.frame = CGRectMake(0,0,320,20);
self.view.frame = CGRectMake(0,0,320,460);
[UIView commitAnimations];
}
}
Problem might be caused by gesture recognizer. Try to add delegate after creating tapRecognizer:
tapRecognizer.delegate = self;
And use delegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIButton class]]){
return NO;
}
return YES;
}
I have been trying to implement a UITapGestureRecognizer that will dismiss a modal view controller by detecting a tap outside the modal view controller on iOS 8.
The issue I am seeing is that in landscape mode on iPad that the tap gesture is only recognised inside parts of the view controller. My modal view is 380 width x 550 height. When orientation is in landscape (with the home button at the right) the tap gesture is detected inside the bottom half of the modal view. When orientation is in landscape but with the home button at the left hand side the tap gesture is detected inside the top half of the modal view.
Problem 1: The tap gesture is only detected inside the modal view when it should be detected outside.
Problem 2: No detection in some areas of the view in landscape orientation.
Here is the code I used:
UIViewController+DismissOnTapOutside.h
#import <UIKit/UIKit.h>
#interface UIViewController (DismissOnTapOutside)
- (void)registerForDismissOnTapOutside; // Call in viewDidAppear
- (void)unregisterForDismissOnTapOutside; // Call in viewWillDisappear
#end
UIViewController+DismissOnTapOutside.m
#import "UIViewController+DismissOnTapOutside.h"
#import <objc/runtime.h>
static char gestureRecognizerKey;
static char gestureRecognizerDelegateKey;
#interface SimpleGestureRecognizerDelegate : NSObject <UIGestureRecognizerDelegate>
#end
#implementation SimpleGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return [otherGestureRecognizer isMemberOfClass:[UITapGestureRecognizer class]];
}
#end
#interface UIViewController ()
#property (assign, nonatomic) UIGestureRecognizer *gestureRecognizer;
#property (strong, nonatomic) SimpleGestureRecognizerDelegate *gestureRecognizerDelegate;
#end
#implementation UIViewController (DismissOnTapOutside)
- (void)setGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
objc_setAssociatedObject(self, &gestureRecognizerKey, gestureRecognizer, OBJC_ASSOCIATION_ASSIGN);
}
- (UIGestureRecognizer *)gestureRecognizer
{
return objc_getAssociatedObject(self, &gestureRecognizerKey);
}
- (void)setGestureRecognizerDelegate:(SimpleGestureRecognizerDelegate *)delegate
{
objc_setAssociatedObject(self, &gestureRecognizerDelegateKey, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIGestureRecognizer *)gestureRecognizerDelegate
{
id delegate = objc_getAssociatedObject(self, &gestureRecognizerDelegateKey);
if (!delegate)
{
delegate = [[SimpleGestureRecognizerDelegate alloc] init];
self.gestureRecognizerDelegate = delegate;
}
return delegate;
}
- (void)handleDismissTap:(UIGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateEnded)
{
UIView *view = self.navigationController.view ?: self.view;
// Passing nil gives us coordinates in the window
CGPoint location = [gesture locationInView:nil];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
location = CGPointMake(location.y, location.x);
}
}
// Then we convert the tap's location into the local view's coordinate system
location = [view convertPoint:location fromView:self.view.window];
if (![view pointInside:location withEvent:nil])
{
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
}
}
- (void)registerForDismissOnTapOutside
{
// This approach is attributed to Danilo Campos:
// http://stackoverflow.com/a/6180584/456434
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDismissTap:)];
recognizer.delegate = self.gestureRecognizerDelegate;
recognizer.numberOfTapsRequired = 1;
recognizer.cancelsTouchesInView = NO;
self.gestureRecognizer = recognizer;
[self.view.window addGestureRecognizer:recognizer];
}
- (void)unregisterForDismissOnTapOutside
{
[self.view.window removeGestureRecognizer:self.gestureRecognizer];
self.gestureRecognizer = nil;
}
#end
The key method that should set the correct location for tap detection does not seem to be setting the location correctly in landscape orientation in iOS 8:
- (void)handleDismissTap:(UIGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateEnded)
{
UIView *view = self.navigationController.view ?: self.view;
// Passing nil gives us coordinates in the window
CGPoint location = [gesture locationInView:nil];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
location = CGPointMake(location.y, location.x);
}
}
// Then we convert the tap's location into the local view's coordinate system
location = [view convertPoint:location fromView:self.view.window];
if (![view pointInside:location withEvent:nil])
{
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
}
}
All of the examples I have looked at on StackOverflow in relation to this issue are adamant that the above code works. But in my case it simply does not detect taps outside the modal view controller.
How can a tap outside a modal be detected in iOS 8 on iPad in landscape orientation?
I'm using a UIPicker to populate a UITextField instead of a keyboard. This works great but I can't close the UIPicker now. I want to be able to tap anywhere on the screen and close the UIPicker. I've tried each of the touches method an none will fire.
setUserInteractionEnabled:YES.
Not sure if it makes a difference but I'm using a StoryBoard
Should I have set something up in my AppDelegate to listen for touches?
Here is my .h
#import <UIKit/UIKit.h>
#interface RNMemberTableViewController : UITableViewController<UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UIPickerView *behaviorPicker;
#property (nonatomic, weak) IBOutlet UIDatePicker *dateOfRecordPicker;
#property (nonatomic, strong) NSArray *behaviorLevels;
#property (weak, nonatomic) IBOutlet UITextField *behaviorTextField;
#end
here is some of the implementation...
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setUserInteractionEnabled:YES];
[self buildBehaviorPicker];
NSLog(#"Member View");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"test");
[self.view touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
int tCount = touch.tapCount;
NSLog(#"Touched %d", tCount);
}
- (void) buildBehaviorPicker
{
behaviorLevels = [[NSArray alloc] initWithObjects:#"Unsatisfactory", #"Needs Improvement", #"Satisfactory", #"Outstanding", nil];
UIPickerView *pickerView = [[UIPickerView alloc] init];
pickerView.dataSource = self;
pickerView.delegate = self;
[pickerView selectRow:2 inComponent:0 animated:NO];
self.behaviorTextField.inputView = pickerView;
}
Thanks in advance
-Bob
You can always try the following:
-(void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapReceived:)];
[tapGestureRecognizer setDelegate:self];
[self.view addGestureRecognizer:tapGestureRecognizer];
}
-(void)tapReceived:(UITapGestureRecognizer *)tapGestureRecognizer
{
// do something, like dismiss your view controller, picker, etc., etc.
}
Hope this helps ...
You can add a transparent button of the same size as the controller's view under the picker.
Set button.hidden = NO when the picker is shown and set button.hidden = YES when the picker is hidden.
There are ways to hide the picker. The easiest way is to set picker.hidden = YES.
If you are using UIScrollView then
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
will not be called, as touchesBegan is a method of UIView and not of UIScrollView.
I have been trying to do this simple thing : adding an action to a simple custom view. I have looked over the internet and found two "easy" solution :
UITapGestureRecognizer
UIButton
I want to do this programmatically and I just need to handle the tap.
Here is my code so far, I've tried both solutions separately and together and it doesn't work !
.m
#import "AreaView.h"
#implementation AreaView
#define GREY 27.0/255.0
#define PINK_R 252.0/255.0
#define PINK_G 47.0/255.0
#define PINK_B 99.0/255.0
- (id) initWithFrame:(CGRect)frame imageName:(NSString *)imageName areaName:(NSString *)areaName minimumSpending:(int)minimumSpending andCapacity:(int)capacity
{
self = [self initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithRed:GREY green:GREY blue:GREY alpha:1];
self.userInteractionEnabled=YES;
//Init variables
_areaName=areaName;
_capacity=capacity;
_minimumSpending=minimumSpending;
//Image view
_logoImageView = [[UIImageView alloc]initWithFrame:CGRectMake(5, 4, 66, 50)];
//_logoImageView.image = [UIImage imageNamed:imageName];
_logoImageView.backgroundColor = [UIColor grayColor];
//Label
_areaNameLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 54, 76, 18)];
_areaNameLabel.textAlignment = NSTextAlignmentCenter;
_areaNameLabel.textColor = [UIColor whiteColor];
_areaNameLabel.font = [UIFont systemFontOfSize:12.0];
_areaNameLabel.text = areaName;
//button
_button = [[UIButton alloc]initWithFrame:self.bounds];
_button.userInteractionEnabled=YES;
_button.backgroundColor=[UIColor yellowColor];
[_button addTarget:self action:#selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
//tap gesture racognizer
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setDelegate:self];
[self addGestureRecognizer:tapRecognizer];
[self addSubview:_logoImageView];
[self addSubview:_areaNameLabel];
[self addSubview:_button];
}
return self;
}
-(void)handleTap:(UIButton *)button
{
NSLog(#"tapped!");
}
-(void)tapped:(UITapGestureRecognizer *)recognizer
{
NSLog(#"tapped!");
}
#end
.h
#import <UIKit/UIKit.h>
#interface AreaView : UIView <UIGestureRecognizerDelegate>
#property (nonatomic) UIImageView *logoImageView;
#property (nonatomic) UILabel *areaNameLabel;
#property (nonatomic) NSString *areaName;
#property (nonatomic) int minimumSpending;
#property (nonatomic) int capacity;
- (id) initWithFrame:(CGRect)frame imageName:(NSString *)imageName areaName:(NSString *)areaName minimumSpending:(int)minimumSpending andCapacity:(int)capacity;
#property (nonatomic, strong) UIButton *button;
#end
Thanks for your help!
EDIT
The problem is that both handleTap and tapped are never fired even if I comment the button solution or the tap gesture one to test them separately. For the button implementation, I can see it on my interface but clicking on it does nothing.
My UIView is then added programmatically several times (for several views) in a UIScrollview.
EDIT 2
The problem is more complicate than that. The custom view is inside a scrollview which is inside another different custom view whose main function is to rewrite hittest, so that touches on this view are held by the scrollview. (Here is the purpose of all that).
It seems that as long as hittest is involved, it doesn't work.
Implement this delegate method, which will help you.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
id touchedView = gestureRecognizer.view;
if ([touchedView isKindOfClass:[UIButton class]])
{
return NO; //It won't invoke gesture method, But it'll fire button method.
}
return YES;
}
I'm not sure why your UIButton and UITapGestureRecognizer selectors aren't firing. However another option to handle taps on a view is to simply override the touchesEnded:withEvent method:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
//handle a tap
}
That way, you won't have to create a UIButton or a UITapGestureRecognizer object at all.
I am trying to detect double tap in a uiwebview. following is the code. when i debug, debugger comes to initwithcoder but never to touchesbegan nor to selector function. why so ?
I am putting a UIWebView in my xib file and setting identity inspector class to MyWebView
#import "MyWebView.h"
#implementation MyWebView
- (void) initTap {
UITapGestureRecognizer *singleFingerDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleFingerDoubleTap:)];
singleFingerDoubleTap.numberOfTouchesRequired = 1;
singleFingerDoubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:singleFingerDoubleTap];
[singleFingerDoubleTap release];
}
- (id)initWithCoder:(NSCoder*)coder
{
if (self = [super initWithCoder:coder]) {
// Initialization code.
[self initTap];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
[self initTap];
}
return self;
}
-(void)handleSingleFingerDoubleTap:(id)sender {
NSLog(#"Doulble click detected !");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
//put you zooming action here
NSLog(#"Doulble click detected !");
}
}
- (void)dealloc {
[super dealloc];
}
#end
You just need to add delegate to the recognizer
singleFingerDoubleTap.delegate = self;
Then implement the following method to return YES.
#pragma mark Gesture recognizer delegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Don't forget to add conform declaration to your class file.
Like this
#interface ORWebViewController : UIViewController<UIGestureRecognizerDelegate>
In case, you want to override the default zooming action on double tap, this will guide you.
UPDATE:
Please do not consider above mentioned reference as the author has moved it.
You can find the references from Disabling default zoom effect.