I have a UIViewController and I am adding subviews to the main view. The main view has a UIGestureRecogniser and each subview also has a UIGestureRecognizer. In interface builder I have checked canceled in view in the attributes inspector so I would expect taps on the subviews to only fire my gestureRecognizerShouldBegin in the subview handler but they are also firing the handler attached to the view they are place on.
Does anyone have any idea why this might be the case?
I guess I could keep a handle to the particular instance of the GestureRecogniser attached to each subview and compare if the event being fired has come from the correct recogniser but this does not seem like the correct solution. Any help would be much appricated.
Edit: This second solution doesn't even work seems like every UIGestureRecogniser conforming to the current gesture is being fired!
Edit:
The best solution I have come up with is,
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if([view isEqual:[touch view]] && self.editing)
return YES;
return NO;
}
I would like the Gesture not to receive the touch at all though
Related
I added ICarousel to my IOS project and it worked fine. I could scroll pictures.
Then my view contain another data. So I needed to add a UISCrollView wich cover all my view. So, now, I have some elements (labels, textViews, and the UIVIew for ICarousel) in my ScrollView.
The ScrollView works fine. But now, the ICarousel doesn't switch pictures. Pictures are loaded (I see the first one and a part of the second one) but the carousel doesn't work anymore.
Did someone have the same problem? How to solve it?
Edit:
After #Wain advices, I tried this:
- (void)viewDidLoad
{
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:panRecognizer];
panRecognizer.delegate = self;
}
- (void)pan:(id)sender {
NSLog(#"Pan");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
It doesn't work, but I checked with a breakpoint, it ran shouldRecognizeSimultaneouslyWithGestureRecognizer method when I tried a horizontal scroll.
Where am I wrong?
Looks like a clash between gesture recognisers because, by default, only one can be active at any one time.
Not sure why your scroll view covers everything, but you should be able to make your controller the delegate of the gestures and implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: to allow them to recognise concurrently.
UIScrollView has a panGestureRecognizer property that you can access to set yourself as the delegate.
iCarousel is a bit different as it doesn't make the gesture available publicly so if setting the delegate on the scroll view doesn't work you can edit the carousel (which sets itself as the delegate) to implement the delegate method.
I'd like to use the touchesBegan method to know when the user is taping somewhen outside of a UITextField in my TableView.
I've got a UIView that contains a TableView, and I have the following method :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"See a tap gesture");
UITouch *touch = [touches anyObject];
if(touch.phase==UITouchPhaseBegan){
//find first response view
for (UIView *view in [self.view subviews]) {
if ([view isFirstResponder]) {
[view resignFirstResponder];
break;
}
}
}
[super touchesBegan:touches withEvent:event];
}
My self.view and my self.tableView have both the userInteractionEnabled set to YES, but for some reason, touchesBegan is never triggered. Any idea?
If your UIView contains your UITableView, then the table view is at the top of the responder chain and the touch events won't make it into your view. But there might be a better way to do this. What are you after?
UPDATE
Implement UITextFieldDeletate's textFieldShouldReturn: method to intercept a press of 'Return':
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
Also, a UITableViewDelegate is a UIScrollViewDelegate, so you can hook into those methods to determine when the user interacts with the table view.
Generally speaking I think you don't have to worry about dismissing the keyboard immediately when a user touches outside of it, especially if you have other text inputs on the same screen.
MOAR
Ok, fair enough, but things start to get complex when you intercept touch events via composite transparent views (these can get expensive as well), and so on. And you never know the repercussions that will arise down the road, not only for the user but for you the programmer when you want to upgrade the app in the future.
Why not keep it simple? How about just a 'Done' UIBarButtonItem, or a little translucent UIToolbar that slides up on top of the keyboard/picker ala Mobile Safari? These solutions are acceptable to the user, and can wind up making his life easier. They certainly make development easier by separating artifacts and functionality into modular units.
One final note
Using UITapGestureRecognizer I think will be difficult to get right in your situation. I'm worried that any tap recognizer you add to the table view will prevent things like row selection or moving control to another UI element (text field, switch, etc).
Try adding a tap gesture recognizer to your table view. That way you should be able to allow the normal behavior as well as capturing the tap event to do your extra stuff.
I've implemented similar functionality in one of my applications, I did use textFields rather than textViews, but it should still work. I used
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[textField reresignFirstResponder];
}
Without doing any checks, if the user taps the resignFirstResponder method gets called, but if the user taps on the textView, then that triggers it to become the first responder again, resulting in no visible change. I've implemented this concept on an app with 9 textFields on a single view, and I have yet to experience problems.
Make sure you have set your object to process those touch events:
[myButton addTarget:self action:#selector(buttonTouch:withEvent:) forControlEvents:UIControlEventTouchUpInside];
And then process the touch event in the method you declared:
- (IBAction) buttonTouch:(id) sender withEvent:(UIEvent *) event
Updated for UIView (Assuming you already have an outlet created and wired up):
[myView addTarget:self action:#selector(myTouchEvent:withEvent:) forControlEvents:UIControlEventTouchUpInside];
And then:
- (IBAction) myTouchEvent:(id) sender withEvent:(UIEvent *) event
Now that I understand what you are trying to do (dismiss the keyboard when touching outside the textbox), there is a simple solution that works well with almost every scenario).
Step 1 - Create a button that is sized the same as the View that the TableViewController is placed in. (large button).
Step 2 - Send that button to the back (Editor->Arrange->Send to Back)
Step 3 - Wire up an IBAction to that button and call it dismissKeyboardButtonPressed (hint if you are not already using the assistant editor, you should be)
Step 4 - Inside the IBAction method put (assuming your TextField is called myTextField):
[myTextField resignFirstResponder];
Step 5 - Run it. Whenever you touch anywhere in the view outside of the textbox, it will dismiss the keyboard.
This is the method I use almost anytime I put a TextField on a view, and need to dismiss the KB when touching outside the TextField.
I try to implement touchBegan and SwipeGestureRecognizer in the same view but it did not
Detect the swipeGestureRecognizer every touch is call touchBegan so, can it works together?
I am not positive about the SwipeGestureRecognizer but I was able to implement the TapGestureRecognizer along with touchesBegan. I would suggest first commenting out the touchesBegan and seeing if the Swipe is recognized. This would be your starting point. It could be that the swipeGestureRecognizer is not connected to the view or that you did not set setUserInteraction: to YES or TRUE. But once you get the swipe to register. Then uncomment your touchesBegan, and test again. There is also this delegate method that may give you more information:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
Also you can check out: UIGestureRecognizerDelegate_Protocol
and UISwipeGestureRecognizer Class Reference
I have read some other articles like here and here but unfortunately there are some differences in my code that won't make these work (so please don't suggest these answers).
I would like to dismiss the keyboard when the user taps the background. Normally this would be easy, except that my UITextField objects are inside a UIScrollView which makes it so I can't catch the touch events (the UIScrollView swallows them so they don't reach the base view). One way to get around this is to register for a generic gesture (a tap), but this catches all taps, including the ones intended for the submit button.
So basically, 'touchesBegan:withEvent:' wont work because it never gets called, and gestures wont work because they dont account for button presses.
Here's the question: is there some way to detect a simple tap on a UIScrollView? Once I detect the tap I know how to do the rest. Thanks!
You can't use touchesBegan:withEvent on the superview of the scrollview, but what about subclassing UIScrollView and handling the touch there? You can then proceed normally with a call to super's implementation to keep from stepping on the UIScrollView's toes:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Insert code to dismiss keyboard if needed */
// This makes sure scrolling proceeds normally.
[super touchesBegain:touches withEvent:event];
}
I also ran into same problem. While in IOS 6.0, it was working fine, but as soon i switched to IOS 5.0, it started to show the same behaviour you have mentioned.
Workout for IOS 5.0 - You should use the UITapGestureRecognizer. Then set its delegate to self and implement the following delegate method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
as
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ! ([touch.view isKindOfClass:[UIControl class]]);
}
#end
above code will verify that the object under touch is not a UIButton or any control element, then only handle the touch
I hope it would solve your problem.
i am trying to get a subview to become firstResponder. my understanding is that this is done in its viewDidAppear method, like so:
- (void)viewDidAppear {
[self becomeFirstResponder];
}
while overriding canBecomeFirstResponder to return YES:
- (BOOL)canBecomeFirstResponder {
return YES;
}
however, when i insert the subview in its parent view's viewDidLoad method:
- (void)viewDidLoad {
subViewController = [[SubViewController alloc] init];
[self.view insertSubview: subViewController.view atIndex: 0];
[subViewController viewDidAppear: NO];
[super viewDidLoad];
}
(i call viewDidAppear manually, because it does not get triggered automatically), the subview does not become firstResponder.
why does the subview not become firstResponder? and how can i make it firstResponder?
thanks,
mbotta
btw, this is a rewrite of my original question:
i am trying to build an iphone app where a rootviewcontroller object manages two subviews, one of which should react to a user shaking his iphone.
after some digging, i concluded the subview must be made firstResponder in its view controller's viewDidAppear method. moreover, the canBecomeFirstResponder method should be modified to return YES.
so here's what happens: i instantiate the rootviewcontroller in the app delegate. in its viewDidLoad method, i tell it to addSubView firstViewController. all of this works just beautifully.
however, firstViewController does not react to any shaking. i sprinkled some NSLogs around and found that while we DO tell firstViewController in canBecomeFirstResponder to return YES, and while we DO tell it to [self becomeFirstResponder] in viewDidAppear, in actual fact, it is not the firstResponder.
so, my questions are:
1) does a subview actually need to be firstResponder in order to react to shaking?
a) if not, how does one make a subview react without being firstResponder?
2) how does one make a subview firstResponder?
what makes this interesting is that if i perform the same sequence (canBecomeFirstResponder, [self firstResponder], motionBegan:) in a different project with only one view controller, it all works flawlessly. clearly, i must be hitting a design flaw of my own making.
thanks,
mbotta
Not 100% sure, but this could be your problem. If we could see the offending methods it might be easier.
From the Event Handling Best Practices (emphasis added by me):
If you handle events in a subclass of
UIView, UIViewController, or (in rare
cases) UIResponder,
You should implement all of the event-handling methods (even if it is
a null implementation).
Do not call the superclass implementation of the methods.
If you call the superclass methods the events are probably getting passed along to the nextResponder.
EDIT
The Event Handling Best Practices link above is dead. I couldn't find that pull quote anywhere, but Event Handling Guide for UIKit Apps seems to be the most relevant.