UIGestureRecognizer Crash - ios

I am add to put a swipe up gesture to an image, but when swiped, app craches with BAD_EXEC error.
This is what I have:
.h file:
#interface MyViewController : UIViewController <UIGestureRecognizerDelegate>
{
UISwipeGestureRecognizer* swipeUpGesture;
IBOutlet UIImageView* myImage; //Connected from Interface Builder
IBOutlet UIScrollView* myScrollView;
}
#property (retain, nonatomic) UISwipeGestureRecognizer* swipeUpGesture;
#property (retain, nonatomic) IBOutlet UIImageView* myImage;
#property (retain, nonatomic) IBOutlet UIScrollView* myScrollView;
.m file:
- (void)viewDidLoad
{
//myImage is inside of myScrollView
swipeUpGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swiped)];
[swipeUpGesture setDirection:UISwipeGestureRecognizerDirectionUp];
[swipeUpGesture setDelegate:self];
[myImage addGestureRecognizer: swipeUpGesture];
}
- (void)swiped:(UISwipeGestureRecognizer*)sentGesture
{
NSLog (#"swiped");
}
So basically, within myView, I have myScrollView. Inside of myScrollView, I have myImage.
When I run above code, app runs until I swipe up, then it actually recognizes the swipe, but does not get to NSLog, crashes and I get BAD_EXEC.
Thanks in advance.

If you use addSubview, do the following:
[self addChildViewController:myViewController];
After that:
[self.view addSubView: myViewController.view];
Then use a UISwipeGestureRecognizer in the view controller.

You have a signature mismatch.
swipeUpGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swiped)];
Your selector is "swiped", without a colon, which means objective c runtime will try to find a method that takes "zero" parameter.
And since your "swiped" takes in a parameter, the runtime will fail to find a match when it tries to invoke the method and so crashes.
--
Change your #selector(swiped) to #selector(swiped:) and it should work.

You forgot a colon:
swipeUpGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swiped)];
should be
swipeUpGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swiped:)];
Without a colon, the Obj-C runtime will try to find a method without any arguments.

(As #Undo said, you forgot a colon.)
However, you can still get a EXC_BAD_ACCESS error if your ViewController was released before the touch event occurred.
This can happen when adding the view of ViewController as a subview to another view controller. e.g.
[mainViewController.view addSubview:self.view]
Where self is your MyViewController. You can check for this by adding a breakpoint into the
-(void)dealloc
method of your MyViewController. And checking if MyViewController is dealloced before your touch event.
You can fix this by adding a strong reference to MyViewController (ARC) wherever it is that you instantiate it.

Related

Basic example using labels and gesture recognizer not working

I'm trying to create a basic project that uses labels and gesture recognition but am having a little difficulty. I created an interface of 4 UILabels positioned vertically along the center of the app screen. Looks something like:
Question
Next Question
Answer
Show Answer
...just 4 labels, positioned one after the next. I make connections from Interface Builder to my ViewController.h file to create IBOutlet properties for each of the labels. Simple enough. I then go ahead and write all the implementation code in the .m file (seen below). The only issue is when the app runs, all that is displayed is four positioned labels with the default "Label" text from Interface Builder. The gesture recognizers I added to two of the labels do not seem to be connected/triggering either.
Hoping someone could shed a little light on the issue for me. What started as a simple exercise is driving me a little mad.
Here's my Viewcontroller.h file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, strong) IBOutlet UILabel *labelQuestion;
#property (nonatomic, strong) IBOutlet UILabel *labelNextQuestion;
#property (nonatomic, strong) IBOutlet UILabel *labelAnswer;
#property (nonatomic, strong) IBOutlet UILabel *labelShowAnser;
#end
And here's my Viewcontroller.m file:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, copy) NSArray *arrayQuestions;
#property (nonatomic, copy) NSArray *arrayAnswers;
#property (assign) int incrementer;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// First create the datasource arrays with five elements each
self.arrayQuestions = [[NSArray alloc] init];
self.arrayQuestions = [NSArray arrayWithObjects:#"Who am I?", #"Who are you?", #"Where are we?", #"What's going on?", #"Why is this happening?", nil];
self.arrayAnswers = [[NSArray alloc] init];
self.arrayAnswers = [NSArray arrayWithObjects:#"Damian", #"Dmitri", #"I don't know.", #"You tell me.", #"I have no clue", nil];
// Reset the incrementer's value
self.incrementer = 0;
// Next configure the labels
self.labelQuestion = [[UILabel alloc] init];
self.labelQuestion.text = [self.arrayQuestions objectAtIndex:self.incrementer];
self.labelNextQuestion = [[UILabel alloc] init];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(nextQuestionLabelPressed)];
[tap setNumberOfTapsRequired:1];
[self.labelNextQuestion addGestureRecognizer:tap];
self.labelNextQuestion.text = #"Show next question";
self.labelAnswer = [[UILabel alloc] init];
self.labelAnswer.text = #"?????";
self.labelShowAnser = [[UILabel alloc] init];
self.labelShowAnser.userInteractionEnabled = YES;
UITapGestureRecognizer *tapp = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showAnswerLabelPressed)];
[tapp setNumberOfTapsRequired:1];
[self.labelShowAnser addGestureRecognizer:tapp];
self.labelShowAnser.text = #"Show answer";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)nextQuestionLabelPressed {
self.incrementer++;
self.labelQuestion.text = [self.arrayQuestions objectAtIndex:self.incrementer];
self.labelAnswer.text = #"?????";
}
- (void)showAnswerLabelPressed {
self.labelAnswer.text = [self.arrayAnswers objectAtIndex:self.incrementer];
}
#end
Let's say you've connected a label from the storyboard to self.labelQuestion. So far so good. When you launch, self.labelQuestion points to the label from the storyboard, which is the one that you see in the visible interface of the running app.
But then you say:
self.labelQuestion = [[UILabel alloc] init];
That disconnects the outlet and replaces the value of that variable (self.labelQuestion) with a new blank label! So anything you now do to labelQuestion cannot possibly affect the one from the storyboard, as you have broken the connection.
You do that for all four labels, so you have smashed all four outlets into dust. Thus, your other code has no effect on the visible labels, which come from the storyboard.
Just delete all four [[UILabel alloc] init] lines, and all will be well.

initWithTarget:self doesn't get my class to call selector

I create a class for dealing with dismissing keyboard
custom class:
tapReconizer.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface tapReconizer : NSObject {
UIView* myView;
}
-(void) tapreg:(UIView*)view;
#end
tapReconizer.m:
#import "tapReconizer.h"
#implementation tapReconizer
-(void) tapreg:(UIView*)view {
myView = view;
UITapGestureRecognizer *tapGestureRecognizer;
tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[myView addGestureRecognizer:tapGestureRecognizer];
}
- (void)singleTap:(UIGestureRecognizer *)recognizer {
[myView endEditing:true];
}
#end
and in my viewcontorller:
tapReconizer* reg = [[tapReconizer alloc]init];
[reg tapreg:self.view];
but it's seems singleTap: doesn't been called.
Can I do this all just in this custom class?
Thanks in advance.
Your tapReconizer instance (reg) gets deallocated when it goes out of scope at the end of whatever block it is created in.
You need to keep a strong reference to it. The easiest way is probably to make reg an instance variable.
FYI - you probably meant to name your class TapRecognizer. You misspelled recognizer and it is standard practice to name classes to start with uppercase letters. Method and variable names start with lowercase.
In your View controller
tapReconizer* reg = [[tapReconizer alloc]init];
[reg tapreg:self.view];
Here reg is getting deallocated, so make it is global for strong.
Declare it in your viewcontroller.h
#property (strong, nonatomic) TapReconizer* reg;
or in your .h or .m of view controller
after
#interface ViewController : UIViewController
{
TapReconizer* reg;
}
Hope it will help you.

UIButton enable doesn't work

I have a UIViewController with a single button and an activity indicator.
In the class for this VC MainViewController.m I do the following in viewDidAppear:
- (void)viewDidLoad
{
[super viewDidLoad];
_actLoadLoc.color = [UIColor blueColor];
_startButton.enabled = NO;
[_startButton setTitle:#"Fetching Location" forState:UIControlStateDisabled];
}
Another method in my MainViewController.m is called readyToGo and is implemented as follows:
-(void) readyToGo
{
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"Done sleeping");
_startButton.enabled = YES;
[_startButton setTitle:#"Start" forState:UIControlStateNormal];
_actLoadLoc.stopAnimating;
}
I have properties for both UIButton, UIActivityIndicatorView and a declaration of the readyToGo method in my MainViewController.h as follows:
#property (weak, nonatomic) IBOutlet UIButton *startButton;
#property (weak, nonatomic) IBOutlet UIActivityIndicatorView *actLoadLoc;
-(void) readyToGo;
The readyToGo method is called from another class abc.[h/m] which imports MainViewController.h. The call happens after one of the functions in abc.m completes filling an array with calculated data.
The call works since Done Sleeping shows in the output, however the startButton is not enabled, its test does not change and the actLoadLoc does not stop animating... Any idea what's wrong with my code/method?
Thanks in Advance!
You are calling the readyToGo on the wrong instance of the view controller. You have an instance which is displaying content on the screen and you are, in some way, creating a new one to call the method on. You need to get the existing one instead.
It's not ideal, but you should be able to get the controller with:
UINavigationController *n = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController;
SDPPMainViewController *mvc = (SDPPMainViewController *)[n viewControllers][0];
(Will need to add some casts, and should probably break out to multiple lines)

app crashing when responding to tapgesture

Basically, I have a UIViewController with a xib built for holding content in a UIScrollView. this gets populated from an RSS feed. this works as expected. I want a tap on the UIView that the view controller owns to respond to a tap and continue on its way. however, a tap crashes the app with the "unrecognized selector sent to instance" error. I really have no clue what's going on.
The .h file of the xib's view controller looks like this:
#property (nonatomic,retain) IBOutlet UIImageView *article_image;
#property (nonatomic,retain) IBOutlet UILabel *article_title;
#property (nonatomic,retain) IBOutlet UILabel *article_date;
#property (nonatomic,retain) IBOutlet UIActivityIndicatorView *spinner;
#property (nonatomic,retain) NSString *preview_image;
- (void) loadPreviewImage;
Then the .m file looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGesture)];
singleTap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:singleTap];
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
self.view.layer.shadowOpacity = 0.6;
self.view.layer.shadowRadius = 1.5;
self.view.layer.shadowOffset = CGSizeMake(0, 0);
}
- (void) singleTapGesture:(UITapGestureRecognizer *)gesture {
NSLog(#"TAPPED!");
}
Even if I put just a simple roundrectbutton in the xib and attach it to an IBAction - it crashes, so I'm figuring there's something wrong with how my xib is set up, but I have no clue as to what. those properties in the .h get populated just fine from the VC that holds the scrollview, so the xib obviously knows the file it's associated with, so I just can't figure out why it's crashing and throwing that error on a tap.
The fix is this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGesture:)];
notice the : at the end of the selector.
Indeed, your action method signature is:
- (void) singleTapGesture:(UITapGestureRecognizer *)gesture;
while
#selector(singleTapGesture)
would rather identify:
- (void) singleTapGesture;
Hope this clarifies the issue.
well, i never really figured out exactly what was causing the crash - so i wound adding the gesture recognizer to the objects into the loop that builds them and all works great. not how i'd like to have done it (was trying to keep it in the object's class definition) but whatever. perhaps this will help someone else.

How to make my custom gesture recognizer class instances work simultaneously?

You can download code example from this libk:
https://github.com/mriddi/ExempleCustomGestureRecognizer.git
I create my cusom gesture recognizer class like this:
#protocol MyGestureRecognizerDelegate <UIGestureRecognizerDelegate>
#optional
-(void) willDo;
#end
#interface MyGestureRecognizer: UIGestureRecognizer
{
…
}
#property (nonatomic, assign) id <MyGestureRecognizerDelegate> delegate;
…
#end
#implementation MyGestureRecognizer
#synthesize delegate;
…
-(void) call{
[delegate willDo];
}
…
#end
My ViewController adopt MyGestureRecognizerDelegate protocol.
In ViewController I create instances of my class:
MyGestureRecognizer * grFerst;
MyGestureRecognizer * grSecond;
grFerst.delegate=self;
grLeft.delegate=self;
[self.view addGestureRecognizer: grFerst];
[self.view addGestureRecognizer: grSecond];
I want to make both gesture recognizers instances to work simultaneously.
I try to add to my ViewController method
shouldRecognizeSimultaneouslyWithGestureRecognizer
but this method never calls, I checked it using NSLog function.
Please help me solve this problem (allow work both gesture recognizers simultaneously).
Maybe you should allocate the Gestures
MyGestureRecognizer * gesture = [[MyGestureRecognizer alloc] initWithTarget:self action:nil];
gesture.delegate = self;
[self.view addGestureRecognizer:gesture];
Or you could use the control wheel as the target and do all your angle calculations in there. Then you delegate back to the mainViewController.

Resources