Newbie question.
In IB I added UIImageView to scene and set few gestures for it in -viewDidLoad this way:
image.userInteractionEnabled = YES;
image.multipleTouchEnabled = YES;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(controlPan:)];
[image addGestureRecognizer:pan];
pan.delegate = self;
// and few others gestures same way
...
and gestures worked fine. After that in IB I added UIView to the same scene and implement gestures absolutely identically to previous code, again in -viewDidLoad. But after that gestures stop working for both UIView and UIImageView. Do I need to make .h and .m for this view and to implement gesture recognizers there, or reason can be in something else?
If you have same handler method you need to make distinction from which object gesture came, so you can properly handle action. If you set different handler methods for every object you dont have to care from which object gesture was invoked.
Related
I was looking for GestureRecognizer tutorials on the web and saw that most people used for example UIPanGestureRecognizer directly in the view. Is this common practice, if so, why not in the controller?
I generate my views in the controller and have methods that I would need to call, when the gesture is used. How should the delegation method look like, if my gesture recognizer is in the view class, and the method to be called in the controller class?
A simple UIGestureRecognizer implementation, and the most common one in my opinion is as follows:
//this is where you create the view that you want to add the gesture recognizer
UIImageView * imageView = [[UIImageView alloc] initWithFrame:frame];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] init];
tapRecognizer.numberOfTapsRequired = 1;
[tapRecognizer addTarget:self action:#selector(imageViewTapped:)];
imageView.userInteractionEnabled = YES;
[imageView addGestureRecognizer:tapRecognizer];
After creating the gesture recognizer and adding it to a UIView subclass you can get the taps with the function you pointed above:
- (void)imageViewTapped:(UIGestureRecognizer *)gestureRecognizer
{
if ( gestureRecognizer.state == UIGestureRecognizerStateRecognized )
{
NSLog(#"tap recognized!");
}
}
So basically, you dont need to worry about the controller/view dilemma.
You always add the gesturerecognizer to a view. and when adding the gesture recognizer you add a target function, which you need to implement in the controller class.
P.S This SO Question might clarify in detail of the dilemma you are facing
In one sense, yes, this violate the MVC pattern. As you say, the view shouldn't have anything to do with how to control it, it's a better habit to group such code in another part of the application.
You can drag an gesture into the storyboard(into the view controller),too.Then from the document outline you make an action(in the vc) for that gesture
I have a longpress gesture recognizer that I create in ViewDidLoad then attach to a button like this, the button is created in the storyboard and linked to my class.
UILongPressGestureRecognizer *hold = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(secretChange:)];
hold.minimumPressDuration = 5.0;
hold.delegate = self;
[_button addGestureRecognizer:hold];
The class conforms to the GestureRecognizer protocol and I have my selector here
- (void)secretChange:(UILongPressGestureRecognizer *)sender {
// Some stuff
NSLog(#"Secret");
}
The selector is not being called and I cannot figure out why, this seems to be the code everyone gives out on the internet, I have tried removing the minimum duration to make sure I didn't accidentally set it ridiculously long
UPDATE: I am actually adding this gesture recognizer to multiple buttons like this
[_button1 addGestureRecognizer:hold];
[_button2 addGestureRecognizer:hold];
[_button3 addGestureRecognizer:hold];
What is happening is the gesture recognizer is only being applied to the last button I add it to. How do I get the gesture recognizer added to ALL the buttons? Do I need to make a new one for every button?
You should have three instance of UILongPressGestureRecognizer.
Before add a gesture recognizer to a new view, the addGestureRecognizer method will remove the gesture recognizer from the view it has been attached to.
I'm somewhat new to iOS programming
I have some code (abridged) that looks like the following
UIView *someSubView = [[UIView alloc] initWithFrame:...];
[self addSubView:someSubView];
[someSubView addTarget:self action:#selector(_handleTapOnView:) forControlEvents:UIControlEventTouchUpInside];
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(_handleTap:)];
_tapGestureRecognizer.delegate = self;
[self.view addGestureRecognizer:_tapGestureRecognizer];
Unfortunately the gesture recognizer triggers and my views addTarget call does not. I've tried commenting out the gesture recognizer code and it works, so I know its not the call to addTarget on the subview.
I solved this initially by using the gestureRecognizer:shouldReceiveTouch: and doing a hit test for the sub view, but I feel like I'm missing some fundamental understanding here that wouldn't require me adding a manual hit test.
Its important to note that I don't want the code in the _handleTap in the _tapGestureRecognizer to execute when I have tapped on my subview.
Any guidance here? Thanks!
Try using:
_tapGestureRecognizer.cancelsTouchesInView = NO;
otherwise the gesture recogniser will intercept the touches and will not forward them further (in other words, the gesture recogniser gets the touch, handles it, and since it cancel it, no other object gets the touch). By not cancelling, the touch is forwarded for any other object (recognisers or views) to handle it.
I am refactoring a ViewController that I have adopted and is creating UIViews in the controller code. I'd like to move it to a custom UIView but would like to handle the Gesture Recognizer in the controller.
I have an ItemViewController which has a custom UIView. Within the ItemController, I'd like to do something like:
ItemView *itemView = [[ItemView alloc] initWithFrame:CGRectMake(30.0f,_runningYPosition,100.0f, 50.0f)];
UITapGestureRecognizer *tapRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRecognized:)];
itemView.miHeaderLabel.tag = [item itemID];
[itemView.miHeaderLabel addGestureRecognizer: tapRecognizer2];
that references
- (void)tapRecognized:(id)sender
{
...
}
in ItemViewController (NOT in the custom view).
But it doesn't seem to work. Is it possible to add a GestureRecognizer to a label that is a property of a subview in a ViewController? Anything obvious I'm doing wrong?
You need to set userInteractionEnabled to YES on the label for it to accept touches (and therefore for the gesture to receive touches).
I'm having a bit of a problem with a UITapGestureRecognizer. I create it this way:
self.userInteractionEnabled = YES;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapRecognizer.cancelsTouchesInView = NO;
tapRecognizer.delaysTouchesBegan = YES;
tapRecognizer.delegate = self;
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
[self addGestureRecognizer:tapRecognizer];
In the header file I also include and implement the shouldReceiveTouch: method like so:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
I've looked through countless threads of people with similar problems and can't seem to find a solution. I've heard that if the view you're adding the gesture recognizer has subviews with userInteractionEnabled set to YES, that could possibly interfere with the tap recognition, so I also include this:
for(UIView *subview in self.subviews) {
subview.userInteractionEnabled = NO;
}
Anyone know why the gesture recognizer doesn't work?
Edit:
Here are some details:
I'm adding the UITapGestureRecognizer to a UIView subclass.
I add the gesture recognizer in the subclass' initWithFrame: method.
I've verified that the gesture recognizer is being added by stepping through the portion of the code where it's actually added.
The view controller that contains this view does not have any gesture recognizers attached to it, but does implement touchesBegan, touchesMoved, and touchesEnded. However, according to this question, that the view controller implements those shouldn't affect the gesture recognition of the view.
Edit 2:
I've verified that there are no other views blocking the view with the gesture recognizer from receiving touches. I've also verified that the view is actually being added to the containing view controller's view. It seems like the problem's elsewhere.
Your set up should work, so I would guess that one of two things is happening. You may have forgotten to actually add this view to the ViewController's view, or there maybe another view placed on top of this view which is stealing the touches?
Another possibility causing the problem is that the view on which the UITapGestureRecognizer is added is not big enough, so when you tap on the screen, the touch point is not located in the view's bounds.
Also make sure the UIView is not transparent. Set the background color to e.g. black. This solved the problem for me.