BAD_ACCESS_EXEC when adding gestureRecognizer in different class - ios

I'm trying to build a generic class with a couple of Gesture Recognizers, so that I can call one method and have both Recognizers added to the view
Here is my class implementation code
#import "saUIHelper.h"
#implementation saUIHelper
-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
NSLog(#"pan");
}
-(void)handleLP:(UILongPressGestureRecognizer *)recognizer
{
NSLog(#"Test");
}
-(saUIHelper*)initWithView:(UIView*)view
{
self = [super init];
if(self){
self.myView = view;
self.myView.userInteractionEnabled = YES;
}
return self;
}
-(void)start
{
UIPanGestureRecognizer* panRecognizer =
[[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(handlePan:)];
[self.myView addGestureRecognizer:panRecognizer];
UILongPressGestureRecognizer* lpRecognizer =
[[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleLP:)];
[self.myView addGestureRecognizer:lpRecognizer];
}
#end
And I try to use it like this
UIImage *playImg = [UIImage imageNamed:#"playBtn"];
self.playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.playBtn.frame = CGRectMake(0.0f,0.0f, 60.0f, 60.0f);
self.playBtn.center = CGPointMake(viewWidth/2,(viewHeight/2)-35);
[self.playBtn setBackgroundImage:playImg forState:UIControlStateNormal];
[self.playBtn addTarget:self action:#selector(play:) forControlEvents:UIControlEventTouchUpInside];
[self.menu addSubview:self.playBtn];
saUIHelper* helper = [[saUIHelper alloc] initWithView:self.playBtn];
[helper start];
When I run this code and do a Pan or LongPressure gesture on the button I added, I get a crash with BAD_ACCESS_EXEC code
If however instead of calling [helper start]; I type in the same code as implemented in the start method, and add the handlePan: and handleLP: callbacks to the main class, I get no issues and everything runs fine
It's obvious I'm doing something very wrong here, not sure if it's threading issues or something else, can anyone give me some pointers?

Store a strong reference to saUIHelper* helper in your view/viewcontroller so that it doesn't get dealloced. You're probably getting a crash because the target is the helper, which might have been dealloced.

Related

UIview custom class including a tapgesture is it possible?

I tried to use initWithFrame but I am not able to display the UIView, This kind of code solved many questions as I saw here but not in my case:
- (id)initWithFrame:(CGRect)frame myParams:(NSArray*)params;
.m file
- (id)initWithFrame:(CGRect)frame myParams:(NSArray*)params{
self = [super initWithFrame:frame];
if(self){
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
[self addSubview:view];
}
return self;
}
Here is how I am trying to add the view in UIViewController:
Maybe here I am doing it wrong, right?
Custom *view = [Custom alloc] initWithFrame:self.view.bounds myParams:array];
[self.view addSubview:view];
So, my question is it possible to add UITapGesture in the UIView generated in this class, cause it is not firing:
.h file
+ (UIView *)viewParams:(NSArray*)params;
.m file
+ (UIView *)viewWithParams:(NSArray*)params{
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
NSLog(#"read params %#", params);
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
return view;
}
- (void)test{
NSLog('it works...');
}
UPDATE : As instance method is working. But as class method I can not make it work. somebody know if possible?
1st Part
If you are using XIB for your customView then you need to use awakeFromNib method. But if you are trying to create through code only then just use initWithFrame: method and add your gesture recognizer in the initializer.
2nd Part
For firing tap gesture recognizer you are adding recognizer target self i.e your customView. So event will fire in your customViewClass class only then to get these events in your controller you need to use protocol and delegate. And set the delegate of customView to whatever controller you want to use in the controller.
View will add after firing the initwithcoder just use this code in init with coder ...
dispatch_after(0.1, dispatch_get_main_queue(), ^(void)
{
UIView *view = [UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
//Trying to send an uiview with gesture include
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(test)];
[view addGestureRecognizer:tap];
[self addSubview:view];
});
}
return self;
}
Minimum Time interval 0.1 max time interaval set as per your data load requirement

How to get tag from UIImageViiew in ios?

I have added tap gesture on UIImageView. I want to get the tag from it when I tap on the image. Please tell me how can I do it?
UITapGestureRecognizer *tapGesture1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap_post_image:)];
tapGesture1.numberOfTapsRequired = 1
[tapGesture1 setDelegate:self];
[cell.beizer_image setUserInteractionEnabled:true];
[cell.beizer_image addGestureRecognizer:tapGesture1];
[cell.beizer_image setTag:indexPath.row];
- (void) tap_post_image: (id)sender
{
NSInteger the_tag = ((UIView*)sender).tag;
NSLog(#"tap post image is called");
NSLog(#"TAG is %ld",(long)the_tag);
}
Above code crashes the app.
While you really need to provide details of the crash on your question, one obvious issue is that you assume the sender parameter of your top_post_image: method is a UIView.
This is incorrect. The parameter will be the gesture recognizer, not a view. But you can get the view from the gesture.
The code should be:
- (void)tap_post_image:(UITapGestureRecognizer *)gesture {
NSInteger the_tag = gesture.view.tag;
}
On an unrelated note you need to work on your naming conventions. In Objective-C (as well as many other languages), it is common practice to use what is called "camel case'. Your method should be named tapPostImage:, not tap_post_image.
You will crash because sender in your code is not uiview, uiimageview.
sender is UITapGestureRecognizer class.
You should do
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapImageView:)];
[tapGesture setNumberOfTapsRequired:1];
[self.sampleImage setUserInteractionEnabled:YES];
[self.sampleImage addGestureRecognizer:tapGesture];
[self.sampleImage setTag:99];
- (void)tapImageView:(id)sender {
NSLog(#"Sender is a %# class",NSStringFromClass([sender class]));
UITapGestureRecognizer *tapGesture = ((UITapGestureRecognizer *)sender);
UIImageView *imageView = (UIImageView *)tapGesture.view;
NSLog(#"imageView.tag = %d",imageView.tag);
}
I hope it helps!

use class methods for encapsulation?

i have made some #properties of uiimage view type in a class and then initialized that class in my rootviewcontroller.
questions.h:
#property(nonatomic,strong)UIImageView *img1;
#property(nonatomic,strong)UIImageView *img2;
#property(nonatomic,strong)UIImageView *img3;
#property(nonatomic,strong)UIImageView *img4;
'viewcontroller.m'
-(void)viewdidload{
questions *q1=[[questions alloc]init];
q1.img1 =[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"red.png"]];
[self.view addSubview:q1.img1];
UITapGestureRecognizer *Tap1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(iTap:)];
[q1.img1 addGestureRecognizer:Tap1];
}
-(void)iTap:(UIGestureRecognizer *)gestureRecognizer {
// do something..
}
i know its not a good idea to populate viewdidload with initialization code..
is there any better way to acive this using class methods ?
like :
questions.m:
+(questions * )mymethod{
questions *q1=[[questions alloc]init];
q1.img1 =[[uiimageview alloc]initwithimage [uimage imagenamed#"red.png"]];
[self.view addSubview:q1.img1];
UITapGestureRecognizer *Tap1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(iTap:)];
[q1.img1 addGestureRecognizer:Tap1];
return q1;
}
and then calling that method in viewdidload
viewcontroller.m:
-(void)viewdidload{
[questions mymethod];
}
There's nothing really wrong with creating adding views in viewDidLoad. You can however also use - [UIViewController loadView].

How do I get UIGestureRecognizers to work?

I'm having trouble just turning on gesture recognizers in my code. I added a handleTap callback to to the tap gesture recognizer but the print statement never happens. I'm just not having any luck. Does anyone see what I'm doing wrong here?
In my ViewController.h
#interface ViewController : UIViewController<UITextFieldDelegate, UIGestureRecognizerDelegate> {
}
#end
This is what I have in my ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
View *myview = [[View alloc] initWithFrame:applicationFrame];
myview.userInteractionEnabled = YES;
myview.multipleTouchEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap)];
tap.numberOfTapsRequired = 1;
tap.delegate = self;
[myview addGestureRecognizer:tap];
self.view = myview;
}
-(void)handleTap:(UITapGestureRecognizer*)recognizer {
NSLog(#"dismiss keyboard");
//[usernameTextField resignFirstResponder];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
Edit: I added a text field and when I click on the text field I see the tap selector print statement. But if I tap off the text field I don't get the selector print statement.
I think the problem is with your selector. Try changing
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap)];
And replace it with
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap:)];
To make the selector match the signature for handleTap.
You are passing the wrong selector when initialising.
The line should read like this:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap:)];
Notice the colon after handleTap to match the method you want to use.
Apparently subclassing the UIView and overriding drawrect with an empty function caused the selector to be called.

UIGestureRecognizer in UIView Object

I have created a UIViewController, which contains a UIView object.
I want the UIView object response to a single tap by 1 finger, and also pinch by 2 fingers.
I have clicked the "User Interaction Enabled" and the "Multiple touch" options in the xib.
I create both UITapGestureRecognizer, and UIPinchGestureRecognizer inside the UIView function initWithFrame:, as it is the only entry point for the UIView object. But the object doesn't response to the UIGestureRecognizer objects. Why? Or, where is the best place to put the UIGestureRecognizer objects?
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Your problem is that when you instantiate your custom view subclass in IB, initWithCoder: is called. If you had done it programmatically using initWithFrame: it would have worked properly.
Two ways you can fix this,
Move the gestures setup to a different method and call them in both initWithFrame: and initWithCoder:. I would recommend doing this if you intend to reuse this component and expose some kind of callbacks on gestures.
If you want to implement this once and have a lot of interacting with the controller element, add them in viewDidLoad.
I tried your code and it works for me.
Where are you setting the view? Maybe it has any other view in front, or its superview has userInteractionEnabled disabled.
I created a UIView subclass and added this code:
-(void) handleSingleTap:(UITapGestureRecognizer *)gr {
NSLog(#"handleSingleTap");
}
-(void) handleDoubleMove:(UIPinchGestureRecognizer *)gr {
NSLog(#"handleDoubleMove");
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Then, in my controller:
-(void) viewDidLoad {
[super viewDidLoad];
MyView * myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor greenColor];
[self.view addSubview:myView];
[myView release];
}
And it works.
i think if you are working with .xib, initialization can also be done in
-(void)awakeFormNib;
Adding the GestureRecognizers is typically done in the viewDidLoad method of the UIViewController.

Resources