kal delegate issue - ios

I think setting the delegate for the KalViewController correctly - copying both the Holiday & NativeCal demo - but I must not be because I'm getting the following error when I click upon a date cell:
-[UINavigationButton didSelectDate:]: unrecognized selector sent to instance
When I set breakpoints on All Exceptions I see that it's raising an exception in KalGridView.m in this function:
- (void)setSelectedTile:(KalTileView *)tile
{
if (selectedTile != tile) {
selectedTile.selected = NO;
selectedTile = [tile retain];
tile.selected = YES;
[delegate didSelectDate:tile.date]; // exception raised here
}
}
And here's the code where I set the delegate (pretty much copied from the demo code):
- (void) viewDidLoad
{
KalViewController *calendar = [[KalViewController alloc] init];
calendar.delegate = self;
[[self.view viewWithTag:1] addSubview:calendar.view];
....}
The error makes sense because I'm setting the delegate to my own ViewController which doesn't implement didSelectDate. On the other hand, neither does the Holiday and NativeCal classes which are set at the KalViewController's delegate, both of which are of type
NSObject <UIApplicationDelegate, UITableViewDelegate>
Thanks a pile.. I'm more than a touch stuck.

This is one of those rare instances where the delegate should probably be retained because it's being released and replaced. Override -setDelegate: and retain your delegate.

Related

iOS singleton viewDidLoad empty and on viewDidAppear not

I created a singleton in ios7 like this:
SharedData.h
#interface SharedData : NSObject
{
}
+ (id)sharedInstance;
#property (strong, nonatomic) NSMutableArray *list;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize list;
// Get the shared instance thread safe
+ (SharedData *)sharedInstance {
static dispatch_once_t once = 0;
static SharedData *sharedInstance = nil;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
//initialize
list = [[NSMutableArray alloc] init];
}
return self;
}
#end
I always use this code to access this class:
SharedData *sharedData = [SharedData sharedInstance];
The problem is now when I switch the view in my viewDidLoad method the list is empty but in my viewDidAppear method everything is fine. Any ideas?
EDIT:
This is the code how I change the views:
SharedData *sharedData = [SharedData sharedInstance];
//clear feed and add new feed
[sharedData.list removeAllObjects];
[sharedData.list addObjectsFromArray:newList];
//show new gui
[self.navigationController performSegueWithIdentifier:#"goToMain" sender:self];
NOTE: I push from a normal ViewController to a TabBarController -> NavigationController -> TableViewController to display the list.
I guess you have the confusion between these two viewcontroller methods:
-(void)viewDidLoad{
//
}
&
-(void) viewDidAppear{
//
}
viewDidAppear is the method which is called each time your view changes but viewDidLoad is the method which is not necessarily called each time your view changes.
ViewDidLoad method is called when view loads for the first time, after that it doesn't get called until the views are removed/released.
P.S: I suggest you to put the breakpoint in your viewDidLoad and viewDidAppear method and feel it. Your answer lies there.
Hope this helps you alot.
Good Luck.
The problem was i created a segue which went from the button to the next view. Because of this the viewDidLoad gets earlier called than the list assigned. I just changed the segue to go from view to view.
How are you changing from one viewController to the other? Wich classes are the parents of your destination ViewController?,
If you are modifying properties of the view in the prepareForSegue method... you are forcing the view to load.
For example, you are setting the list of your singleton in prepareForSegue, but before setting the list you are modifying a property of your destination viewController. (doing something like destVC.view = XXX or destVC.viewControllers = XX if you are subclassing a UITabBarViewController...) Then you are triggering the viewDidLoad method , and it's executing before you have set the list to the correct value.
Or maybe you are seguing in two different places to the destinationViewController. And when the viewDidLoad happens, you still have not updated the list on the singleton.
Here is the transcription of the chat with the poster of the question: https://chat.stackoverflow.com/transcript/55218

Pass BOOL value

I am working on an app and I got stuck at the point where I can't seem to retrieve the value of a BOOL set in a class.
I spent too much time already on it, been through all the questions I found that seem to cover the matter.
The bad thing here is that I get something, but not what I need (I get a 0, which means, I guess, that the value wasn't retrieved correctly as it should be 1).
The things I tried are :
pass a pointer to my first class and access to my BOOL like this:
//in some method
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
by declaring it (talking of the pointer) as a property in my second class (and importing the h. file from my first class, where my BOOL is declared as property too):
#property FirstClass *pointerFirstClass;
But I got 0 using this.
The other shot I gave was add my BOOL in the first class and create an instance of the class in my second class
//in some method
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
But I got 0 too.
As Booleans are primitive types, like in C, I get a bit confused since I am new to object-oriented programming, I don't know how I could like create a getter for this, for example.
I also tried to do a - (BOOL)getBOOLValue method in my first class, and call this method in my second class and assign it to a BOOL in that second class.
But the result wasn't better.
Am I missing something?
Is there a way to get my value that I didn't think of or didn't know about yet?
I am running low on thoughts on how to get around this, it shouldn't be that hard IMO so I hope it is something simple that I just left aside.
EDIT :
Some actual code. I am working between 2 files called AppDelegate (yes, the actual one) and WelcomeViewController (so a VC).
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
BOOL inRegion; //thought of this making my BOOL as a property of AppDelegate
}
#property (strong, nonatomic) UIWindow *window;
#property BOOL inRegion; //Declaring my BOOL here to make it accessible for another class
- (BOOL)getBOOLValue; //An attempt to pass my BOOL value
AppDelegate.m
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside)
{
self.inRegion = YES; //Set my BOOL to TRUE
}
else if (state == CLRegionStateOutside)
{
self.inRegion = NO; //Else set it to False
}
- (BOOL)getBOOLValue
{
return inRegion; //Tried to create a custome "getter"
}
WelcomeViewControler.m (I changed nothing in the .h file)
I said I tried many things, right now, this is the last version of my code.
//Simply trying to do a Segue on a condition...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
As said, I want to retrieve the BOOL value of the AppDelegate.
Thank you.
This code doesn't make sense:
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
The first line doesn't do anything. You're not assigning anything to the property, and you're not doing anything with the value. Furthermore, the second line doesn't relate to the first line in any way that we can see from the code you've provided. Try this instead:
self.pointerFirstClass = [[FirstClass alloc] init];
self.pointerFirstClass.myBOOL = YES;
NSLog(#"myBOOL = %d", self.pointerFirstClass.myBOOL);
In other words, you need to be sure that self.pointerFirstClass points to a valid object. And then you need to make sure that you've assigned the value you want to the myBOOL property of that object.
Update: This looks like a case where you're talking to the wrong object. Look at this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
This is surely not what you really want. The application object is a single object -- a real singleton, in fact, meaning that there is and can be only one application object. That object has a delegate object, and that's a specific instance of your AppDelegate class. In this code, though, you're creating a new instance of AppDelegate, one that's different from the one that the application is using. Any changes that are made to the actual application delegate in response to messages from the application will not be reflected in the new object that you've created.
What I think you want is to get the actual application delegate object, and you can do that using:
[[UIApplication sharedApplication] delegate];
So, change your code to look like this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
// note: you don't need an else clause if it doesn't do anything
}
That way, you'll be talking to the same object that the app uses, which is the one that has the inRegion property set in response to the location manager call.
UPDATE - Now we can see your code the problem is obvious, you are trying to access the appDelegate by creating a new one...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
Instead you should be doing this....
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]
--
Not sure if you are posting your actual code? but the first example you give...
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
Shouldn't the second line be
NSLog(#"%d", self.pointerFirstClass.myBOOL);
Also this property...
#property FirstClass *pointerFirstClass;
Won't retain it once you've set it, it needs to be
#property (nonatomic,strong) FirstClass *pointerFirstClass;
In the second example...
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
You allocate and initialise a new FirstClass object and then check the property straight away, if you are not setting this to YES in the init then it will be false
Like I say, maybe you're not posting your actual code?
I guess what you want is initializing myBOOL to 1.
If so, you need do something as following
#implement FirstClass
- (id)init
{
self = [super init];
if(self) {
_myBOOL = 1;
}
return self;
}
// Other methods
#end
EDIT:
The comments is why you get 0.
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init]; // this is the problem.
// you create a new appdelegate,
// and never call locationManager:didDetermineState:forRegion:
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
rewrite your code as following:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}

tableView:numberOfRowsInSection:]: unrecognized selector sent to instance

I have a weird problem. I get this error:
-[FourSquareCheckInViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x7aecc0
2012-09-14 19:18:39.039 [5869:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FourSquareCheckInViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x7aecc0'
*** First throw call stack:
(0x3545e88f 0x37805259 0x35461a9b 0x35460a83 0x353bb650 0x32ee3693 0x32ee49ed 0x32ee493f 0x32ee451b 0x32ef17df 0x32ef16af 0x32e95f37 0x353bd1fb 0x3228daa5 0x3228d6bd 0x32291843 0x3229157f 0x322894b9 0x35432b1b 0x35430d57 0x354310b1 0x353b44a5 0x353b436d 0x37050439 0x32ec0cd5 0xb7ebb 0xb7e60)
terminate called throwing an exception(lldb)
Ok so I have made sure that the ViewController is set as the dataSource and the Delegate. I have added the UITableViewDelegate and UITableViewDataSource to the <> thingies.
I have set tableView.delegate = self and tableView.dataSource = self in my viewDidLoad method.
Here is my implementation of the viewDidLoad and viewDidAppear methods:
- (void)viewWillAppear:(BOOL)animated{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; // 1km
[locationManager startUpdatingLocation];
locationLat = [locationManager location].coordinate.latitude;
locationLng = [locationManager location].coordinate.longitude;
[locationManager stopUpdatingLocation];
// 1
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = locationLat;
zoomLocation.longitude= locationLng;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
// 3
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
// 4
[_mapView setRegion:adjustedRegion animated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_tableView.delegate = self;
_tableView.dataSource = self;
// Do any additional setup after loading the view.
// 1
MKCoordinateRegion mapRegion = [_mapView region];
CLLocationCoordinate2D centerLocation = mapRegion.center;
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; // 1km
[locationManager startUpdatingLocation];
locationLat = [locationManager location].coordinate.latitude;
locationLng = [locationManager location].coordinate.longitude;
[locationManager stopUpdatingLocation];
}
Here is my method body for numberOfRowsInSection:
- (NSInteger)numberOfRowsInSection:(NSInteger)section{
return 5;
}
Here is my header file for the view controller:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface FourSquareCheckInViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, CLLocationManagerDelegate, MKMapViewDelegate>{
}
#property (weak, nonatomic) IBOutlet MKMapView *_mapView;
- (NSInteger)numberOfRowsInSection:(NSInteger)section;
#property (weak, nonatomic) IBOutlet UITableView *_tableView;
#end
Here is a screenshot showing how my tableView is hooked up:
Read what the error message is telling you!
You have implemented numberOfRowsInSection:. So what? That's the wrong method. It is irrelevant. It will never be called.
The method you need to implement is called tableView:numberOfRowsInSection:. That's completely different.
If you click on the view it should show the above connections in INSPECTOR.
If you haven't got the ViewController hooked up as well you will get the error:
'NSInvalidArgumentException', reason: '-[UIView
tableView:numberOfRowsInSection:]: unrecognized selector sent to
instance
How I stopped the error is you
press on the ViewController to select it
look for Referencing Outlets in the Connection inspector to the right
drag from the circle of the New Referencing Outlet to the view
when you release the mouse, a tiny popup will show - select datasource
repeat step 3, and this time select delegate
When done, it should look like the image below!
This stopped the above error for me.
I hope this helps someone.
Check if you have UITableViewDelegate, UITableViewDataSource defined in your class file:
class ClassName: UIViewController, UITableViewDelegate, UITableViewDataSource
{
}
You'll also see this error if, although you've hooked everything up properly in the view controller, you have forgotten to assign your custom view controller class in your storyboard:
I think it's important that I show this answer. It seems silly, probably it is, but I lost a good while until I saw the error.
It happened to me creating a new view and copying part of the code.
I created the new view:
class THIRD: UIViewController {
[...]
}
And I started copying the code:
func numberOfSections(in tableView: UITableView) -> Int {
...
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
...
}
Etc.
And then, the message error:
[Fonts.THIRD tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x10204b200
I reviewed the Storyboard again and again. And I checked the code again and again. And I lost a good time.
Finally I realized!!!
I forgot to declare:
class THIRD: UIViewController, UITableViewDelegate, UITableViewDataSource {
Perhaps the error may be, do not declare the UITableViewDelegate and UITableViewDataSource protocols
Keep it in mind, do not forget it!
It is very simple, I had got a similar problem. I am implementing my TableView without Storyboard in my ViewController which has subclassed UIViewController with two protocols: <UITableViewDataSource, UITableViewDelegate>
But if you look to the UITableViewDataSource there are two methods witch are #required and these you need to implement in your code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
All other methods in UITableViewDelegate are #optional hence you don't need to implement them.
You'll also see this error if, although you've hooked everything up properly in the view controller, you have forgotten to extend UITableViewDelegate,UITableViewDataSource.
- (NSInteger)numberOfRowsInSection:(NSInteger)section;
You are not implementing UITableViewDataSource method. Remove the declaration and it won't crash anymore. Or alternatively provide a method body for it.
Another cause of this error I found (even when conforming to UITableViewDataSource!) was when setting a tableView.tableHeaderView before setting the tableView.dataSource.
// Header config
self.tableView.tableHeaderView = header; // CRASH!! Triggers table view update and thus calls unknown selector.
self.tableView.dataSource = self;
Really simple fix is to set the data source first before setting the tableHeaderView (or tableFooterView):
self.tableView.dataSource = self;
// Header config
self.tableView.tableHeaderView = header; // Runtime can now find selector even though it is already defined.
If you have set up everything correctly and you still get this error. You will need to assign a controller to your custom class

Custom UITextField delegate

I have a uitextfield subclass and in the init method and setDelegate I have this:
- (void) setDelegate:(id<UITextFieldDelegate>)paramDelegate{
[super setDelegate:paramDelegate];
MRAAdvancedTextFieldDelegate *limitedDelegate = [[MRAAdvancedTextFieldDelegate alloc] init];
self.delegate = limitedDelegate;
}
I am using ARC, but this results in a BAD_ACCESS. Any ideas?
You write self.delegate = limitedDelgate within your setDelegate: method. This is exactly the same as calling [self setDelegate:limiatedDelegate]. Since you are within the -setDelegate: method itself, you are causing infitine recursion. Hope this helps!
EDIT: per your comment about your intention, override it like this:
- (void) setDelegate:(id<UITextFieldDelegate>)paramDelegate{
MRAAdvancedTextFieldDelegate *limitedDelegate = [[MRAAdvancedTextFieldDelegate alloc] init];
[super setDelegate:limitedDelegate];
}
But I don't believe it's a good idea to do this - you should have your client code pass in instances of your delegate instead.
self.delegate = limitedDelegate;
is turned into
[self setDelegate:limitedDelegate];
by the compiler, resulting in an infinite loop. Solution: instead of using the property, use the instance variable instead in the custom setter method:
delegate = limitedDelegate;

Calling UIViewController and never is deallocated

I've the class OTNetwork that is subclass of UIViewController.
When user pushes a button I use this code to call it:
OTNetwork *net = [[OTNetwork alloc] initWithNibName:#"OTNetwork" bundle:nil];
[self presentModalViewController:net animated:YES];
[net release];
When user wants to exit, pushes a button and the OTNetwork object sends a notification that makes the caller ViewController dismiss the view controller. This is the code:
[self dismissModalViewControllerAnimated:YES];
My problem is that the OTNetwork object dealloc method is never called. And here is the invalidate call to a timer that never is stopped. An aditional problem is the memory leak.
In the caller View Controller this object only is created and dismissed by these lines of code.
Any help please?
Thanks in advance!.
Autorelease never guarantees when the dealloc will be called and you shouldn't rely on that.
And autorelease pools should be used for threads or when you have large memory allocations in a closed loop. It shouldn't be used on the main thread which already runs in a separate pool.
You should probably move the invalidate timer call to viewDidUnload or viewWillDisappear in OTNetwork class.
Hope that helps.
[Update: Mar 02, 2012]
If you'd like to ensure that dealloc is called, try the following
1) Store a reference to OTNetwork controller
OTNetwork *net = [[OTNetwork alloc] initWithNibName: #"OTNetwork" bundle: nil];
net.delegate = self;
self.modalV = net; // #property (nonatomic, strong) OTNetwork *modalV;
[net release];
[self presentModalViewController: modalV animated: YES];
2) Define a protocol / delegate in OTNetwork to report back when it's closed
// .h
#protocol OTNetworkDelegate;
- (void) netViewClosed;
#end
// .m
- (void) viewDidUnload
{
[self.delegate netViewClosed];
}
3) In mainViewController, implement the protocol
- (void) netViewClosed
{
if(modalV)
{
[modalV release], modalV = nil;
}
}
when you pass your OTNetwork object to the self which i'm assuming is a navigationController then your OTNetwork object is in the release pool and you don't need to worry about it being deallocated also cause your code is good on memory management.
So the short answer, its in the autorelease pool
you can try this for dealloc to be called , by using your own autorelease pool.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
try{
//your code for allocating that object
OTNetwork *net = [[[OTNetwork alloc] initWithNibName:#"OTNetwork" bundle:nil] autorelease]; [self presentModalViewController:net animated:YES];
}
finally{
[pool drain];
}

Resources