Obj C delegate methods not assigned to UIButton (iOS)? - objective-c

I am trying to assign a delegate's method to a UIButton using addTarget:action:forControlEvents:. Everything compiles without warnings, the IBOutlet is connected to the button in Interface Bulder (XCode 4). If I moves the delegate's method to the controller, it works fine. (All code worked fine, but I refactored to use a delegate, it's my first try with delegates and protocols.)
(added) The protocol declaration, placed before #interface in the .h:
#protocol MMGLVDelegate<NSObject>
-(void)receiveQuitRequest:(id)sender;
#end
In the controller interface, these properties:
#property (nonatomic, assign) id<TheDelegateProtocol> delegate;
#property (nonatomic, retain) IBOutlet UIButton *quitBtn;
In the controller implementation:
-(void)setDelegate:(id<MMGLVDelegate>)delegate {
DLog(#"MMGLVSegmented setDelegate: Entered");
_delegate = delegate;
[self.quitBtn addTarget:self.delegate action:#selector(receiveQuitRequest:)
forControlEvents:UIControlEventTouchUpInside];
}
Any help appreciated. Changing target to any of self.delegate, _delegate, or delegate doesn't change app behavior.
What I'm hoping to do is not have to declare a class receiveQuitRequest: that then passes off to the delegate, I'd rather go straight to the delegate from the control.

I think you should write
[self.quitBtn addTarget:delegate action:#selector(receiveQuitRequest:)
forControlEvents:UIControlEventTouchUpInside];
I am not sure, but this may work in your case

I have a couple of minor suggestions:
Try disconnecting and re-connecting the delegate for the button in IB. I've noticed that sometimes this seems to reset it properly.
Clean and rebuild. Again, this helps to reset things that didn't get set properly.

If I've understood everything correctly, the UIButton does not include a receiveQuitRequest: selector, so there is nothing to be executed when the user touches the button.

Related

Using a protocol and a delegate

I am trying to get some code in a view working. I have declared a delegate and it does not get instantiated, any Idea what I am missing?
I have tried to summarise how I have done this below. I think that the issue is that somewhere, my dataSource delegate needs to be instantiated.
I have a View called graph view and a delegate that is in the viewcontroller GraphViewController.
I know that the method in GraphView is doing something as it calls the AxisDrawing helper class and draws in Axes.
Here is the relevant code
In GraphView.h I set up the protocol and the dataSourceDelegate
#class GraphView;
#protocol GraphViewDataSourceDelegate
- (float)functionOfX:(float)xValue inGraphView:(GraphView *)sender;
#end
#interface GraphView : UIView
#property(nonatomic, weak) IBOutlet id <GraphViewDataSourceDelegate> dataSourceDelegate;
#end
Which I synthesize in GraphView.m
#import "GraphView.h"
#import "AxesDrawer.h"
#implementation GraphView
#synthesize dataSourceDelegate = _dataSourceDelegate;
Also in Graph View I try to use the delegate as follows (pixel is a CGPoint). This routine does run and I can draw to GraphView from here provided I do not try to use my protocol method. i.e. Some of my DrawRect stuff does get drawn which checks out the linking of the UIView to my custom View
pixel.y = [self.dataSourceDelegate functionOfX:pixel.x inGraphView:self];
++++Breakpoint put in here+++++
In GraphViewController I state that I implement the protocol and implement it as follows. The compiler warns me and spots when the implementation is done, turning of the warning. (I am only returning 3.0 at the moment as a test).
#interface
GraphViewController () <GraphViewDataSourceDelegate>
#property (nonatomic, weak) IBOutlet GraphView *graphView;
#end
...
-(float) functionOfX:(float)xValue inGraphView:(GraphView *)sender{
NSLog(#"fofx accessed");
return 3.0;
}
If I look at the GraphView* object just after a breakpoint, it seems to have no instance. What am I missing out.
This is from the trace at the breakpoint
_dataSourceDelegate struct objc_object * 0x0
EDIT: (In addition to #Clays answer below)
It turned out that my connection to the custom view was broke. This meant that the fault lay with the View not talking to the Custom ViewController. The link is made by ctrl dragging from the View Controller button in the Interface builder to the View within the ViewController.
This caused the ViewController to be instantiated and everything then worked fine.
To put it another way.
The link in question was the Outlet. This has to be declared in the ViewController as a property and then linked in IB using ctrl Drag from the ViewController Name-Bar to the View in the Controller.
Provided that you have added the Outlet property correctly, when you do the ctrl drag, your view will appear an option.
The context button popup information on the ViewController button in IB does give a clue.
If you have neglected to put the outlet property into the view controller, the link does appear in the context button popup but it is greyed out, and when you do the link your View is not named.
Once you put the outlet in, the name appears in the menu but not greyed out.
Make sure you've hooked everything up correctly: setting your GraphView's dataSourceDelegate, and your GraphViewController's graphView.
From the trace it looks like you haven't done that; or you lose the reference somewhere along the way because at some point nothing's holding on to it.

Setting a property from another property's setter from segue Objective-C

For reference, I'm trying to learn Objective-C through the Stanford iTunes-U course. I wanted to update one property from the setter of another (they are inherently connected. Also, is that bad style?). The property I am trying to update is a UILabel, but it doesn't work like I thought.
This code is in one of my view controllers:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[segue.destinationViewController setProgram:self.brain.program];
}
Naturally, this code calls the setter for the Program property of the incoming viewController. Here's that setter code:
-(void)setProgram:(id)program {
_program = program;
self.title = [CalculatorBrain descriptionOfProgram:program];
[self.graphview setNeedsDisplay];
self.testLabel.text = #"Trying to update your text!";
}
and the header file:
#import <UIKit/UIKit.h>
#interface GraphViewController : UIViewController
#property (nonatomic, strong) id program;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
Now, when this code is run, as the segue happens, the new view DOES have its title changed (the self.title line works). However, the UILabel DOES NOT get updated. Furthermore, if I call the setter again say in viewDidLoad, it does change the UILabel. Is this because of self being updated? What's going on here?
In one of the lectures, the professor explains that outlets aren't set yet in prepareForSegue. This means testLabel is nil, and you're sending a message to nil (which is allowed and doesn't crash your app). To solve your problem, set the textLabel's text in viewDidLoad or viewWillAppear.
I believe this is "fixed" in iOS 6, but it won't be backwards compatible, so if you want to support iOS 5 still, you'll have to use viewDidLoad or viewWillAppear to update outlets (which I think is better to do anyway).

Can't access #Property from other class

I'm having trouble accessing some variables in my program.
I have one class called MainMenu.
In the .h file I have declared 2 properties as follows:
MainMenu.h:
#property (nonatomic, retain) IBOutlet NSView *mainView;
#property (nonatomic, retain) IBOutlet NSWindow *theMainWindow;
In another class file, I want to be able to access these 2 variables, current I am using the following code in the other .h class file which does not work, I don't understand what I'm doing wrong:
AppDelegate.m:
MainMenu *theMainMenu = [[MainMenu alloc] init];
[theMainMenu switchViews:theMainMenu.theMainWindow:theMainMenu.mainView];
Here I create an object of the MainMenu class, and invoke a method called 'switchViews' in its definition, I then want to pass it the 2 variables which I'm having trouble accessing.
Any help greatly appreciated.
Thanks in advance everyone.
EDIT: 'switchViews' method shown below:
- (void)switchViews:(NSWindow*)mainWindow:(NSView*)newView {
NSView *dummyView;
[mainWindow setContentView:dummyView];
[mainWindow setContentSize:newView.frame.size];
[mainWindow setContentView:newView];
}
I think the problem is one of 2 different instances of your MainMenu class. If you hooked up your IBOutlets, you must have a blue cube in IB set to your MainMenu class, correct? However, when you alloc init one in your app delegate, that creates another instance of MainMenu that doesn't have those properties connected to anything. Instead, you should also have a blue cube in IB set to your app delegate, and have an IBOutlet in that class that you connect to the MainMenu blue cube in IB.
This might be silly, but did you remember to link your NSWindow and NSView in Interface Builder? Or are you at least manually instantiating them?
My best guess is (assuming there is a MainMenu.xib somewhere), that when you call init, it object is inited but not necessarily loaded. You should try to call your switchViews method in your controllers viewDidLoad or viewWillAppear method to be sure it is all loaded.

Modifying properties of a view controller form another view controller

In my project, there are two view controllers - let's say firstViewController and secondViewController. The second view controller has a button, and I want to make sure when the button gets pressed, the second view controller is telling somehow the first view controller - "hey, I got pressed, do something!", and it will do something, like changing a label. How is this possible to perform? Thanks in advance. Some code :
#interface firstViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
#implementation firstViewController
#synthesize textLabel;
#end
#interface secondViewController : UIViewController
-(IBAction)buttonPressed;
#end
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// Hey, I got pressed! Set the text on textLabel to "OK"!
}
#end
This is a very simple case of delegation and protocol mechanism of objective-c..
have a look at this tutorial which will explain you how its done.. you can do this via notification also but that is not usually advised...(because notification is usually used when the receiver is unknown , like in the case of UIDeviceBatteryLevelDidChangeNotification you don't exactly know which view controller wants to know about this.)
I'd first consider what the button press means. Does it change the state of the model?
Say your model is an int, and the button increments it. The view controllers wouldn't message each other about that, they would just both observe the state of the model. (The one with the button could change the state, too).
Thinking about it this way, the solution probably isn't delegation. It's probably notification or KVO.
See the answer to this question: Passing data between two view controllers via a protocol
However, ask yourself if you really need a protocol here. If it is just between this classes or just about the question of accessing data of a class or sending information to a class then that is what the interface of a class is made for.
#interface firstViewController : UIViewController{
UILabel *textLabel; // I personally alway add IBOutlet here too, but I think that is not required.
}
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
And in SecondViewController.m:
#import "FirstViewController.h"
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// You will have to have a properly set instance variable firstViewController
[firstViewController.textLabel setText:#"OK"];
}
#end
So your second view controller needs to 'know' the first one. One way of achieving that is defining
FirstViewController *firstViewController;
as property and set it from wherever the second view controller is created and the first one is already known. How to do that exactly depends very much on the architecture of your app.

When to alloc/init an ivar, and when not to

All,
In Apple's sample code "DateCell"
http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html
the ivar "pickerView" is declared in MyTableViewController.h like this:
#interface MyTableViewController : UITableViewController
{
#private
UIDatePicker *pickerView;
UIBarButtonItem *doneButton; // this button appears only when the date picker is open
NSArray *dataArray;
NSDateFormatter *dateFormatter;
}
#property (nonatomic, retain) IBOutlet UIDatePicker *pickerView;
...
It is synthesized in the class file MyTableViewController.m like this:
#implementation MyTableViewController
#synthesize pickerView, doneButton, dataArray, dateFormatter;
...
When this app runs, I can insert NSLog(#"%#",pickerView) into ViewDidLoad and see that, sure enough, the ivar pickerView is real and has a value. Nowhere, though, does this class alloc/init pickerView. And that's the root of the question: how's it getting done if it's not being done explicitly?
Well, I naively copied this stuff to my code into my RootViewController.h and .m files figuring I could do the same, but pickerView stubbornly remains uninitialized (and my NSLog calls return "(nil)" as its value) no matter what I try short of explicitly alloc/initing it. Certainly RootViewController is being instantiated, or the RootView wouldn't be showing up, right? So shouldn't my pickerView be coming along for the ride just as it does for Apple?
So... do I have to manually alloc/init the pickerView instance variable? If so, where's Apple doing it? Or how are they doing it somehow otherwise?
I think I'm missing something very basic here, but I have no idea what it is. I can't see anything in Interface Builder or XCode that looks different between mine and theirs, but I've got tunnel vision at this point and can't see anything clearly anymore.
Thanks,
Bill
The IBOutlet modifier on this line is the key...
#property (nonatomic, retain) IBOutlet UIDatePicker *pickerView;
IBOutlet is a decorator that indicates that the object will be hooked up/connected/initialised when the corresponding xib (Interface Builder) file is loaded. The sample application you're looking up will contain a UITableViewController is a xib which has a connection to a UIPickerView.
You can either go the route of creating your own custom xib file and wire to an instance of UIPickerView or you can manually initialise the picker yourself.
Interface Builder (nib or xib) treats automatically IBOutlet ivar with connection of components.
IBOutlet is a special keyword that is
used only to tell Interface Builder to
treat an instance variable or property
as an outlet. It’s actually defined as
nothing so it has no effect at compile
time.
Your First iOS Application - The
View Controller Interface
Declaration, Making Connections
Interface Builder User Guide -
Defining Outlets and Actions in
Xcode