iOS: Tap Recognizer not consistent - ios

I have a scenario like shown below
Right now, I am showing only 1 view with label 8. But I am planning to add 3 more such views to the HolderView.
The SmallerView/s are created from other Nib files.
I did this code for adding Tap Recognizer for ViewController's view
UITapGestureRecognizer *tapRecognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapRecognized2:)];
[tapRecognizer setDelegate:self];
[self.view addGestureRecognizer:tapRecognizer];
Code for adding Tap Recognizer to the smaller views
I added smaller views to the HolderView. And assigned Tag IDs to them. After that,
for (SmallerView *view in HolderView.subviews) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRecognized:)];
[recognizer setDelegate:self];
NSLog(#"Added gesture to the view with tag: %ld",view.tag);
[view addGestureRecognizer:recognizer];
}
3.
- (void)tapRecognized:(UITapGestureRecognizer *)paramSender
{
NSLog(#"tapped on %ld", paramSender.view.tag);
}
- (void)tapRecognized2:(UITapGestureRecognizer *)paramSender
{
NSLog(#"VC view");
}
I have enabled UserInteraction (both in code and Inspector) for all the views and UILabels on smaller views too.
The problem now is...
The smaller view's Tap recognisers are not really working consistently. Sometimes they print the output. All at suddenly it prints the ViewController's recogniser's output.
Please help
UPDATE:
Below is my View diagram.
Green Border: (In UIView's initWithFrame)d
self.layer.borderColor = [UIColor greenColor].CGColor;
Red Border:
MyTile *tile = [[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] objectAtIndex:0];
self.myLabel.layer.borderColor=[UIColor redColor].CGColor;
Why that Green Border is coming only of that size? Shouldn't that be full square?
And, the gesture works only when I tap on the green area. Why?

It seems that you have a tap gesture recognizer on a view and also a tap gesture recognizer on its superview. The way gesture recognizers work is that, by default, both of them will be candidates to recognize the tap. If that is not what you want, it is up to you to prevent it.
You can:
set up a "you-go-first" relationship between the gesture recognizers, or
you can use their delegates to help decide between them, or
you can set up the subview so that it stops the superview's gesture recognizer from recognizing.
You have lots of options!
But if you do none of them, then both this view's gesture recognizer and its superview's gesture recognizer will be trying to recognize.

Perhaps a better solution would just to add one tapGestureRecognizer to the parent view.
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[tap addTarget:self action:#selector(handleTap:)];
[holderView addGestureRecognizer:tap];
Then add the target method
- (void) handleTap:(UITapGestureRecognizer *)tap {
UIView * holderView;
CGPoint tapPoint = [tap locationInView:holderView];
for (UIView * v in holderView.subviews) {
if (CGRectContainsPoint(v.frame, tapPoint)) {
// v is the subview that was pressed.
// add your code here.
break;
}
}
}

I have assigned a new frame to the frame which was having wrong borders. This helped in getting the gestures right.

Related

Adding different UITapGestureRecognizer to UIViews

I've made a custom UIView that appears in my app in two places. It first appears in a regular view controller along with a text view, and then a tap gesture on the UIView moves to another view controller where the custom UIView is shown enlarged. I want to have a different tap gesture for the enlarged UIView, but when I tried putting this into the storyboard, the first tap gesture stopped working (since the new tap gesture was only reachable via the first tap gesture's segue to the new view controller, I wasn't able to see if it worked or not.
Does adding the second UITapGestureRecognizer to the view cause the first to become invalid? I don't see any other reason this would've caused an issue. If so, do I need to create separate classes for the two different custom UIViews? I tried to avoid this since they're showing the same thing just in different sizes but it seems the different tap gestures may be causing an issue.
Adding different UITapGesture in different.
View1
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapBehind1:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO;
[View1 addGestureRecognizer:recognizer];
View2
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapBehind2:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO;
[View2 addGestureRecognizer:recognizer];
View1 handle Tap method
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
[View1 removeGestureRecognizer:sender];
}
}
View2 handle Tap method
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
[View2 removeGestureRecognizer:sender];
}
}

How to handle a tapping on non-cell area of UITableView

I have a UITableView with a couple of UITableViewCells in it. Because I only have a couple of cells, there is an area of the table view that's not covered by the cells. And I want to do something when the empty area is tapped.
I tried adding a UITapGestureRecognizer on the table view. It detects the tapping on the empty area, but then the cells fail to respond to tapping. I tried adding the tap gesture recognizer on the super view of the table view, but the result is the same.
There must be a way to do this, but I can't quite figure it out yet. Is there any way to achieve what I want to do?
Try this:
initialize and add the UITapGestureRecognizer to your tableView:
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
gr.delaysTouchesBegan = YES;
gr.delegate = self;
[_tableView addGestureRecognizer:gr];
implement the gesture recognizer delegate method:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
CGPoint tapPoint = [gestureRecognizer locationInView:_tableView];
UIView * clickedView = [_tableView hitTest:tapPoint withEvent:nil];
NSString *viewClassName = NSStringFromClass(clickedView.class);
return ![viewClassName hasPrefix:#"UITableViewCell"];
}
this way every tap you do outsude cells (but inside the tableview) will be recognized with your UITapGestureRecognizer
The last line: return ![viewClassName hasPrefix:#"UITableViewCell"];
should be changed to return [viewClassName hasPrefix:#"UITableView"]; since the cell you are clicking doesn't have to be a UITableViewCell, but a UIView or some other custom view

Strikethrough Text with Animation iOS

What is the best way to display a strikethrough animation? When a user swipes their finger across a UITableViewCell, I would like to animate a thin line across cell.textlabel.text
The two ways I've thought of so far would be using Animation or somehow displaying a custom image and revealing it slowly from left to right? Does anybody have any advice on this?
I already have the swipe gestures working, I now just need to know how to make the animation happen:
Add Gesture Recognizer:
//Add a left swipe gesture recognizer
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeLeft:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[self.tableView addGestureRecognizer:recognizer];
//Add a right swipe gesture recognizer
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeRight:)];
recognizer.delegate = self;
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[self.tableView addGestureRecognizer:recognizer];
Delegate Methods for Gestures:
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
NSLog(#"uncompleted");
}
// Cross Item off of the list
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
NSLog(#"completed");
}
Based on what you have so far the following should work:
when handleSwipeLeft (or right) fires, place a new UIView with a black background color over the textfield at the textfield's x point and around halfway to the y point with a width of 0 and height of 1
then, call [UIView animationWithDuration....] changing the UIView's width property to be roughly the width of the textfield.
This should be close to what you want with some tweaking. I don't think it will be possible to animate the strikethrough from using the properties of the font alone but this technique should simulate it just fine.
Good job on getting half way there.

how to place a subview on a cell so it does not block the tapgesture of its superview(cell)

I have the following code on a custom cell -
PSCollectionViewTapGestureRecognizer *gr = [[PSCollectionViewTapGestureRecognizer alloc] initWithTarget:self action:#selector(didSelectView:)];
gr.delegate = self;
[newView addGestureRecognizer:gr];
newView.userInteractionEnabled = YES;
how can i pass this gesture to its subviews? instead of adding a new gesture on subview.
currently the subview is blocking half of the view but tapping on it doesn't do anything.
needed to disable subviews interaction

How can I detect the touch event of an UIImageView?

I have placed an image (UIImageView) on the navigation bar. Now I want to detect the touch event and want to handle the event. How can I do that?
In practical terms, don't do that.
Instead add a button with Custom style (no button graphics unless you specify images) over the UIImageView. Then attach whatever methods you want called to that.
You can use that technique for many cases where you really want some area of the screen to act as a button instead of messing with the Touch stuff.
A UIImageView is derived from a UIView which is derived from UIResponder so it's ready to handle touch events. You'll want to provide the touchesBegan, touchesMoved, and touchesEnded methods and they'll get called if the user taps the image. If all you want is a tap event, it's easier to just use a custom button with the image set as the button image. But if you want finer-grain control over taps, moves, etc. this is the way to go.
You'll also want to look at a few more things:
Override canBecomeFirstResponder and return YES to indicate that the view can become the focus of touch events (the default is NO).
Set the userInteractionEnabled property to YES. The default for UIViews is YES, but for UIImageViews is NO so you have to explicitly turn it on.
If you want to respond to multi-touch events (i.e. pinch, zoom, etc) you'll want to set multipleTouchEnabled to YES.
To add a touch event to a UIImageView, use the following in your .m file:
UITapGestureRecognizer *newTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myTapMethod)];
[myImageView setUserInteractionEnabled:YES];
[myImageView addGestureRecognizer:newTap];
-(void)myTapMethod{
// Treat image tap
}
You can also add a UIGestureRecognizer. It does not require you to add an additional element in your view hierarchy, but still provides you will all the nicely written code for handling touch events with a fairly simple interface:
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[imgView_ addGestureRecognizer:swipeRight];
[swipeRight release];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[imgView_ addGestureRecognizer:swipeLeft];
[swipeLeft release];
I've been on different threads on the past few hours trying to find a solution for my problem, to no avail. I see that many developers share this problem, and I think people here know about this. I have multiple images inside a UIScrollView, trying to get tap events on them.
I am not getting any events from an UIImangeView, but I do get an event from a similar UILable with very similar parameters I am setting to it. Under iOS 5.1.
I have already done the following:
set setUserInteractionEnabled to YES for both `UIImageView and parent
view .
set setMultipleTouchEnabled to YES for UIImageView.
Tried subclassing UIImageView, didn't help any.
Attaching some code below, in this code I initialize both a UIImageView and UILabel, the label works fine in terms of firing events. I tried keeping out irrelevant code.
UIImageView *single_view = [[UIImageView alloc]initWithFrame:CGRectMake(200, 200, 100, 100)];
single_view.image = img;
single_view.layer.zPosition = 4;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[single_view addGestureRecognizer:singleTap];
[single_view setMultipleTouchEnabled:YES];
[single_view setUserInteractionEnabled:YES];
[self.myScrollView addSubview:single_view];
self.myScrollView.userInteractionEnabled = YES;
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
testLabel.backgroundColor = [UIColor redColor];
[self.myScrollView addSubview:testLabel];
[testLabel addGestureRecognizer:singleTap];
[testLabel setMultipleTouchEnabled:YES];
[testLabel setUserInteractionEnabled:YES];
testLabel.layer.zPosition = 4;
And the method which handles the event:
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
UIView *tappedView = [gesture.view hitTest:[gesture locationInView:gesture.view] withEvent:nil];
NSLog(#"Touch event on view: %#", [tappedView class]);
}
As said, the label tap is received.
Instead of making a touchable UIImageView then placing it on the navbar, you should just create a UIBarButtonItem, which you make out of a UIImageView.
First make the image view:
UIImageView *yourImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"nameOfYourImage.png"]];
Then make the barbutton item out of your image view:
UIBarButtonItem *yourBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:yourImageView];
Then add the bar button item to your navigation bar:
self.navigationItem.rightBarButtonItem = yourBarButtonItem;
Remember that this code goes into the view controller which is inside a navigation controller viewcontroller array. So basically, this "touchable image-looking bar button item" will only appear in the navigation bar when this view controller when it's being shown. When you push another view controller, this navigation bar button item will disappear.
You might want to override the touchesBegan:withEvent: method of the UIView (or subclass) that contains your UIImageView subview.
Within this method, test if any of the UITouch touches fall inside the bounds of the UIImageView instance (let's say it is called imageView).
That is, does the CGPoint element [touch locationInView] intersect with with the CGRect element [imageView bounds]? Look into the function CGRectContainsPoint to run this test.
First, you should place an UIButton and then either you can add a background image for this button, or you need to place an UIImageView over the button.
Or:
You can add the tap gesture to a UIImageView so that get the click action when tap on the UIImageView.
For those of you looking for a Swift 4 solution to this answer, you can use the following to detect a touch event on a UIImageView.
let gestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
imageView.addGestureRecognizer(gestureRecognizer)
imageView.isUserInteractionEnabled = true
You will then need to define your selector as follows:
#objc func imageViewTapped() {
// Image has been tapped
}
Add gesture on that view. Add an image into that view, and then it would be detecting a gesture on the image too. You could try with the delegate method of the touch event. Then in that case it also might be detecting.

Resources