Superview isn't getting touch events - ios

I have a simple iPad app with the following layers of views:
BaseView (covers the entire available area).
SquareView (covers 100x100px area).
The SquareView is added as subview to BaseView.
I also have a BaseViewController.m file. In this file I added the touchesBegan and touchesEnded functions to handle touch events. The problem is, when I click on the SquareView the touches functions get called, but when I click anywhere else they don't get called.
I have userInteractionEnabled = YES for the SquareView. I need this variable to be set in order to receive touches.
Clicking on the area outside the SquareView does not trigger the touches functions. I am puzzled as to why. Setting the userInteractionEnabled variable in the BaseView has no impact. What could be happening here? Is there some rule that I am missing? I am pretty sure I had it working at one point.
When the user clicks on the subview (SquareView) I need to launch a menu (let's say MenuView class). The idea is, when the user clicks on the area outside of the menu (or SquareView) I need to remove the pop-up MenuView by calling [menuView removeFromSuperview]. Is there a better way to handle this?

You could try to use this code in your controller. It should work for you.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
subview.backgroundColor = [UIColor lightGrayColor];
[subview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(subviewTouched)]];
[self.view addSubview:subview];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(viewTouched)]];
}
- (void)viewTouched
{
NSLog(#"viewTouched");
}
- (void)subviewTouched
{
NSLog(#"subviewTouched");
}

Related

When placing a subclassed PFLogInViewController object in a UIView, why does it appear outside the frame?

For a project I’m working on, I need to heavily customise the Parse.com login screen.
I realise that I need to subclass the PFLogInViewController, as well as do most of the modifications within the viewDidLayoutSubviews method. Also, I know I will eventually need to place elements of the PFLogInViewController into a UIView (which I’ll then place in a UITableViewCell), however I’m running into a strange problem.
When I try to add the self.loginview.dismissButton as a subview of a UIView I’ve called testView, the dismissButton does not appear within testView’s frame, it appears well outside of it (I set the x and y values of the dismissButton both to 0). However when I set testView.hidden to YES both objects are hidden.
Does anyone have any idea what I am doing wrong? Or perhaps any suggestion to work around this? I’ve listed the relevant code below
Thanks in advance.
- (void)viewDidLoad {
[super viewDidLoad];
[self.logInView.dismissButton removeFromSuperview];
UIView *testView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)];
[testView setBackgroundColor:[UIColor yellowColor]];
[testView addSubview:self.logInView.dismissButton];
[self.logInView addSubview:testView];
}
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.logInView.dismissButton setFrame:CGRectMake(0, 0, self.logInView.dismissButton.frame.size.width, self.logInView.frame.size.height)];
}

Removing UIImageView if the user taps on any point outside the UIImageView

My app shows an UIView which contains four UIButtons, one of them removes the UIView, but I also need is to remove the UIView, including all containing buttons when the user taps outside it.
This is how I remove the UIView using a button action:
-(IBAction)closeSideTasks: (id)sender
{
UIView * backgroundView = (UIView *)[self.view viewWithTag:7];
[backgroundView removeFromSuperview];
}
Any help or advice is welcome.
MORE INFORMATION:
This is the scenario: there is a table view. If the user swipes from left to right on a row, then the UIView appears on the right side of the view. The UIView *newView is removed after tapping on each of the buttons inside it and executing each button action. But may be the user does not tap on any of the buttons, and the UIView remains on the view. Then, what I want to achieve is a way that the UIView *newView gets removed if the user taps anywhere outside it, to prevent that it remains always on the view...
Check out UITapGestureRecognizer.
Example:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
tapGesture.delegate = self;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[tapGesture addTarget:self action:#selector(tapChangeImage)];
[self.profileImageView addGestureRecognizer:tapGesture];
Now add the action method:
#pragma tap gestures
-(void)tapChangeImage{
// Remove view from superview!
}
Put a large button, full screen, with no graphics, no text, and a clear background. Have its action also remove the background

Button click not responding in ScrollView when I create outlet of ScrollView in iOS

I have a Button1 outside ScrollView and another Button2 inside scrollview. I am using storyboard. I have used drag drop segue from both buttons to a different view. Button1 works fine, the problem is with Button2, it doesnt work no matter how many times I click, it only works when I click and drag (strange!!). When I troubleshooted I found that whenever I create an outlet of scrollview it behaves this way, if I remove the connection of scrollview to the outlet it works fine, but I need the outlet for the scrollview to work. Any solution on how I can get this done? This is a sample code of my view did load if it helps
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"bg.png"]];
self.scrollView.contentSize =CGSizeMake(320, 500);
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(resignKeyboard)];
[self.scrollView addGestureRecognizer:singleTap];
singleTap.numberOfTapsRequired = 1;
}
-(void) resignKeyboard
{
[self.view endEditing:YES];
}
This is how I defined the outlet
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
Note that Button2 Click does work but on click and drag/swipe not on single or multiple clicks.
EDIT:I checked and found that it works fine for simulator 6.1 but not for 5.0
Check out that your button 2 is inside the bounds of scrollview. If button's any part will lie outside the parent container's frame, it will not respond to any events. Test it by changing color of background of the scrollview. Also you can set button2.clipToBounds = YES. If button will be out of bounds, that part will be clipped.
I figured out the problem was because of adding tap gesture, because when your button is inside scrollview the first tap it works for scrollview and not button, so all I had to do is check if touch is for button or not. Here is the code that fixed this issue.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (self.scrollView.superview != nil) {
if ([touch.view isKindOfClass:[UIButton class]])
{
return NO; // ignore the touch
}
}
return YES; // handle the touch
}
Add delegate UIGestureRecognizerDelegate and in viewdidload add this line in the end
singleTap.delegate = self;
I had similar issue. What I did was remove entire hierarchy involving problematic view (in this case Button 2 and scroll view), then recreate them. Then recreate outlets and IBActions.
This works because storyboard is an xml document and sometimes xcode messes things up in writing it.

UITapGestureRecognizer Recognize taps on main view (not inputs or buttons)

I would like to get notified when a user clicks any part of the background of my UIView; basically if the user clicks outside of any control that is on my UIView (tables, textfields, buttons, etc).
How do I go about it? I tried doing this:
if ([touch.view isKindOf:[UIView class]]) {
do something....
}
But, obviously all controls extend UIView... so, I'm a bit stuck.
You should be able to do the following:
backgroundView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapHit:)] autorelease];
[backgroundView addGestureRecognizer:tap];
-(void) tapHit:(UITapGestureRecognizer *tap) {
NSLog( #"background tapped" );
}
One way you could do this is add a separate background view that is a child of your VC's main view. You can simply make it transparent so it doesn't interfere with your styling. Place is in the behind all the other children views. Then register your GR on that view.

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