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.
Related
I'm pretty new to objective c and iOS development, and I feel sometimes like I'm banging my head into a brick wall...
I'm adding an image programmatically to the screen to server as a menu button. I have a class handling that button. This is the .h file:
#import <Foundation/Foundation.h>
#interface MenuButton : NSObject <UIGestureRecognizerDelegate>
#property UIImageView * menuButton;
- (id) initWithButton: (UIImageView *) mb;
- (void) onTapMenuButton;
#end
And this is the .m file:
#import "MenuButton.h"
#implementation MenuButton
- (id) initWithButton: (UIImageView *) mb {
self = [super init];
if(self) {
self.menuButton = mb;
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapMenuButton)];
tap.numberOfTapsRequired = 1;
[self.menuButton addGestureRecognizer:tap];
tap.delegate = self;
}
return self;
}
- (void) onTapMenuButton {
NSLog(#"SUCCESS!");
}
#end
Whenever I tap the button, the program stops running. In the output window all I can see is some text: "(lldb)", and the editor shows me the main function, highlighting the only line I have there, which I haven't touched at all (the one created automatically when I start a single-view application).
What am I doing wrong?
I suspect that you're creating a MenuButton instance, which sets up this tap gesture recognizer, but you're not retaining the the MenuButton instance and it becomes deallocated. The result: the tap gesture recognize is calling onTapMenuButton on an object that no longer exists.
How are you keeping hold of the MenuButton objects?
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)
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.
As said in the title, my IBOutletCollection of UIButtons is empty after viewDidLoad.
I created a IBOutletCollection of UILabels the same way, and this one is working perfectly.
Any idea how this can be fixed, or where i made a mistake?
Here is the Code:
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_save;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_cancel;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_edit;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_changeData;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_save;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_cancel;
The buttons are placed in a xib and linked correctly to the corresponding outlets. Just like the labels.
The time i press the one of the Buttons is the first time, i want to access the Buttons in Code.
for (UIButton *btn in _btn_changeData) {
btn.hidden = NO;
btn.userInteractionEnabled = YES;
}
for (UIButton *btn in _btn_save) {
btn.hidden = YES;
btn.userInteractionEnabled = NO;
}
for (UIButton *btn in _btn_cancel) {
btn.hidden = YES;
btn.userInteractionEnabled = NO;
}
for (UILabel *lbl in _lbl_save) {
lbl.hidden = YES;
}
for (UILabel *lbl in _lbl_cancel) {
lbl.hidden = YES;
}
for (UILabel *lbl in _lbl_edit) {
lbl.hidden = NO;
}
That is also where i got the following Exception and realized, that my Button OUtletcollection is empty.
-[UIButton countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xa8a8850
I neither overwrite the outletcollection, nor do i change attributes of the buttons.
Its just that the labels are there in the collection and the buttons not. And i have no idea why.
Thx in advance for any help.
Mav
First idea that comes to my mind is that the properties are not correctly synthesized. Is _btn_changeData really the ivar behind btn_changeData property?
Second idea is something I saw while debugging someone else's code. When outlets are incorrectly connected, for example, if the controller references itself, two controller instances can be create. Obviously only of of them will have the outlets connected. Make sure only of instance is created.
For debugging, implementing the setter by yourself might be a good idea.
Edit:
After rereading, the problem is actually different they you say in your question. The error message -[UIButton countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xa8a8850
doesn't mean that _btn_changeData is an empty array. It means there is a UIButton instead of an array.
Having said this, you should check if you are not overwriting the data in _btn_changeData somewhere.
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.