I am using a Category to add functionality to my ViewControllers. When the function from the category is run I get an error unrecognized selector sent to instance 0x7970ebf0. To test out the function I'm calling, I originally had the code within my viewDidLoad where I am calling the added function and it worked fine, so I don't think it is a problem with the function itself. So here is my code for the category and where I call it. Am implementing the Category incorrectly?
Here is "UIViewController+StatusBar.h"
#import <Foundation/Foundation.h>
#interface UIViewController (StatusBar)
-(void) addStatusBarBackground;
#end
Here is "UIViewController+StatusBar.m"
#import "UIViewController+StatusBar.h"
#implementation UIViewController (StatusBar)
-(void) addStatusBarBackground(){
//for making the background of the UIStatus bar black
UIView *statusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, -20, [[self view] bounds].size.width, 22)];
statusBarView.backgroundColor = [UIColor blackColor];
[self.navigationController.navigationBar addSubview:statusBarView];
}
#end
And then I call the function in viewDidLoad of my controller after including UIViewController+StatusBar.h like so
[self addStatusBarBackground]; This is where the error happens, when this is called.
Thanks for the help in advance!
I figured out what I was doing wrong. It ended out to be nothing to do with the category. The category was implemented correctly except for the declaration -(void) addStatusBarBackground(). The parenthesis needed to be deleted. The problem was that I did not select the target memberships that my app has on the right panel in my UIViewController+StatusBar.m file. So the file was not being compiled for my project. I guess its kind of like it wasn't included. I haven't dealt with target memberships before so that was why I was unaware of the problem. Thanks for the comments helping me figure out the answer!
Related
I am completely stumped and have been researching for days. Probably something really simple that I am missing.
I have a ViewController which contains a custom UIView called GameView, and a UIView called buttonBox which contains a "next level" button. What I am trying to achieve is when the level is completed in GameView, it fires a function in my ViewController which shows the buttonBox so the user can click the "next level" button. It simply will not work.
I have attempted this in 3 ways, neither have worked:
Creating an IBOutlet in the ViewController, connecting it to the hidden UIView (and it was definitely connected) and calling setHidden:NO.
Calling the [self.view viewWithTag:xxx] and then calling setHidden:NO.
Using hidden=NO instead of setHidden:NO.
Relevant code for ViewController as follows:
#interface PlayViewController : UIViewController
#property GameView *gv;
#property (strong, nonatomic) IBOutlet UIView *buttonBox;
-(void) showButtonBox;
#end
#implementation PlayViewController
#synthesize buttonBox;
...
- (IBAction)showButtonBox {
UIView *uiv = (UIView*) [self.view viewWithTag:999];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Showing box function");
NSLog(#"%#", uiv);
uiv.hidden = NO;
});
}
#end
And my custom view:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
The thing is, the variable uiv is returning null in NSLog, which is obviously why hidden is not working, but I have no idea why. It also didn't work when I was using IBOutlet.
Also, current output from NSLog is as follows:
2015-11-24 00:18:38.612 ib[12579:1264539] Showing box function
2015-11-24 00:18:38.612 ib[12579:1264539] (null)
Thanks in advance.
Correct Answer:
The problem was that I was using StoryBuilder to build my UI, but by using the alloc init method was creating a new view controller (which is never shown) instead of correctly referencing the view controller which was being displayed. This is achieved by passing the view controller being displayed to the view in the viewDidLoad function, see below:
#implementation PlayViewController
#synthesize buttonBox;
#synthesize gv;
- (void)viewDidLoad
{
[super viewDidLoad];
gv = [self.view viewWithTag:777];
[gv setPlayViewController:self];
}
...
Man, it's simple. Let's take a look at:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
Here we have the issue:
dispatch_async(bgQueue, ^{
I assume, bgQueue stands for "background queue", which means this is not served by the main thread (the UI thread).
Having that said, it's quite naive to expect
[pvc showButtonBox];
to work properly. Just move this code into the main thread. For instance, you can just wrap the aforementioned line of code into a dispatch_async on the main queue. That should solve your probem, if your outlets and/or tags are OK. Cheers.
[[PlayViewController alloc]init];
This creates a new instance of PlayViewController. Where have you defined your outlets and views?
In a storyboard? You can't use this initialiser - nothing from the storyboard will be picked up, you have to use a segue or initializeViewControllerWithIdentifier:.
In a xib file? Is it called PlayViewController.xib? If not, it won't be picked up by the initialiser. Plain alloc/init of a view controller will only find a nib file as described in the documentation of the nibName property.
Do you really want alloc / init at all? Do you actually want to make a new view controller, or is one already on the screen?
From your comments it seems option 3 is the right answer. The PlayViewController is already on the screen, alloc/init is creating a new instance of it, which is never being put on screen, which never loads any views regardless of storyboards or nibs.
You need to get a reference to the existing instance of PlayViewController. Without knowing the structure of your app it's not too easy to say how that's done - is it presenting the game view? Is the game view a subview of the view controller's view? You may need to pass in a reference (weak) to the game view when it is created, at viewDidLoad, or set up an outlet in the storyboard.
So this one has me stumped - probably something simple, but I'm clueless.
I'm defining a custom class, containing one method that receives one message (an integer). When calling that method, the compiler refuses to recognize the message I'm trying to send along with the call. ("No known class method for selector 'sendMessage:'. Removing the message from both the call and the definition - i.e. removing the :(int)mode from the definition, and the :1 from the call - allows it to compile fine (but then of course I lose the functionality).
I've tried defining it as an instance method, and as a class method - neither one works.
Many thanks in advance for your collective wisdom!
custom class "Communications.h":
#interface Communications : NSString
+(NSString*)sendMessage:(int)mode;
#end
Communications.m:
#import "Communications.h"
#interface Communications ()
#end
#implementation Communications
+(NSString*)sendMessage:(int)mode {
// Do something important
}
ViewController.h:
#import "Communications.h"
- (void) tapPanic:(UITapGestureRecognizer*)sender;
ViewController.m:
- (void) tapPanic:(UITapGestureRecognizer *)sender {
[Animations animatePanic:self.view type:0];
panicactive = 1;
NSString* tmpResponse = [Communications sendMessage:1];
UILabel* tmpServerResponsePanic = [self.view viewWithTag:10002];
tmpServerResponsePanic.text = tmpResponse;
[[self serverResponsePanic] setNeedsDisplay];
}
So, chalk it up to weirdness with Xcode... copy / pasting the contents of Communications .h and .m into new files, with a new class definition (Comms), did the trick. I think the compiler got confused and was remembering an old definition of the method.
Being new to objective-C coding I started out writing a basic app, fully programmatically (not using storyboards or xib) in one file, my AppViewController h and m files.
Everything worked lovely.
So then I wanted to break up the mass of code by subclassing sections, and everything went well apart from the UIPickerView. In fact simply commenting out the [background addSubview:colorPicker]; seemed to totally fix the issue. I never found the answer online so I proceeded to make a new document to replicate said issue.
So here goes:
UIPickerViewController.h
#import <UIKit/UIKit.h>
#import "Picker.h"
#interface UIPickerViewController : UIViewController
#end
Simply imports my new class.
UIPickerViewController.m
#import "UIPickerViewController.h"
#interface UIPickerViewController ()
#end
#implementation UIPickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *superview = self.view;
int height = superview.bounds.size.height;
int width = superview.bounds.size.width;
CGRect popupRect = CGRectMake(0, 0, width, height);
UIView *popup = [[UIView alloc]initWithFrame:popupRect];
popup.tag = 8;
[superview addSubview:popup];
Picker *picker = [[Picker alloc]initWithFrame:popupRect];
[picker viewAddTypeScreenToView:superview];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Sets up a new view with a tag (so that i could reference it later with my new class)
Then actions a method from my new class to populate my new view.
Picker.h
#import <UIKit/UIKit.h>
#interface Picker : UIView
<UIPickerViewDataSource,UIPickerViewDelegate>
{
UIPickerView *colorPicker;
NSMutableArray *colorsArray;
}
#property (nonatomic, retain) UIPickerView *colorPicker;
#property (nonatomic, retain) NSMutableArray *colorsArray;
#property (strong,nonatomic) UILabel *myValue;
-(void)viewAddTypeScreenToView:(UIView*)superview;
#end
Setting up my variables and accessible method.
Picker.m
#import "Picker.h"
#implementation Picker
#synthesize colorsArray;
#synthesize colorPicker;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)viewAddTypeScreenToView:(UIView*)superview
{
UIView *baseView =[superview viewWithTag:8];
int height = baseView.bounds.size.height;
int width = baseView.bounds.size.width;
CGRect fullScreen = CGRectMake(0, 0, width, height);
UIView *background = [[UIView alloc]initWithFrame:fullScreen];
background.backgroundColor = [UIColor blackColor];
colorsArray = [[NSMutableArray alloc] initWithObjects:#"Red",#"Blue",#"Yellow",#"Green",nil];
CGRect myPickerRect = CGRectMake(10, 70, (width/2)-40, 200);
colorPicker = [[UIPickerView alloc]initWithFrame:myPickerRect];
colorPicker.dataSource = self;
colorPicker.delegate = self;
colorPicker.showsSelectionIndicator = YES;
[colorPicker selectRow:2 inComponent:0 animated:YES];
CGRect labelFrame = CGRectMake(10, 10, 180, 50);
_myValue = [[UILabel alloc]initWithFrame:labelFrame];
_myValue.textColor = [UIColor redColor];
_myValue.text = #"select colour";
[background addSubview:_myValue];
[background addSubview:colorPicker];
[baseView addSubview:background];
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return colorsArray.count;;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return colorsArray[row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
_myValue.text = [NSString stringWithString:colorsArray[row]];
}
#end
And finally the initiation called by the method in the picker class file.
This gives me an error along these lines
-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000
2014-03-19 10:29:48.407 Briefcase[1800:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000'
Which i've read is to do with either the datasource, or ARC systems, however none of the responses that I have found relate to or work with the type of set up that I have above. I'm sure it's something really simple but after a few days of failed searching, it's officially driving me crazy.
The problem is most likely that the instance of Picker that is being created in UIPickerViewController is never added to the view hierarchy and thus gets released prematurely (provided we're talking about a project using ARC here).
This leads to the pickerview's delegate and datasource becoming invalid and, basically, pointing at any random object. That's what is causing your crash: A message to your delegate cannot be delivered because the delegate is dead already. The picker still keeps a pointer which used to point at the delegate, but which has become invalid and points at a random object now, in this case a table view cell, which basically doesn't know what to do with this message and crashes.
The problem should go away if you add Picker *picker as an ivar or a retaining / strong property to UIPickerViewController.h - this will retain the picker beyond the scope of the viewDidLoad method and should keep it alive.
But that would be just a workaround, the real problem is your overall design. You said you're new to objective-c and indeed, it looks like you lack a basic understanding of iOS view and view controller hierarchies and, to some degree, the concept of object oriented programming. You might want to dig into something more basic before trying to fix your code because, quite frankly, it should be rather re-written than fixed.
I'd be happy to provide you with suggestions about how to structure your code, but please provide some information about what functionality you'd like to achieve first.
Edit (in response to your comment):
As a rule of thumb, do not spread functionality over several classes unless necessary. For objects, which serve a rather infrastructural purpose, like a specialized textfield or a pickerview, always ask yourself: "If I would like to reuse that object in another project, would that be as easy as using any other existing object, like, for example, UILabel?" If the answer is "No", then something is wrong. Ideally, interface objects are self-contained and to use them, you just invoke them, add them to a view and tell them, which text to display or which options to offer. If that information is subject to change or if the object needs to interact with other parts of your code, make use of delegation and protocols. Under no circumstances should the functionality of your object be tied to hard coded values or rely to some view to have a certain tag.
If you subclass UIView, the resulting object should behave like any other instance of UIView. It should be added to the view hierarchy by you or some object, but it shouldn't add or remove itself. If it works without being added to the view hierarchy at all, something is wrong. A view serves the purpose of being a part in your interface and all the logic it contains should work to that end, not more, not less.
Normally, interface objects should not interfere with one another. If something happens to one object (button pressed, option selected, text changed...) and another object is supposed to reflect that change, it is the view controllers responsibility to make that happen. The view controller is the place where the logic happens. If there is a task which requires a lot of complex logic, it might be a good idea to encapsule that logic into a purpose build class. One such example would be a class which manages network connections. This class should be again self contained: If the view controller needs some remote information, it asks your network class. Once your network class has that information (or failed to retrieve it), it reports back to your view controller. The view controller then updates the interface - under no circumstance should the networking class contain code which affects the interface.
It is important to understand that you could very well ignore these rules and still end up with a working app. And in some cases, the "direct" way may appear to be easier to implement and thus may look very tempting. But you'll pay the price later - once you start debugging your code. If your picker does not behave the way it should, you need to look into several places and wrap your mind around several objects, just to make one interface object behave right. And likely you will break one functionality while fixing the other.
So, try to make it right from the start, even though it requires more planning and learning. Trust me, it pays out, I started out just like you several years ago ;)
I know that this question was asked before many times but I didn't find the solution for my problem. I am not new at iOs and this should be pretty trivial stuff, but it is driving me mad and crazy :). Ok I have class C that is subclass of UIViewController. In it'S view there is a UIScrollView and in that scroll view I put class B and A also subclasses of UIViewController and I do it like this:
a = [[A alloc] initWithNibName:#"A" bundle:nil];
[a setDelegate:self];
[self addChildViewController:a];
[a.view setFrame:CGRectMake(2*vwMainScroller.frame.size.width, 0, a.view.frame.size.width, a.view.frame.size.height)];
[vwMainScroller addSubview:a.view];
b = [[B alloc] initWithNibName:#"B" bundle:nil];
[b setDelegate:self];
[self addChildViewController:b];
[b.view setFrame:CGRectMake(vwMainScroller.frame.size.width, 0, b.view.frame.size.width, speedScreen.view.frame.size.height)];
[vwMainScroller addSubview:b.view];
Both class A and class B have buttons on their views added programatically in viewDidLoad method like this:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *btnChangeColor = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 460)];
[btnChangeColor addTarget:self action:#selector(Btn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnChangeColor];
}
-(void)Btn
{
NSLog(#"I am the selector and I have been called");
}
But when I press class A button the selector is never called, but when I press class B button the selector is called just like it should be.
I tried:
putting buttons on xibs
enabling user interaction for every view in my code - it was already enabled -> didn't help
painting buttons to see if they are where they should be -> they were
restarting computer and xCode
running on device and simulator
Nothing helped, always the same thing, button works on class B but not on class A, after I tried to add some other buttons in IB of class A, after I found out that they also don't call their selectors I checked the box "Shows touches on highlight" and guess what, they didn't glow when I touched them. So please I see that something is eating my touch events but just don't have any ideas how to proceed in debugging this matter. Please help me :)...
Try set up vwMainScroller.delaysContentTouches = Yes on your scroll view.
Add
[a.view setUserInteractionEnabled:YES];
and
[b.view setUserInteractionEnabled:YES];
before you add it as a subview to your scrollView, If it doesn't help set up your view controller as a delegate of your UIScrollView and add this:
-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return ![view isKindOfClass:[UIButton class]];
}
Ok, stop thinking, cause that is what U need to do to solve this problem. I stopped thinking and created a new class "Anew", copied all code from class A, created exactly the same xib, by copying elements from A.xib, deleted class A and implemented Anew in exactly the same way I did with class A. And now everything is working as it should. So the answer is when U spend 4 hours on trying to solve something as trivial as this, and u tried every possible approach that U can imagine and that others suggested, and nothing helps, it is time to stop thinking and start deleting cause the problem is probably deeper than U can reach...
My app is crashing when trying to access a method in my parentViewController. Here is the layout in StoryBoard
MainViewController = STLMMainViewController (ParentViewController)
Scene1 = STLMTimeDateViewController (ChildViewController)
Here is the code for STLMTimeDateViewController
#import "STLMTimeDateViewController.h"
#import "STLMMainViewController.h"
#interface STLMTimeDateViewController ()
#property (nonatomic, strong) STLMMainViewController *stlmMainViewController;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"The name of the controller %#",self.navigationController.parentViewController);
stlmMainViewController= (STLMMainViewController *) self.parentViewController;
[stlmMainViewController locationButtonSelected]; // This is where the App crashes
NSLog(#"TimeDateController");
}
The App Runs, but when STLMMainViewController is called, the app crashes with the following error:
2013-02-10 16:33:57.422 MyApp[9120:c07] The name of the controller <STLMMainViewController: 0x83850d0>
2013-02-10 16:33:57.434 MyApp[9120:c07] -[UINavigationController locationButtonSelected]: unrecognized selector sent to instance 0x8371a70
If I remove the following line:
stlmMainViewController = (STLMMainViewController *) self.parentViewController;
and just leave
[stlmMainViewController locationButtonSelected];
The App runs, no error, but the following method in [STLMMainViewController locationButtonSelected] does not get called (I never see the log):
-(void)locationButtonSelected
{
[LocationButton setSelected:YES];
[eatDrinkbutton setSelected:NO];
[timeCalButton setSelected:NO];
[carButton setSelected:NO];
[contactButton setSelected:NO];
NSLog(#"LocationButtonSelected Method");
}
All the properties in the locationButtonSelected method and the method itself is declared in .h of STLMMainViewController for public access.
Thanks
You might try this:
self.stlmMainViewController= (STLMMainViewController *)self.navigationController.parentViewController;
(EDIT: actually, as someone else just pointed out, you might want to use presentingViewController instead.)
It looks like you had it right in the log message right before this. You want your navigation controller's parent in this case.
BTW, the reason you don't crash when you delete this line, is because you end up sending the locationButtonSelected to nil. That won't crash, but it also won't do anything either.