I am using the following code to attach gesture recognizers to a custom view. But for some reason I am not able to get the gestures to work.
for(NSString *option in _options) {
UIView *blind = [self createBlind:option];
blind.userInteractionEnabled = YES;
blind.tag = index;
[self addSubview:blind];
[self addGravityBehaviorToBlind:blind];
[self addCollisionBehaviorToBlind:blind];
[self addElasticityBehaviorToBlind:blind];
[self registerGestureRecognizer:blind];
_spacingY += 44;
index++;
}
}
-(void) registerGestureRecognizer:(UIView *) blind {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(blindTapped:)];
[blind addGestureRecognizer:tapGestureRecognizer];
}
-(void) blindTapped:(UITapGestureRecognizer *) recognizer { <-- THIS NEVER GETS CALLED
NSLog(#"blindTapped");
}
For some reason the blindTapped method never gets called even if I tap on the blind view.
UPDATE:
Yes, I see the views on the screen. The views (blind) is a subview in another view. The parent view gets added to the controller.view. There is no place where I have disabled interaction.
Here is the createBlind method:
-(UIView *) createBlind:(NSString *) option {
UIView *blind = [[UIView alloc] initWithFrame:CGRectMake(0, _spacingY, 200, 44)];
blind.layer.borderWidth = 1.0f;
blind.layer.borderColor = [UIColor whiteColor].CGColor;
UILabel *optionTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, blind.bounds.size.width, blind.bounds.size.height)];
optionTitleLabel.text = option;
optionTitleLabel.font = [UIFont fontWithName:#"GillSans" size:17];
optionTitleLabel.textColor = [UIColor whiteColor];
optionTitleLabel.textAlignment = NSTextAlignmentCenter;
[blind addSubview:optionTitleLabel];
return blind;
}
I have removed all the gravity and UIKit methods but still I am not able to trigger the tap gesture.
SOLUTION:
When adding the custom view to my controller.view I was not setting the frame. Once the frame was setup everything worked.
Related
My scenario is that there will be many sub views in the main view and tapping on each sub view should generate different result.
My approach is for each sub view to implement its own tap recognizer, instead of for the main view to have one single tap recognizer and calculate the which sub view's area the user has tapped in. Is this a correct and viable approach?
I have tried this approach but it doesn't seem to work. The tap method never gets called. I read lots of articles on stackoverflow but they didn't seem to help.
For example, although the sub view is not an image view I still manually set its userInteractionEnabled property to YES, as some posts suggested. But that didn't help.
Below is the main code of the sub view:
- (void) handleOneTap:(UITapGestureRecognizer*)paramSender{
// *** Never gets called
NSUInteger touchCounter = 0; for (touchCounter = 0;
touchCounter < paramSender.numberOfTouchesRequired;
touchCounter++){
CGPoint touchPoint = [paramSender locationOfTouch:touchCounter
inView:paramSender.view];
NSLog(#"Touch #%lu: %#",
(unsigned long)touchCounter+1, NSStringFromCGPoint(touchPoint));
}
}
- (void)viewDidLoad {
[super viewDidLoad];
_container = [[UIView alloc] initWithFrame:CGRectMake(20, 50, 280, 300)];
[self.view addSubview:_container];
_container.backgroundColor = [UIColor redColor];
_container.opaque = YES;
// setup tap recognizer
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleOneTap:)];
self.tapGestureRecognizer.numberOfTouchesRequired = 1;
self.tapGestureRecognizer.numberOfTapsRequired = 1;
self.view.userInteractionEnabled = YES;
self.container.userInteractionEnabled = YES;
[self.view addGestureRecognizer:self.tapGestureRecognizer];
}
Any help will be appreciated.
If you want to add UIGestureRecognizer to your subview, you should do it like that:
_container = [[UIView alloc] initWithFrame:CGRectMake(20, 50, 280, 300)];
[self.view addSubview:_container];
_container.backgroundColor = [UIColor redColor];
_container.opaque = YES;
_container.userInteractionEnabled = YES;
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleOneTap:)];
self.tapGestureRecognizer.numberOfTouchesRequired = 1;
self.tapGestureRecognizer.numberOfTapsRequired = 1;
self.view.userInteractionEnabled = YES;
[_container addGestureRecognizer:self.tapGestureRecognizer];
It looks like that problem is not in UITapGestureRecognizer, but in case how you add Subview from ViewController.
Right way to add subviewcontoller is:
SSubViewController *pvc = [SSubViewController controllerWithSubViewID:0];
[self.view addSubview:pvc.view];
[self addChildViewController:pvc];
Do it in your ViewController and it should solve your problem
I have this piece of code, which is in init.
This should be some swipe cell, which is created by UIScrollView and there is some another view (my own action view) below cell.
My problem: if I want swipe my cell, UIScrollView doesnt call delegate methods :-/
Any suggestions? thx
self.cellState = kCellStateClosed;
self.cellScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kSWIPECELL_WIDTH, kSWIPECELL_HEIGHT)];
self.cellScrollView.delegate = self;
self.cellScrollView.showsHorizontalScrollIndicator = NO;
self.cellScrollView.scrollsToTop = NO;
self.cellScrollView.scrollEnabled = YES;
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(selectCell)];
self.longPressGestureRecognizer = [[PAPLongPressGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewPressed:)];
self.longPressGestureRecognizer.minimumPressDuration = 0.1;
[self.cellScrollView addGestureRecognizer:self.tapGestureRecognizer];
[self.cellScrollView addGestureRecognizer:self.longPressGestureRecognizer];
// Create the content view that will live in our scroll view
self.scrollViewContentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kSWIPECELL_WIDTH, kSWIPECELL_HEIGHT)];
self.scrollViewContentView.backgroundColor = [UIColor whiteColor];
[self.cellScrollView addSubview:self.scrollViewContentView];
// Add the cell scroll view to the cell
UIView *contentViewParent = [self.subviews firstObject];
NSArray *cellSubviews = [contentViewParent subviews];
[self insertSubview:self.cellScrollView atIndex:0];
for (UIView *subview in cellSubviews)
{
[self.scrollViewContentView addSubview:subview];
}
self.containingTableView.directionalLockEnabled = YES;
//alloc action view
self.actionView = [[PAPActionCellView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height) andPages:[self createActionPages] parentCell:self];
[self.cellScrollView insertSubview:self.actionView belowSubview:self.scrollViewContentView];
What you are missing is to set the contentSize of your UIScrollView, when you set it to some bigger value than its bounds, it will start to scroll, i.e:
self.cellScrollView.contentSize = CGSizeMake(2* kSWIPECELL_WIDTH, kSWIPECELL_HEIGHT);
This is what I'm trying to do:
I want to start recognizing a gesture in a subview and then, when the finger leaves the subview, let the superview take over and handle the gesture. Is that possible?
At the moment, the view in which the gesture started "wins". Is there any way to "hand over" a gesture from one view to another "on the fly", without lifting the finger off the screen?
This is the code for a simple demo:
#implementation SPWKViewController {
UIView *_innerView;
UIPanGestureRecognizer *_outerGestureRecognizer;
UIPanGestureRecognizer *_innerGestureRecognizer;
UILabel *_label;
}
- (void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor yellowColor];
_innerView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, self.view.frame.size.width - 100, 200)];
_innerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_innerView.backgroundColor = [UIColor greenColor];
_label = [[UILabel alloc] initWithFrame:CGRectMake(0, 300, self.view.frame.size.width, 40)];
_label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_label.textAlignment = NSTextAlignmentCenter;
_label.backgroundColor = [UIColor clearColor];
[self.view addSubview:_innerView];
[self.view addSubview:_label];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_outerGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.view addGestureRecognizer:_outerGestureRecognizer];
_innerGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[_innerView addGestureRecognizer:_innerGestureRecognizer];
}
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
if (gestureRecognizer == _outerGestureRecognizer) {
_label.text = #"outer";
} else {
_label.text = #"inner";
}
} else {
_label.text = #"";
}
}
#end
Some background information: The actual problem I'd like to solve is this: I'd like to put a UIWebView inside of a UIScrollView. When I scroll to the bottom of the UIWebView, I want the parent UIScrollView to "take over" and keep scrolling without having to lift the finger off the screen first. I figured that in order to achieve that, I'd need to "hand over" the gesture from the UIWebView to the parent UIScrollView.
Thank you!
I've used UITapGestureRecognizer tons of times, but in this case I'm getting EXC_BAD_ACCESS when a tap occurs. I think it has to do with the fact that I'm adding it to an Alert-type overlay view. And for some reason the overlay view isn't getting retained even though it's on-screen.
I'm creating the view like this:
HelperView *testHelper = [HelperView helperWithBodyText:#"test text"];
[testHelper presentAtPoint:screenCenter];
The convenience method in HelperView.m looks like this:
+ (id)helperWithBodyText:(NSString*)text
{
return [[self alloc] initWithFrame:CGRectZero bodyText:text];
}
And the rest of the code looks like this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.container = [[AGWindowView alloc] initAndAddToKeyWindow];
self.container.supportedInterfaceOrientations = UIInterfaceOrientationMaskLandscapeRight;
self.overlay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
self.overlay.backgroundColor = [UIColor redColor];
self.overlay.alpha = 0.6;
[self.container addSubviewAndFillBounds:self.overlay]; //this fills the screen with a transparent red color, for testing
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissMe:)];
[self.overlay addGestureRecognizer:tap]; //I want to dismiss the whole view if the overlay is tapped
self.content = [[UIView alloc] initWithFrame:CGRectZero];
}
return self;
}
- (id)initWithFrame:(CGRect)frame bodyText:(NSString*)bodyText
{
self = [self initWithFrame:frame];
if (self) {
//TEST frame
self.content.frame = CGRectMake(0, 0, 217, 134);
// Initialization code
UIImage *bgImage = [[UIImage imageNamed:#"helper-bg"]
resizableImageWithCapInsets:UIEdgeInsetsMake(30, 28, 20, 20)];
UIImageView *bgImgView = [[UIImageView alloc] initWithImage:bgImage];
bgImgView.bounds = self.content.frame;
[self.content addSubview:bgImgView];
}
return self;
}
- (void)presentAtPoint:(CGPoint)loc
{
CGPoint newPoint = [self.container convertPoint:loc toView:self.container];
self.content.center = newPoint;
[self.container addSubview:self.content];
}
- (void)dismissMe:(UITapGestureRecognizer*)recognizer
{
//this never happens - I get EXC_BAD_ACCESS when I tap the overlay
}
HelperView is not the view getting displayed and it's not getting retained, but you're using as the target for the gesture recognizer. The AGWindowView property "container" is getting displayed and retained by its superview. Your code needs refactoring since you have this view HelperView that doesn't ever display anything itself, but if you want it to work like this you need to retain HelperView so it doesn't automatically get released. You can do this by assigning it to a strong instance variable.
I have a UIScrollView and inside this I have UILabels. I need to detect touch events for the UILabels. At the moment, it is detecting the touch inside the second label only. It ignores the first.
I have the code -
Creating the UIScrollView
backGroundView = [[UIScrollView alloc] init];
backGroundView.frame= self.view.frame;
backGroundView.userInteractionEnabled = YES;
[backGroundView setScrollEnabled:YES];
backGroundView.showsVerticalScrollIndicator = YES;
backGroundView.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
backGroundView.delegate = self;
[self.view addSubview:backGroundView];
Creating the UILabel
UILabel *OneDay = [[UILabel alloc] initWithFrame:CGRectMake(15, stockChart.bounds.origin.y + stockChart.bounds.size.height + 35, 40, 30)];
OneDay.text = #"1d";
OneDay.tag = 1;
OneDay.userInteractionEnabled = YES;
OneDay.layer.borderColor = [UIColor grayColor].CGColor;
OneDay.layer.borderWidth = 1.0f;
OneDay.textAlignment = UITextAlignmentCenter;
[OneDay addGestureRecognizer:detectTimeFrameChange];
[backGroundView addSubview:OneDay];
UILabel *FiveDay = [[UILabel alloc] initWithFrame:CGRectMake(45, stockChart.bounds.origin.y + stockChart.bounds.size.height + 35, 40, 30)];
FiveDay.text = #"5d";
FiveDay.tag = 2;
FiveDay.userInteractionEnabled = YES;
FiveDay.layer.borderColor = [UIColor grayColor].CGColor;
FiveDay.layer.borderWidth = 1.0f;
FiveDay.textAlignment = UITextAlignmentCenter;
[FiveDay addGestureRecognizer:detectTimeFrameChange];
[backGroundView addSubview:FiveDay];
Creating the gesturerecognizer
UITapGestureRecognizer *detectTimeFrameChange = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(timeFrameLabelTapped:)];
detectTimeFrameChange.numberOfTapsRequired = 1;
[backGroundView addGestureRecognizer:detectTimeFrameChange];
Handling gesture
-(void)timeFrameLabelTapped:(UITapGestureRecognizer*)recognizer{
if (recognizer.view.tag == 1) {
NSLog(#"One pressed");
}
else if (recognizer.view.tag == 2){
NSLog(#"2 pressed");
}
}
You can use this :
UITapGestureRecognizer *labelTap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(labelTapped)];
labelTap.numberOfTapsRequired=1;
[yourLabel addGestureRecognizer:labelTap];
handle the touch tap event inside labelTapped method:
-(void)labelTapped
{
//your code to handle tap
}
Touch events are not detected on UIScrollview for getting your requirement add tap gestures to your label.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured)];
[OneDay addGestureRecognizer:singleTap];
-(void)singleTapGestureCaptured{
NSLog(#"touch detected");
}
You can find using tapgesturerecogniser like that...
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[self.scrollview addGestureRecognizer:singleFingerTap];
//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
if(recognizer.view.tag == 1){}
//Do stuff here...
}
Where have you written the touchesBegan?
If you want to detect the touches in the label you'll have to create a subclass of label and write your touchesBegan there to detect the touch events
The problem here is that you are trying to use the same gesture recognizer for multiple views. A gesture recognizer can only be attached to a single view at once. You are only receiving events from the last view, because that is the view the recognizer is currently attached to. To fix the issue, simply create a gesture recognizer for each view you want to detect touches in.