I have created a UIViewController, which contains a UIView object.
I want the UIView object response to a single tap by 1 finger, and also pinch by 2 fingers.
I have clicked the "User Interaction Enabled" and the "Multiple touch" options in the xib.
I create both UITapGestureRecognizer, and UIPinchGestureRecognizer inside the UIView function initWithFrame:, as it is the only entry point for the UIView object. But the object doesn't response to the UIGestureRecognizer objects. Why? Or, where is the best place to put the UIGestureRecognizer objects?
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Your problem is that when you instantiate your custom view subclass in IB, initWithCoder: is called. If you had done it programmatically using initWithFrame: it would have worked properly.
Two ways you can fix this,
Move the gestures setup to a different method and call them in both initWithFrame: and initWithCoder:. I would recommend doing this if you intend to reuse this component and expose some kind of callbacks on gestures.
If you want to implement this once and have a lot of interacting with the controller element, add them in viewDidLoad.
I tried your code and it works for me.
Where are you setting the view? Maybe it has any other view in front, or its superview has userInteractionEnabled disabled.
I created a UIView subclass and added this code:
-(void) handleSingleTap:(UITapGestureRecognizer *)gr {
NSLog(#"handleSingleTap");
}
-(void) handleDoubleMove:(UIPinchGestureRecognizer *)gr {
NSLog(#"handleDoubleMove");
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Then, in my controller:
-(void) viewDidLoad {
[super viewDidLoad];
MyView * myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor greenColor];
[self.view addSubview:myView];
[myView release];
}
And it works.
i think if you are working with .xib, initialization can also be done in
-(void)awakeFormNib;
Adding the GestureRecognizers is typically done in the viewDidLoad method of the UIViewController.
Related
I added custom UIView with XIB. I just tried to perform button actions and tapgestures , nothing is working eventhough user interaction enabled for all elements including ContentView.
Intializing UIView
-(void)initializeSubviews {
self.backgroundColor = [UIColor clearColor];
[[[NSBundle mainBundle]loadNibNamed:#"DatesView" owner:self options:nil]firstObject];
[self addSubview:self.contentView];
// self.contentView.frame = self.bounds;
[self.fromDateButton addTarget:self action:#selector(tapOnfromDate:) forControlEvents:UIControlEventTouchUpInside];
self.fromDateButton.backgroundColor = [UIColor redColor];
}
Tap gestures
UITapGestureRecognizer *fromDateTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnfromDate)];
[datesView.contentView addGestureRecognizer:fromDateTapRecognizer];
[datesView.fromMonthYearLabel addGestureRecognizer:fromDateTapRecognizer];
[datesView.fromDayLabel addGestureRecognizer:fromDateTapRecognizer];
Button Action
[datesView.fromDateButton addTarget:self action:#selector(tapOnfromDate) forControlEvents:UIControlEventTouchUpInside];
UIView interface
Couple things...
1) By commenting out this line in your view subclass:
// self.contentView.frame = self.bounds;
the view ends up with a frame of 0,0. The button and labels etc are visible because the view defaults to .clipsToBounds = NO, but the objects do not receive user interaction.
2) A UITapGestureRecognizer is a single instance. If you add it to one view, then try to add it to additional views, it will only exist on the last view to which is was added.
Try it like this (be sure to un-comment the above line):
- (void)viewDidLoad {
[super viewDidLoad];
// do all the loading stuff here...
// local declaration
UITapGestureRecognizer *tapRecognizer;
// Optional --- make *sure* user interaction is enabled
[datesView.contentView setUserInteractionEnabled:YES];
[datesView.fromMonthYearLabel setUserInteractionEnabled:YES];
[datesView.fromDayLabel setUserInteractionEnabled:YES];
// new recognizer
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnfromDate:)];
// add it to contentView
[datesView.contentView addGestureRecognizer:tapRecognizer];
// new recognizer
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnfromDate:)];
// add it to fromMonthYearLabel
[datesView.fromMonthYearLabel addGestureRecognizer:tapRecognizer];
// new recognizer
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnfromDate:)];
// add it to fromDayLabel
[datesView.fromDayLabel addGestureRecognizer:tapRecognizer];
// add target action to fromDateButton
[datesView.fromDateButton addTarget:self action:#selector(fromDateButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) tapOnfromDate: (UITapGestureRecognizer *)recognizer {
NSLog(#"Tapped! %#", recognizer.view);
}
- (void) fromDateButtonTapped: (id)sender {
NSLog(#"Button Tapped! %#", sender);
}
I added tapGesture for self.view and UILabel (subview of mainView), each performs different selectors.
But the only main view tapGesture is being called and label tapgesture is not being called. How it is handled?
Here is the code:
UITapGestureRecognizer *tapGesForSelf = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForSelf:)];
[self.view addGestureRecognizer:tapGesForSelf];
UITapGestureRecognizer *tapLblClick = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForLbl:)];
[lbl addGestureRecognizer:tapLblClick];
For two selectors only one method is called tapGesForSelf.
Here lbl is the subview of self.view.
Try this
- (void)viewDidLoad {
[super viewDidLoad];
[_lbl setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapGesForSelf = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForSelf:)];
[self.view addGestureRecognizer:tapGesForSelf];
UITapGestureRecognizer *tapLblClick = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForLbl:)];
[_lbl addGestureRecognizer:tapLblClick];
[tapGesForSelf requireGestureRecognizerToFail:tapLblClick];
}
- (void)tapGesForSelf:(UITapGestureRecognizer *)gesture
{
NSLog(#"self");
}
- (void)tapGesForLbl:(UITapGestureRecognizer *)gesture
{
NSLog(#"label");
}
I post answer for your question now once I tried and it worked well.
First see the label in design.I set label text as "Tap Me"
Now I set the code for view and label
- (void)viewDidLoad
{
[super viewDidLoad];
//TapGesture for Label
UITapGestureRecognizer *tapLabel = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionTapLabel:)];
tapLabel.delegate = self;
tapLabel.numberOfTapsRequired = 1;
lblTapMe.userInteractionEnabled = YES;
[lblTapMe addGestureRecognizer:tapLabel];
//TapGesture for View
UITapGestureRecognizer *tapMainView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionTapMainView:)];
tapMainView.delegate = self;
tapMainView.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapMainView];
}
//Action method for Label
-(void)actionTapLabel:(UITapGestureRecognizer *)gestureOnLabel{
UILabel *label = (UILabel *)gestureOnLabel.view;
NSLog(#"Lable text is - %#",label.text);
}
//Action method for View
-(void)actionTapMainView:(UITapGestureRecognizer *)gestureOnMainView{
NSLog(#"The Main view is tapped");
}
Output Screenshot
Please setUserInteractionEnabled:YES for interact the TapGesture
[lbl setUserInteractionEnabled:YES];
I tried to use initWithFrame but I am not able to display the UIView, This kind of code solved many questions as I saw here but not in my case:
- (id)initWithFrame:(CGRect)frame myParams:(NSArray*)params;
.m file
- (id)initWithFrame:(CGRect)frame myParams:(NSArray*)params{
self = [super initWithFrame:frame];
if(self){
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
[self addSubview:view];
}
return self;
}
Here is how I am trying to add the view in UIViewController:
Maybe here I am doing it wrong, right?
Custom *view = [Custom alloc] initWithFrame:self.view.bounds myParams:array];
[self.view addSubview:view];
So, my question is it possible to add UITapGesture in the UIView generated in this class, cause it is not firing:
.h file
+ (UIView *)viewParams:(NSArray*)params;
.m file
+ (UIView *)viewWithParams:(NSArray*)params{
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
NSLog(#"read params %#", params);
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
return view;
}
- (void)test{
NSLog('it works...');
}
UPDATE : As instance method is working. But as class method I can not make it work. somebody know if possible?
1st Part
If you are using XIB for your customView then you need to use awakeFromNib method. But if you are trying to create through code only then just use initWithFrame: method and add your gesture recognizer in the initializer.
2nd Part
For firing tap gesture recognizer you are adding recognizer target self i.e your customView. So event will fire in your customViewClass class only then to get these events in your controller you need to use protocol and delegate. And set the delegate of customView to whatever controller you want to use in the controller.
View will add after firing the initwithcoder just use this code in init with coder ...
dispatch_after(0.1, dispatch_get_main_queue(), ^(void)
{
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
[self addSubview:view];
});
}
return self;
}
Minimum Time interval 0.1 max time interaval set as per your data load requirement
I am new in IOS Dev, was searching onclick event for UIView and i found this code that worked perfectly. But it doesnt call specific UIView as i am using multiple UIViews and want to add event on all of them separately while this code calls handleSingleTap onclick of either UIView. plz help..
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:[recognizer.view superview]];
NSLog(#"Clicked");
}
You can create UITapGestureRecognizer and add it to the views you want it to be clicked.
You can also set up tag property on each view to find out which view was clicked.
For example:
UITapGestureRecognizer *singleFingerTap1 =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
view1.tag = 1;
[view1 addGestureRecognizer:singleFingerTap1];
UITapGestureRecognizer *singleFingerTap2 =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
view2.tag = 2;
[view2 addGestureRecognizer:singleFingerTap2];
//..and so on
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
UIView *viewClicked = recognizer.view;
NSLog(#"Clicked: %d", viewClicked.tag);
}
The other solution is add one gesture recogniser to the main view (the view which hold the others view, something as you posted) and in handleSingleTap: method check if the point is inside the view:
BOOL isPointInsideView1 = [view1 pointInside:location withEvent:nil];
BOOL isPointInsideView2 = [view1 pointInside:location withEvent:nil];
But ic can failed when one view override another.
In my project I would like to make a hidden image view appear when it is tapped for more than 3 seconds. I know I need to use NSTimer, but I have never created a UIImageView touch event. How can I combine the TapGestureRecognizer with NSTimer to achieve what I want to do? I am completely new to touch events in iOS, and I am just beginning to explore this. So, any help would be appreciated. Thank you!
UPDATE:
I implemented the UILongPressGestureRecognizer as below, but now, the hidden image appears even if I press somewhere outside of the image. How can I make it appear only if pressing the hidden image itself?
- (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPress:)];
longPress.numberOfTouchesRequired = 1;
longPress.minimumPressDuration = 3;
[self.view addGestureRecognizer:longPress];
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
BrokenGlass.hidden = NO;
}
}
You don't want a UITapGestureRecognizer and a timer, you want a UILongPressGestureRecognizer.
The image is appearing because you are using the gesture recognizer in the entire view.
[**self.view** addGestureRecognizer:longPress];
The gesture will not trigger on a hidden element.
Here is my solution:
#interface ViewController () <UIGestureRecognizerDelegate>
#property (nonatomic, strong) UIImageView *imageView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create the imageView
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
// enable the user interaction in the imageView (otherwise it will not receive events)
_imageView.userInteractionEnabled = YES;
// add as a subview of the main view
[self.view addSubview:_imageView];
// create the gesture recognizer
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
longPressGesture.delegate = self;
longPressGesture.minimumPressDuration = 3;
// add the gesture to the imageView
[_imageView addGestureRecognizer:longPressGesture];
}
#pragma mark - UIGestureRecognizerDelegate
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
// show the image
_imageView.image = [UIImage imageNamed:#"cat.jpeg"];
}